cocos2dx3.2 A*寻路算法
在此项工程中画出搜索路径,即将搜寻到的节点全部表示出来,暂时还没有列出最优路径,只是对A*算法的一个理解。
先看helloworld.h文件
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
USING_NS_CC;
/*
A*简单寻路,先定义一下节点类,属性有节点的坐标、G值,F值
*/
class NodePoint {
public:
Vec2 curPosition; // 当前的坐标
int GValue;
int FValue;
public:
NodePoint(Vec2 i_curPosition, Vec2 des_position, int i_Gvalue) {
curPosition = i_curPosition;
//函数F = G + H; H用曼哈顿算法(算出从该点到目标节点横纵的节点和)
int H = abs(des_position.x - i_curPosition.x) + abs(des_position.y - i_curPosition.y);
FValue = i_Gvalue + H;
}
};
/*
设置优先队列比较函数 最小的pop出来
*/
struct Cmp {
bool operator ()(NodePoint p1, NodePoint p2) {
return p1.FValue > p2.FValue;
};
};
class HelloWorld : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
// a selector callback
void menuCloseCallback(cocos2d::Ref* pSender);
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
public:
int GValue = 0;
Vec2 startPosition; // 起始的坐标
Vec2 endPosition; // 目的的坐标
Vec2 curPosition;
Vec2 tile_startPosition; //,用瓦片坐标来表示
Vec2 tile_endPosition; //,用瓦片坐标来表示
int markArray[20][30]; // 用来表示是否查询过,查询过的写1,未查询过的初始化为0
Sprite* sprite;
int touches = 0;
cocos2d::TMXLayer* tmxlayer;
cocos2d::TMXTiledMap* _tilemap;
//定义openlist closelist openlist是优先级队列
std::priority_queue<NodePoint, std::vector<NodePoint>, Cmp> openList;
std::vector<NodePoint> closeList;
std::vector<NodePoint>::iterator closeListIter;
public:
//将x,y转化为tile坐标 将点坐标转化为块坐标
Vec2 tileCoordForPosition(Vec2 position);
bool findPath(float dt);
virtual bool onTouchBegan(Touch *touch, Event *unused_event);
void checkPointAndaddOpen(Vec2 point); //判断点,加入openlist
};
#endif // __HELLOWORLD_SCENE_H__
再头文件中定义了节点类,优先级队列,以及helloworld类的一些函数,注释在代码中已经标出
接下来实现cpp
#include "HelloWorldScene.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto 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
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
_tilemap = TMXTiledMap::create("TileMap.tmx");
this->addChild(_tilemap);
tmxlayer = _tilemap->getLayer("Background");
tmxlayer->setAnchorPoint(Point(0.5f, 0.5f));
tmxlayer->setPosition(visibleSize.width / 2, visibleSize.height / 2);
startPosition = Vec2(100,100);
tile_startPosition = tileCoordForPosition(Vec2(100, 100));
markArray[(int)tile_startPosition.y][(int)tile_startPosition.x] = 1;
sprite = Sprite::create("food1.png");
sprite->setPosition((int)tile_startPosition.x*32+16, (int)(20-tile_startPosition.y)*32 - 16);
this->addChild(sprite);
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 30; j++) {
markArray[i][j] = 0;
}
}
auto listener = EventListenerTouchOneByOne::create();
listener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
return true;
}
bool HelloWorld::onTouchBegan(Touch* touch, Event* event) {
if (touches == 1) {
// tmxlayer->removeAllChildren();
Director::getInstance()->replaceScene(HelloWorld::createScene());
}
touches = 1;
// 点击确定终点
tile_endPosition = this->tileCoordForPosition(touch->getLocation());
curPosition = tile_startPosition;
int gid = tmxlayer->getTileGIDAt(Vec2((int)tile_endPosition.x, (int)tile_endPosition.y));
int mapvalue;
if (_tilemap->getPropertiesForGID(gid).isNull()) {
mapvalue = 0;
}
else {
mapvalue = _tilemap->getPropertiesForGID(gid).asValueMap().at("canTouch").asInt();
}
if (mapvalue == 1) {
MessageBox("not touch!", "error");
return true;
}
//每次启动一次寻找过程时设定一下GValue的值
GValue = 0;
//closeList.clear();
//开始寻找最佳路径
findPath(0.2);
return true;
}
/*
此函数为A*算法
*/
bool HelloWorld::findPath(float dt) {
//判断结束条件
if ((int)curPosition.x == (int)tile_endPosition.x &&
(int)curPosition.y == (int)tile_endPosition.y) {
return true; //此时找到终点
}
//判断4个方向的节点是否在openlist列表中,若不在,则放入openlist;
//周围4个节点的情况下,不用去考虑新的G值小于原来的G值,而去改变父节点
//log("AAAAAAAAAAAAAAAAAAAA%d,%d", curPosition.x, curPosition.y);
//先判断上方
log("****************up***************");
checkPointAndaddOpen(Vec2(curPosition.x, curPosition.y - 1));
//判断左方
log("****************left***************");
checkPointAndaddOpen(Vec2(curPosition.x - 1, curPosition.y));
//判断下方
log("****************down***************");
checkPointAndaddOpen(Vec2(curPosition.x, curPosition.y + 1));
//判断右方
log("****************right***************");
checkPointAndaddOpen(Vec2(curPosition.x + 1, curPosition.y));
//重置当前的位置
if (openList.empty()) {
MessageBox("not a right path!", "error");
return true;
}
curPosition = openList.top().curPosition;
log("curPositionX=%d,curPositionY=%d", (int)curPosition.x, (int)curPosition.y);
//将F最小的节点放入closelist
closeList.push_back(openList.top());
//移除该节点从openlist
openList.pop();
//在路径之中添加精灵
sprite = Sprite::create("food.png");
//sprite->setPosition((int)tile_startPosition.x * 32 + 16, (int)(20 - tile_startPosition.y) * 32 - 16);
this->addChild(sprite);
sprite->setPosition((int)curPosition.x *32 + 16, (int)(20-curPosition.y)* 32 - 16);
//递归
findPath(dt);
}
void HelloWorld::checkPointAndaddOpen(Vec2 point) {
GValue = sqrt(((int)point.x - (int)curPosition.x)*((int)point.x - (int)curPosition.x) + ((int)point.y - (int)curPosition.y)*((int)point.y - (int)curPosition.y));
if ((int)point.x >= 0 && (int)point.y >= 0 && (int)point.x <= 29 && (int)point.y <= 19) { //判断是否越界
//接下来判断上方点是否在closeList 或者为木桩等
int gid = tmxlayer->getTileGIDAt(Vec2((int)point.x, (int)point.y));
int mapvalue;
if (_tilemap->getPropertiesForGID(gid).isNull()) {
mapvalue = 0;
}
else {
mapvalue = _tilemap->getPropertiesForGID(gid).asValueMap().at("canTouch").asInt();
}
//log("****************mapvalue*******************%d",mapvalue);
//log("%d,%d, value = %d", (int)point.y, (int)point.x, markArray[(int)point.y][(int)point.x]);
if (mapvalue == 0 && markArray[(int)point.y][(int)point.x] != 1) {
//将该点创建为节点,并且加入优先队列
auto tmpPoint = new NodePoint(point, tile_endPosition, GValue);
openList.push(*tmpPoint);
markArray[(int)point.y][(int)point.x] = 1;
}
}
}
Vec2 HelloWorld::tileCoordForPosition(Vec2 position) {
int x = position.x / _tilemap->getTileSize().width; //点坐标除瓦片的宽度
int y = (_tilemap->getMapSize().height * _tilemap->getTileSize().height - position.y) / _tilemap->getTileSize().height;
return Vec2(x, y);
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
return;
#endif
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
运行结果如下