上一章告诉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());
}
版权声明:本文博客原创文章,博客,未经同意,不得转载。