# cocos2dx3.2 A*寻路算法

来源:互联网 发布:淘宝商品批量上传 编辑:程序博客网 时间:2024/06/14 06:04

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 instancebool 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}

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

这里写图片描述

源码下载链接

0 0