cocos2dx A* + tiledMap
本文转自:http://blog.csdn.net/w18767104183/article/category/1757765
前面一章讲了cocos2dx 中使用A星算法
这章中讲 A*结合tiledmap
先看下效果图
图有点丑,忍受下
绿色的块 表示人物的行走的路线(A*算法的结果)
红色部分 表示A*算法搜寻过的点(越少,速度越快)
黑色的部分(其实是无色块,因为背景是黑色的) 表示障碍物
这张图是用tiledmap做出来的, 看看里面的内容
可以看到 我把不能通过的地区的图块给删了
tiledmap中有2个层 一个是background, 一个是road. 为了方便, 我把road也用同样的图片, 最好的方法是用一种同样的瓦片拼接出来一条能走的路, 让后把background图层加到road图层上就ok了.
下面直接上源码, 用的时cocos2.2.3, 拷贝到项目中就能用了.当然别忘了自己做个像样的tiledMap .
如果你觉得好用, 就在文章底下顶一个吧 , enjoy it !
- #ifndef __HELLOWORLD_SCENE_H__
- #define __HELLOWORLD_SCENE_H__
- #include "cocos2d.h"
- #include "vector"
- using namespace std;
- USING_NS_CC;
- #define MAP_WIDTH 200//要比tmx中的map大
- #define MAP_HEIGHT 200
- class PathSprite
- {
- public:
- PathSprite(CCSprite* sprite)
- {
- m_parent = NULL;
- m_child = NULL;
- m_costToSource = 0;
- m_FValue = 0;
- m_sprite = sprite;
- };
- public:
- CCSprite* m_sprite;//包含的瓦片精灵
- PathSprite* m_parent;//父节点
- PathSprite* m_child;//子节点
- float m_costToSource;//到起始点的距离
- int m_x;//地图坐标
- int m_y;
- float m_FValue;
- };
- class PathSearchInfo//寻路类(主要负责寻路的参数和逻辑)
- {
- public:
- static int m_startX;//开始点
- static int m_startY;
- static int m_endX;//结束点
- static int m_endY;
- static CCSize m_mapSize;//地图大小
- static CCSize m_tileSize;//地图的块大小
- static vector<PathSprite*> m_openList;//开放列表(里面存放相邻节点)
- static PathSprite* m_inspectArray[MAP_WIDTH][MAP_HEIGHT];//全部需要检测的点
- static vector<PathSprite*> m_pathList;//路径列表
- static vector<PathSprite*> m_haveInspectList;//检测过的列表
- static float calculateTwoObjDistance(PathSprite* obj1, PathSprite* obj2)//计算两个物体间的距离
- {
- // float _offsetX = obj1->m_x - obj2->m_x;
- // float _offsetY = obj1->m_y - obj2->m_y;
- // return sqrt( _offsetX * _offsetX + _offsetY * _offsetY);
- float _x = abs(obj2->m_x - obj1->m_x);
- float _y = abs(obj2->m_y - obj1->m_y);
- return _x + _y;
- }
- static void inspectTheAdjacentNodes(PathSprite* node, PathSprite* adjacent, PathSprite* endNode)//把相邻的节点放入开放节点中
- {
- if (adjacent)
- {
- float _x = abs(endNode->m_x - adjacent->m_x);
- float _y = abs(endNode->m_y - adjacent->m_y);
- float F , G, H1, H2, H3;
- adjacent->m_costToSource = node->m_costToSource + calculateTwoObjDistance(node, adjacent);//获得累计的路程
- G = adjacent->m_costToSource;
- //三种算法, 感觉H2不错
- H1 = _x + _y;
- H2 = hypot(_x, _y);
- H3 = max(_x, _y);
- #if 1 //A*算法 = Dijkstra算法 + 最佳优先搜索
- F = G + H2;
- #endif
- #if 0//Dijkstra算法
- F = G;
- #endif
- #if 0//最佳优先搜索
- F = H2;
- #endif
- adjacent->m_FValue = F;
- adjacent->m_parent = node;//设置父节点
- adjacent->m_sprite->setColor(ccORANGE);//搜寻过的节点设为橘色(测试用)
- m_haveInspectList.push_back(adjacent);
- node->m_child = adjacent;//设置子节点
- PathSearchInfo::m_inspectArray[adjacent->m_x][adjacent->m_y] = NULL;//把检测过的点从检测列表中删除
- PathSearchInfo::m_openList.push_back(adjacent);//加入开放列表
- }
- }
- static PathSprite* getMinPathFormOpenList()//从开放节点中获取F值最小值的点
- {
- if (m_openList.size()>0) {
- PathSprite* _sp =* m_openList.begin();
- for (vector<PathSprite*>::iterator iter = m_openList.begin(); iter != m_openList.end(); iter++)
- {
- if ((*iter)->m_FValue < _sp->m_FValue)
- {
- _sp = *iter;
- }
- }
- return _sp;
- }
- else
- {
- return NULL;
- }
- }
- static PathSprite* getObjFromInspectArray(int x, int y)//根据横纵坐标从检测数组中获取点
- {
- if (x >=0 && y >=0 && x < m_mapSize.width && y < m_mapSize.height) {
- return m_inspectArray[x][y];
- }
- return NULL;
- }
- static bool removeObjFromOpenList( PathSprite* sprite)//从开放列表中移除对象
- {
- if (!sprite) {
- return false;
- }
- for (vector<PathSprite*>::iterator iter = m_openList.begin(); iter != m_openList.end(); iter++)
- {
- if (*iter == sprite)
- {
- m_openList.erase(iter);
- return true;
- }
- }
- return false;
- }
- };
- class HelloWorld : public cocos2d::CCLayer
- {
- public:
- // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
- virtual bool init();
- // there's no 'id' in cpp, so we recommend returning the class instance pointer
- static cocos2d::CCScene* scene();
- // a selector callback
- void menuCloseCallback(CCObject* pSender);
- // implement the "static node()" method manually
- CREATE_FUNC(HelloWorld);
- void onEnter();
- virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
- virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
- virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
- void calculatePath();//计算路径
- void drawPath();//绘制路径(测试用)
- void clearPath();//清理路径
- void playerMove();//人物走动
- void update(float dt);//跟新大地图(行走时, 人不动, 地图跟着人动);
- public:
- CCPoint m_orignPoint;//人物的起始点
- PathSprite* m_player;//人物点
- int m_playerMoveStep;//人物当前的行程的索引
- };
- #endif // __HELLOWORLD_SCENE_H__
- #include "HelloWorldScene.h"
- USING_NS_CC;
- vector<PathSprite*> PathSearchInfo::m_openList;
- PathSprite* PathSearchInfo::m_inspectArray[MAP_WIDTH][MAP_HEIGHT] = {NULL};
- vector<PathSprite*> PathSearchInfo::m_pathList;
- vector<PathSprite*> PathSearchInfo::m_haveInspectList;
- CCSize PathSearchInfo::m_mapSize;
- CCSize PathSearchInfo::m_tileSize;
- int PathSearchInfo::m_startX;
- int PathSearchInfo::m_startY;
- int PathSearchInfo::m_endX;
- int PathSearchInfo::m_endY;
- CCScene* HelloWorld::scene()
- {
- // 'scene' is an autorelease object
- CCScene *scene = CCScene::create();
- // 'layer' is an autorelease object
- HelloWorld *layer = HelloWorld::create();
- // add layer as a child to scene
- scene->addChild(layer);
- // return the scene
- return scene;
- }
- // on "init" you need to initialize your instance
- void HelloWorld::onEnter()
- {
- CCDirector* pDirector = CCDirector::sharedDirector();
- pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
- CCLayer::onEnter();
- }
- bool HelloWorld::init()
- {
- //////////////////////////////
- // 1. super init first
- if ( !CCLayer::init() )
- {
- return false;
- }
- CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
- CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
- /////////////////////////////
- // 2. add a menu item with "X" image, which is clicked to quit the program
- // you may modify it.
- CCLabelTTF* pLabel = CCLabelTTF::create("A* + tiledMap", "Arial", 24);
- // position the label on the center of the screen
- pLabel->setPosition(ccp(origin.x + visibleSize.width/2,
- origin.y + visibleSize.height - pLabel->getContentSize().height));
- // add the label as a child to this layer
- this->addChild(pLabel, 1);
- this->scheduleUpdate();
- CCTMXTiledMap* map = CCTMXTiledMap::create("gameMap.tmx");
- this->addChild(map);
- map->setPosition(CCPoint());
- CCTMXLayer* _road = map->layerNamed("road");//行走路径的地图
- CCSize _mapSize = map->getMapSize();
- for (int j = 0; j < _mapSize.height; j++) {
- for (int i = 0; i < _mapSize.width; i++) {
- CCSprite* _sp = _road->tileAt(CCPoint(i, j));
- if (_sp) {
- PathSprite* _pathSprite = new PathSprite(_sp);
- _pathSprite->m_x = i;
- _pathSprite->m_y = j;
- PathSearchInfo::m_inspectArray[i][j] = _pathSprite;//把地图中所有的点一一对应放入检测列表中
- }
- }
- }
- PathSearchInfo::m_mapSize = _mapSize;//获取地图的尺寸
- PathSearchInfo::m_tileSize = map->getTileSize();//获取瓦片的尺寸
- //设置起始和终点
- PathSearchInfo::m_startX =30;
- PathSearchInfo::m_startY = 75;
- //创建一个人物
- m_player = new PathSprite(CCSprite::create("10001.png"));
- m_player->m_sprite->setAnchorPoint(CCPoint(0.5,0));
- this->addChild(m_player->m_sprite);
- m_player->m_x = PathSearchInfo::m_startX;//设置人物的起始的地图坐标
- m_player->m_y = PathSearchInfo::m_startY;
- m_orignPoint = PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY]->m_sprite->getPosition();
- m_player->m_sprite->setPosition(m_orignPoint);//设置人物的起始的世界坐标
- return true;
- }
- void HelloWorld::calculatePath()
- {
- //得到开始点的节点
- PathSprite* _startNode = PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY];
- //得到结束点的节点
- PathSprite* _endNode = PathSearchInfo::m_inspectArray[PathSearchInfo::m_endX][PathSearchInfo::m_endY];
- //因为是开始点 把到起始点的距离设为0, F值也为0
- _startNode->m_costToSource = 0;
- _startNode->m_FValue = 0;
- //把已经检测过的点从检测列表中删除
- PathSearchInfo::m_inspectArray[PathSearchInfo::m_startX][PathSearchInfo::m_startY] = NULL;
- //把该点放入已经检测过点的列表中
- PathSearchInfo::m_haveInspectList.push_back(_startNode);
- //然后加入开放列表
- PathSearchInfo::m_openList.push_back(_startNode);
- PathSprite* _node = NULL;
- while (true)
- {
- //得到离起始点最近的点(如果是第一次执行, 得到的是起点)
- _node = PathSearchInfo::getMinPathFormOpenList();
- if (!_node)
- {
- //找不到路径
- break;
- }
- //把计算过的点从开放列表中删除
- PathSearchInfo::removeObjFromOpenList( _node);
- int _x = _node->m_x;
- int _y = _node->m_y;
- //
- if (_x ==PathSearchInfo::m_endX && _y == PathSearchInfo::m_endY)
- {
- break;
- }
- //检测8个方向的相邻节点是否可以放入开放列表中
- CCLog("%d, %d",_x, _y);
- PathSprite* _adjacent = PathSearchInfo::getObjFromInspectArray( _x + 1, _y + 1);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- _adjacent = PathSearchInfo::getObjFromInspectArray( _x +1, _y);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- _adjacent = PathSearchInfo::getObjFromInspectArray( _x +1, _y-1);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- _adjacent = PathSearchInfo::getObjFromInspectArray( _x , _y -1);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- _adjacent = PathSearchInfo::getObjFromInspectArray( _x -1, _y - 1);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- _adjacent = PathSearchInfo::getObjFromInspectArray( _x -1, _y);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- _adjacent = PathSearchInfo::getObjFromInspectArray( _x -1, _y+1);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- _adjacent = PathSearchInfo::getObjFromInspectArray( _x , _y+1);
- PathSearchInfo::inspectTheAdjacentNodes(_node, _adjacent, _endNode);
- }
- while (_node)
- {
- //把路径点加入到路径列表中
- PathSearchInfo::m_pathList.insert(PathSearchInfo::m_pathList.begin(), _node);
- _node = _node->m_parent;
- }
- }
- void HelloWorld::drawPath( )
- {
- for (vector<PathSprite*>::iterator iter = PathSearchInfo::m_pathList.begin(); iter != PathSearchInfo::m_pathList.end(); iter++)
- {
- (*iter)->m_sprite->setColor(ccGREEN);
- }
- }
- CCRect getBoundingBox(float x, float y, float width, float height)
- {
- return CCRect(x - width/2, y - height/2, width, height);
- }
- bool HelloWorld::ccTouchBegan(CCTouch* touch, CCEvent* event)
- {
- //清除之前的路径
- clearPath();
- auto nodePosition = convertToNodeSpace( touch->getLocation() );
- CCLog("%f, %f", nodePosition.x, nodePosition.y);
- // for (int i = 0; i < PathSearchInfo::m_inspectList.size(); i++)
- // {
- // PathSprite* _sp = PathSearchInfo::m_inspectList[i];
- //
- // CCRect _rect = getBoundingBox( _sp->m_sprite->getPositionX(), _sp->m_sprite->getPositionY(), _sp->m_sprite->getContentSize().width, _sp->m_sprite->getContentSize().height);
- //
- // if (_rect.containsPoint(nodePosition))
- // {
- PathSprite* _sp = PathSearchInfo::m_inspectArray[(int)(nodePosition.x/PathSearchInfo::m_tileSize.width)][(int)(PathSearchInfo::m_mapSize.height - nodePosition.y/PathSearchInfo::m_tileSize.height)];
- if (_sp) {
- CCLog("%f, %f", _sp->m_sprite->getPositionX(), _sp->m_sprite->getPositionY());
- //获取触摸点, 设置为终点
- PathSearchInfo::m_endX = _sp->m_x;
- PathSearchInfo::m_endY = _sp->m_y;
- //计算路径
- calculatePath();
- //绘制路径
- drawPath( );
- //移动物体
- playerMove();
- }
- // }
- //
- // }
- return true;
- }
- void HelloWorld::ccTouchMoved(CCTouch* touch, CCEvent* event)
- {
- }
- void HelloWorld::ccTouchEnded(CCTouch* touch, CCEvent* event)
- {
- }
- void HelloWorld::menuCloseCallback(CCObject* pSender)
- {
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
- CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
- #else
- CCDirector::sharedDirector()->end();
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- exit(0);
- #endif
- #endif
- }
- void HelloWorld::clearPath()
- {
- for (vector<PathSprite*>::iterator iter = PathSearchInfo::m_haveInspectList.begin(); iter != PathSearchInfo::m_haveInspectList.end(); iter++)
- {
- (*iter)->m_sprite->setColor(ccWHITE);
- (*iter)->m_costToSource = 0;
- (*iter)->m_FValue = 0;
- (*iter)->m_parent = NULL;
- (*iter)->m_child = NULL;
- PathSearchInfo::m_inspectArray[(*iter)->m_x][(*iter)->m_y] = (*iter);
- }
- //把移除了障碍物的地图放入检测列表中
- //PathSearchInfo::m_inspectList = PathSearchInfo::m_mapList;
- PathSearchInfo::m_openList.clear();
- PathSearchInfo::m_pathList.clear();
- PathSearchInfo::m_haveInspectList.clear();
- PathSearchInfo::m_startX = m_player->m_x;
- PathSearchInfo::m_startY = m_player->m_y;
- m_player->m_sprite->stopAllActions();
- m_playerMoveStep = 0;
- }
- void HelloWorld::playerMove()
- {
- m_playerMoveStep++;
- if (m_playerMoveStep >= PathSearchInfo::m_pathList.size()) {
- return;
- }
- m_player->m_x = PathSearchInfo::m_pathList[m_playerMoveStep]->m_x;
- m_player->m_y = PathSearchInfo::m_pathList[m_playerMoveStep]->m_y;
- //根据路径列表移动人物
- 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));
- }
- void HelloWorld::update(float dt)
- {
- this->setPosition(m_orignPoint - m_player->m_sprite->getPosition());
- }