cocos2dx A* + tiledMap

时间:2023-03-09 09:47:13
cocos2dx A* + tiledMap

本文转自:http://blog.****.net/w18767104183/article/category/1757765

前面一章讲了cocos2dx 中使用A星算法

这章中讲 A*结合tiledmap

先看下效果图

cocos2dx A* + tiledMap

图有点丑,忍受下

绿色的块 表示人物的行走的路线(A*算法的结果)

红色部分 表示A*算法搜寻过的点(越少,速度越快)

黑色的部分(其实是无色块,因为背景是黑色的) 表示障碍物

这张图是用tiledmap做出来的, 看看里面的内容

cocos2dx A* + tiledMap

可以看到 我把不能通过的地区的图块给删了

tiledmap中有2个层 一个是background, 一个是road. 为了方便, 我把road也用同样的图片, 最好的方法是用一种同样的瓦片拼接出来一条能走的路, 让后把background图层加到road图层上就ok了.

下面直接上源码, 用的时cocos2.2.3, 拷贝到项目中就能用了.当然别忘了自己做个像样的tiledMap .

如果你觉得好用, 就在文章底下顶一个吧 , enjoy it !

  1. #ifndef __HELLOWORLD_SCENE_H__
  2. #define __HELLOWORLD_SCENE_H__
  3. #include "cocos2d.h"
  4. #include "vector"
  5. using namespace std;
  6. USING_NS_CC;
  7. #define MAP_WIDTH 200//要比tmx中的map大
  8. #define MAP_HEIGHT 200
  9. class PathSprite
  10. {
  11. public:
  12. PathSprite(CCSprite* sprite)
  13. {
  14. m_parent = NULL;
  15. m_child = NULL;
  16. m_costToSource = 0;
  17. m_FValue = 0;
  18. m_sprite = sprite;
  19. };
  20. public:
  21. CCSprite* m_sprite;//包含的瓦片精灵
  22. PathSprite* m_parent;//父节点
  23. PathSprite* m_child;//子节点
  24. float m_costToSource;//到起始点的距离
  25. int m_x;//地图坐标
  26. int m_y;
  27. float m_FValue;
  28. };
  29. class PathSearchInfo//寻路类(主要负责寻路的参数和逻辑)
  30. {
  31. public:
  32. static int m_startX;//开始点
  33. static int m_startY;
  34. static int m_endX;//结束点
  35. static int m_endY;
  36. static CCSize m_mapSize;//地图大小
  37. static CCSize m_tileSize;//地图的块大小
  38. static vector<PathSprite*> m_openList;//开放列表(里面存放相邻节点)
  39. static PathSprite* m_inspectArray[MAP_WIDTH][MAP_HEIGHT];//全部需要检测的点
  40. static vector<PathSprite*> m_pathList;//路径列表
  41. static vector<PathSprite*> m_haveInspectList;//检测过的列表
  42. static float calculateTwoObjDistance(PathSprite* obj1, PathSprite* obj2)//计算两个物体间的距离
  43. {
  44. //        float _offsetX = obj1->m_x - obj2->m_x;
  45. //        float _offsetY = obj1->m_y - obj2->m_y;
  46. //        return sqrt( _offsetX * _offsetX + _offsetY * _offsetY);
  47. float _x = abs(obj2->m_x - obj1->m_x);
  48. float _y = abs(obj2->m_y - obj1->m_y);
  49. return _x + _y;
  50. }
  51. static void inspectTheAdjacentNodes(PathSprite* node, PathSprite* adjacent, PathSprite* endNode)//把相邻的节点放入开放节点中
  52. {
  53. if (adjacent)
  54. {
  55. float _x = abs(endNode->m_x - adjacent->m_x);
  56. float _y = abs(endNode->m_y - adjacent->m_y);
  57. float F , G, H1, H2, H3;
  58. adjacent->m_costToSource = node->m_costToSource + calculateTwoObjDistance(node, adjacent);//获得累计的路程
  59. G = adjacent->m_costToSource;
  60. //三种算法, 感觉H2不错
  61. H1 = _x + _y;
  62. H2 = hypot(_x, _y);
  63. H3 = max(_x, _y);
  64. #if 1 //A*算法 = Dijkstra算法 + 最佳优先搜索
  65. F = G + H2;
  66. #endif
  67. #if 0//Dijkstra算法
  68. F = G;
  69. #endif
  70. #if 0//最佳优先搜索
  71. F = H2;
  72. #endif
  73. adjacent->m_FValue = F;
  74. adjacent->m_parent = node;//设置父节点
  75. adjacent->m_sprite->setColor(ccORANGE);//搜寻过的节点设为橘色(测试用)
  76. m_haveInspectList.push_back(adjacent);
  77. node->m_child = adjacent;//设置子节点
  78. PathSearchInfo::m_inspectArray[adjacent->m_x][adjacent->m_y] = NULL;//把检测过的点从检测列表中删除
  79. PathSearchInfo::m_openList.push_back(adjacent);//加入开放列表
  80. }
  81. }
  82. static PathSprite* getMinPathFormOpenList()//从开放节点中获取F值最小值的点
  83. {
  84. if (m_openList.size()>0) {
  85. PathSprite* _sp =* m_openList.begin();
  86. for (vector<PathSprite*>::iterator iter = m_openList.begin(); iter !=  m_openList.end(); iter++)
  87. {
  88. if ((*iter)->m_FValue < _sp->m_FValue)
  89. {
  90. _sp = *iter;
  91. }
  92. }
  93. return _sp;
  94. }
  95. else
  96. {
  97. return NULL;
  98. }
  99. }
  100. static PathSprite* getObjFromInspectArray(int x, int y)//根据横纵坐标从检测数组中获取点
  101. {
  102. if (x >=0 && y >=0 && x < m_mapSize.width && y < m_mapSize.height) {
  103. return m_inspectArray[x][y];
  104. }
  105. return  NULL;
  106. }
  107. static bool removeObjFromOpenList( PathSprite* sprite)//从开放列表中移除对象
  108. {
  109. if (!sprite) {
  110. return  false;
  111. }
  112. for (vector<PathSprite*>::iterator iter = m_openList.begin(); iter !=  m_openList.end(); iter++)
  113. {
  114. if (*iter == sprite)
  115. {
  116. m_openList.erase(iter);
  117. return true;
  118. }
  119. }
  120. return false;
  121. }
  122. };
  123. class HelloWorld : public cocos2d::CCLayer
  124. {
  125. public:
  126. // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
  127. virtual bool init();
  128. // there's no 'id' in cpp, so we recommend returning the class instance pointer
  129. static cocos2d::CCScene* scene();
  130. // a selector callback
  131. void menuCloseCallback(CCObject* pSender);
  132. // implement the "static node()" method manually
  133. CREATE_FUNC(HelloWorld);
  134. void onEnter();
  135. virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
  136. virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
  137. virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
  138. void calculatePath();//计算路径
  139. void drawPath();//绘制路径(测试用)
  140. void clearPath();//清理路径
  141. void playerMove();//人物走动
  142. void update(float dt);//跟新大地图(行走时, 人不动, 地图跟着人动);
  143. public:
  144. CCPoint m_orignPoint;//人物的起始点
  145. PathSprite* m_player;//人物点
  146. int m_playerMoveStep;//人物当前的行程的索引
  147. };
  148. #endif // __HELLOWORLD_SCENE_H__
  1. #include "HelloWorldScene.h"
  2. USING_NS_CC;
  3. vector<PathSprite*> PathSearchInfo::m_openList;
  4. PathSprite* PathSearchInfo::m_inspectArray[MAP_WIDTH][MAP_HEIGHT] = {NULL};
  5. vector<PathSprite*> PathSearchInfo::m_pathList;
  6. vector<PathSprite*> PathSearchInfo::m_haveInspectList;
  7. CCSize PathSearchInfo::m_mapSize;
  8. CCSize PathSearchInfo::m_tileSize;
  9. int PathSearchInfo::m_startX;
  10. int PathSearchInfo::m_startY;
  11. int PathSearchInfo::m_endX;
  12. int PathSearchInfo::m_endY;
  13. CCScene* HelloWorld::scene()
  14. {
  15. // 'scene' is an autorelease object
  16. CCScene *scene = CCScene::create();
  17. // 'layer' is an autorelease object
  18. HelloWorld *layer = HelloWorld::create();
  19. // add layer as a child to scene
  20. scene->addChild(layer);
  21. // return the scene
  22. return scene;
  23. }
  24. // on "init" you need to initialize your instance
  25. void HelloWorld::onEnter()
  26. {
  27. CCDirector* pDirector = CCDirector::sharedDirector();
  28. pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
  29. CCLayer::onEnter();
  30. }
  31. bool HelloWorld::init()
  32. {
  33. //////////////////////////////
  34. // 1. super init first
  35. if ( !CCLayer::init() )
  36. {
  37. return false;
  38. }
  39. CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
  40. CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
  41. /////////////////////////////
  42. // 2. add a menu item with "X" image, which is clicked to quit the program
  43. //    you may modify it.
  44. CCLabelTTF* pLabel = CCLabelTTF::create("A* + tiledMap", "Arial", 24);
  45. // position the label on the center of the screen
  46. pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
  47. origin.y + visibleSize.height - pLabel->getContentSize().height));
  48. // add the label as a child to this layer
  49. this->addChild(pLabel, 1);
  50. this->scheduleUpdate();
  51. CCTMXTiledMap* map = CCTMXTiledMap::create("gameMap.tmx");
  52. this->addChild(map);
  53. map->setPosition(CCPoint());
  54. CCTMXLayer* _road = map->layerNamed("road");//行走路径的地图
  55. CCSize _mapSize = map->getMapSize();
  56. for (int j = 0;  j < _mapSize.height; j++) {
  57. for (int i = 0;  i < _mapSize.width; i++) {
  58. CCSprite* _sp = _road->tileAt(CCPoint(i, j));
  59. if (_sp) {
  60. PathSprite* _pathSprite = new PathSprite(_sp);
  61. _pathSprite->m_x = i;
  62. _pathSprite->m_y = j;
  63. PathSearchInfo::m_inspectArray[i][j] = _pathSprite;//把地图中所有的点一一对应放入检测列表中
  64. }
  65. }
  66. }
  67. PathSearchInfo::m_mapSize = _mapSize;//获取地图的尺寸
  68. PathSearchInfo::m_tileSize = map->getTileSize();//获取瓦片的尺寸
  69. //设置起始和终点
  70. PathSearchInfo::m_startX =30;
  71. PathSearchInfo::m_startY = 75;
  72. //创建一个人物
  73. m_player = new PathSprite(CCSprite::create("10001.png"));
  74. m_player->m_sprite->setAnchorPoint(CCPoint(0.5,0));
  75. this->addChild(m_player->m_sprite);
  76. m_player->m_x = PathSearchInfo::m_startX;//设置人物的起始的地图坐标
  77. m_player->m_y = PathSearchInfo::m_startY;
  78. m_orignPoint = PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY]->m_sprite->getPosition();
  79. m_player->m_sprite->setPosition(m_orignPoint);//设置人物的起始的世界坐标
  80. return true;
  81. }
  82. void HelloWorld::calculatePath()
  83. {
  84. //得到开始点的节点
  85. PathSprite* _startNode = PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY];
  86. //得到结束点的节点
  87. PathSprite* _endNode = PathSearchInfo::m_inspectArray[PathSearchInfo::m_endX][PathSearchInfo::m_endY];
  88. //因为是开始点 把到起始点的距离设为0, F值也为0
  89. _startNode->m_costToSource = 0;
  90. _startNode->m_FValue = 0;
  91. //把已经检测过的点从检测列表中删除
  92. PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY] = NULL;
  93. //把该点放入已经检测过点的列表中
  94. PathSearchInfo::m_haveInspectList.push_back(_startNode);
  95. //然后加入开放列表
  96. PathSearchInfo::m_openList.push_back(_startNode);
  97. PathSprite* _node = NULL;
  98. while (true)
  99. {
  100. //得到离起始点最近的点(如果是第一次执行, 得到的是起点)
  101. _node = PathSearchInfo::getMinPathFormOpenList();
  102. if (!_node)
  103. {
  104. //找不到路径
  105. break;
  106. }
  107. //把计算过的点从开放列表中删除
  108. PathSearchInfo::removeObjFromOpenList( _node);
  109. int _x = _node->m_x;
  110. int _y = _node->m_y;
  111. //
  112. if (_x ==PathSearchInfo::m_endX && _y == PathSearchInfo::m_endY)
  113. {
  114. break;
  115. }
  116. //检测8个方向的相邻节点是否可以放入开放列表中
  117. CCLog("%d, %d",_x, _y);
  118. PathSprite* _adjacent = PathSearchInfo::getObjFromInspectArray( _x + 1, _y + 1);
  119. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  120. _adjacent = PathSearchInfo::getObjFromInspectArray(  _x +1, _y);
  121. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  122. _adjacent = PathSearchInfo::getObjFromInspectArray(  _x +1, _y-1);
  123. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  124. _adjacent = PathSearchInfo::getObjFromInspectArray(  _x , _y -1);
  125. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  126. _adjacent = PathSearchInfo::getObjFromInspectArray(  _x -1, _y - 1);
  127. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  128. _adjacent = PathSearchInfo::getObjFromInspectArray(  _x -1, _y);
  129. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  130. _adjacent = PathSearchInfo::getObjFromInspectArray(  _x -1, _y+1);
  131. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  132. _adjacent = PathSearchInfo::getObjFromInspectArray(  _x , _y+1);
  133. PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
  134. }
  135. while (_node)
  136. {
  137. //把路径点加入到路径列表中
  138. PathSearchInfo::m_pathList.insert(PathSearchInfo::m_pathList.begin(), _node);
  139. _node = _node->m_parent;
  140. }
  141. }
  142. void HelloWorld::drawPath(  )
  143. {
  144. for (vector<PathSprite*>::iterator iter = PathSearchInfo::m_pathList.begin(); iter !=  PathSearchInfo::m_pathList.end(); iter++)
  145. {
  146. (*iter)->m_sprite->setColor(ccGREEN);
  147. }
  148. }
  149. CCRect getBoundingBox(float x, float y, float width, float height)
  150. {
  151. return CCRect(x - width/2, y - height/2, width, height);
  152. }
  153. bool HelloWorld::ccTouchBegan(CCTouch* touch, CCEvent* event)
  154. {
  155. //清除之前的路径
  156. clearPath();
  157. auto nodePosition = convertToNodeSpace( touch->getLocation() );
  158. CCLog("%f, %f", nodePosition.x, nodePosition.y);
  159. //    for (int i = 0; i < PathSearchInfo::m_inspectList.size(); i++)
  160. //    {
  161. //        PathSprite* _sp = PathSearchInfo::m_inspectList[i];
  162. //
  163. //        CCRect _rect = getBoundingBox( _sp->m_sprite->getPositionX(), _sp->m_sprite->getPositionY(), _sp->m_sprite->getContentSize().width, _sp->m_sprite->getContentSize().height);
  164. //
  165. //        if (_rect.containsPoint(nodePosition))
  166. //        {
  167. PathSprite* _sp = PathSearchInfo::m_inspectArray[(int)(nodePosition.x/PathSearchInfo::m_tileSize.width)][(int)(PathSearchInfo::m_mapSize.height - nodePosition.y/PathSearchInfo::m_tileSize.height)];
  168. if (_sp) {
  169. CCLog("%f, %f", _sp->m_sprite->getPositionX(), _sp->m_sprite->getPositionY());
  170. //获取触摸点, 设置为终点
  171. PathSearchInfo::m_endX = _sp->m_x;
  172. PathSearchInfo::m_endY = _sp->m_y;
  173. //计算路径
  174. calculatePath();
  175. //绘制路径
  176. drawPath(  );
  177. //移动物体
  178. playerMove();
  179. }
  180. //        }
  181. //
  182. //    }
  183. return true;
  184. }
  185. void HelloWorld::ccTouchMoved(CCTouch* touch, CCEvent* event)
  186. {
  187. }
  188. void HelloWorld::ccTouchEnded(CCTouch* touch, CCEvent* event)
  189. {
  190. }
  191. void HelloWorld::menuCloseCallback(CCObject* pSender)
  192. {
  193. #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
  194. CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
  195. #else
  196. CCDirector::sharedDirector()->end();
  197. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
  198. exit(0);
  199. #endif
  200. #endif
  201. }
  202. void HelloWorld::clearPath()
  203. {
  204. for (vector<PathSprite*>::iterator iter = PathSearchInfo::m_haveInspectList.begin(); iter !=  PathSearchInfo::m_haveInspectList.end(); iter++)
  205. {
  206. (*iter)->m_sprite->setColor(ccWHITE);
  207. (*iter)->m_costToSource = 0;
  208. (*iter)->m_FValue = 0;
  209. (*iter)->m_parent = NULL;
  210. (*iter)->m_child = NULL;
  211. PathSearchInfo::m_inspectArray[(*iter)->m_x][(*iter)->m_y] = (*iter);
  212. }
  213. //把移除了障碍物的地图放入检测列表中
  214. //PathSearchInfo::m_inspectList = PathSearchInfo::m_mapList;
  215. PathSearchInfo::m_openList.clear();
  216. PathSearchInfo::m_pathList.clear();
  217. PathSearchInfo::m_haveInspectList.clear();
  218. PathSearchInfo::m_startX = m_player->m_x;
  219. PathSearchInfo::m_startY = m_player->m_y;
  220. m_player->m_sprite->stopAllActions();
  221. m_playerMoveStep = 0;
  222. }
  223. void HelloWorld::playerMove()
  224. {
  225. m_playerMoveStep++;
  226. if (m_playerMoveStep >= PathSearchInfo::m_pathList.size()) {
  227. return;
  228. }
  229. m_player->m_x = PathSearchInfo::m_pathList[m_playerMoveStep]->m_x;
  230. m_player->m_y = PathSearchInfo::m_pathList[m_playerMoveStep]->m_y;
  231. //根据路径列表移动人物
  232. m_player->m_sprite->runAction(CCSequence::create(CCMoveTo::create(1/24.0, PathSearchInfo::m_pathList[m_playerMoveStep]->m_sprite->getPosition()), CCCallFunc::create(this, SEL_CallFunc(&HelloWorld::playerMove)) , NULL));
  233. }
  234. void HelloWorld::update(float dt)
  235. {
  236. this->setPosition(m_orignPoint - m_player->m_sprite->getPosition());
  237. }