# cocos2dx3.2 A*寻路算法

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
}

运行结果如下
这里写图片描述

这里写图片描述

源码下载链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值