博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
cocos2d-x的A*寻路
阅读量:6842 次
发布时间:2019-06-26

本文共 18531 字,大约阅读时间需要 61 分钟。

如果你还不熟悉A*寻路,请先看下这篇文章http://blog.csdn.net/dssdss123/article/details/11494065

一、先介绍几个函数和结构:

1、virtual void draw()

这个函数跟与MFC上单文档里的OnDraw函数很像,这里只是少了dc,这个函数会一直被调用,无需刷新,也就是说,你无需像在MFC上一样调用Invalidate或者InvalidateRect

2、virtual void ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent)

这个函数是下响应触摸的,当你点击屏幕时,就会进到这个函数。要使这个函数有效,你需要在init中调用

setTouchEnabled(true);    // 允许该层响应触摸
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, false);    // 注册单点触摸

在这个例子中,我们不需要多点触摸

3、ccColor4F结构

这个结构在ccDrawSolidRect函数中将会使用到,ccDrawSolidRect是画某种颜色的矩形,对应在MFC中,我们使用的是FillSolidRect。ccColor4F是RGBA的结构,RGB是三颜色,红绿蓝,最后一个alpha值,他表示这个颜色的透明度。为1是完全不透明,0时则完全透明。

二、实现

1、去掉coco自带的乱七八糟的显示

1)去掉帧频显示

在函数bool AppDelegate::applicationDidFinishLaunching()中

// turn on display FPS

//pDirector->setDisplayStats(true);

将pDirector->setDisplayStats(true),注释掉

2)去掉menu,Hello World文字标签

在函数void HelloWorld::init()中

// Add the menu to HelloWorld layer as a child layer.

//this->addChild(pMenu, 1);

// Add the label to HelloWorld layer as a child layer.
//this->addChild(pLabel, 1);

将menu和label注释掉

3)替换背景图,并置于最底层

// 3. Add add a splash screen, show the cocos2d splash image.

CCSprite* pSprite = CCSprite::create("map.jpg");    // 将原先的HelloWorld.png,替换为自己的图片,这里我换成map.jpg
CC_BREAK_IF(! pSprite);

2、初始化地图

声明结构表示格子掩码等一些信息,我们的例子中,只需掩码,所以结构如下:

 

struct ST_GRID    {        ST_GRID()  { gf = GRID_FLAG_DEFAULT; }        GRID_FLAG gf;    };    typedef vector
VEC_GRID; VEC_GRID m_vecGrid; // 保存地图产生的所有格子

 

初始化地图的格子掩码,如下:

 

void HelloWorld::InitMap(){    // 初始化格子掩码    srand((unsigned int)time(NULL));    for (int i = 0; i < GetRow() * GetCol(); i++)    {        int nRandFlag = ((int)(CCRANDOM_0_1() * 10)) % 4 == 0 ? GRID_FLAG_OBSTACLE : GRID_FLAG_DEFAULT;    // 十分之四的概率产生障碍        ST_GRID* pGrid = new ST_GRID;        if (!pGrid)        {            return ;        }        pGrid->gf = (GRID_FLAG)nRandFlag;        m_vecGrid.push_back(pGrid);    }}

3、寻路

 

定义一个结构,用于寻路过程中,记录每个格子的信息

 

struct NODE    {        NODE()  {nIndex = 0; nG = 0; pParent = NULL;}        int nIndex;        int nG;        NODE* pParent;    };    vector
m_vecPath; // 寻路的路径

下面开始寻路

 

 

void HelloWorld::FindPath(){    vector
vecClose; // close表 vector
vecOpen; // open表 if (m_nStartIndex == -1 || m_nEndIndex == -1) { return ; } m_vecPath.clear(); // 这里,我们并没有delete,但却不会内存泄漏,因为cocos2d-x使用了跟java一样的技术 -- 内存回收机制,自动处理垃圾 // 先添加开始点 NODE* pNode = new NODE; pNode->nIndex = m_nStartIndex; vecClose.push_back(pNode); int nStep = 0; while(true) { if (nStep++ >= 200) // 最多寻200格 { break; } NODE* pNextNode = vecClose[vecClose.size() - 1]; // 取下一个路径 if (!pNextNode) { break; } if (pNextNode->nIndex == m_nEndIndex) // 找到终点,就不再找了 { break; } for (int i = 0; i < 8; i++) { int nIndex = GetIndexByDir(pNextNode->nIndex, i); // 根据方向取索引 if (-1 == nIndex) { continue; } if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE) // 障碍 { continue; } if (InTable(nIndex, vecClose) != NULL) // 在close表里 { continue; } NODE* pNode = InTable(nIndex, vecOpen); // 在open表里,比较G值,取G值更小的为新路径 if (pNode) { int nNewG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex); if (pNode->nG > nNewG) { pNode->nG = nNewG; pNode->pParent = pNextNode; // 改变节点的父节点 } continue; } // 新搜索到的格子,添加到开放列表 pNode = new NODE; pNode->nIndex = nIndex; pNode->nG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex); pNode->pParent = pNextNode; vecOpen.push_back(pNode); } // 找下一个路径,open表里F值最小的就是了 int nMinF = 0xFFFFFF; pNextNode = NULL; int nNextNodeIndex = 0; for (int i = 0; i < (int)vecOpen.size(); i++) { NODE* pNode = vecOpen[i]; if (!pNode) { continue; } int nH = GetHByIndex(pNode->nIndex); // 计算该点与终点的H值,即路径长度 int nF = nH + pNode->nG; // F = H + G if (nF < nMinF) { nMinF = nF; pNextNode = pNode; nNextNodeIndex = i; } } // 找到F值最小的,放入close表,并从open表里删除  if (nNextNodeIndex >= 0 && nNextNodeIndex < (int)vecOpen.size()) { vecClose.push_back(pNextNode); vecOpen.erase(vecOpen.begin() + nNextNodeIndex); } } // 寻路结束,找最优路径 pNode = vecClose[vecClose.size() - 1]; while (pNode) { m_vecPath.push_back(pNode); pNode = pNode->pParent; }}

4、展示到界面上

 

 

void HelloWorld::draw(){    // 画背景表格    CCSize size = CCDirector::sharedDirector()->getWinSize();    for (int i = 0; i < GetRow(); i++)    {        ccDrawLine(ccp(0, i * GRID_SIDELEN), ccp(size.width, i * GRID_SIDELEN));    }    for (int i = 0; i < GetCol(); i++)    {        ccDrawLine(ccp(i * GRID_SIDELEN, 0), ccp(i * GRID_SIDELEN, size.height));    }    // 画特殊格子颜色    // 寻路得到的路径    for (int i = 0; i < (int)m_vecPath.size(); i++)    {        CCPoint ptObstacleLT;        CCPoint ptObstacleRD;        GetRectPointByIndex(m_vecPath[i]->nIndex, ptObstacleLT, ptObstacleRD);        ccColor4F clrObstacle = {0, 1, 1, 0};        ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);    }    // 开始点    CCPoint ptStartLT;    CCPoint ptStartRD;    GetRectPointByIndex(m_nStartIndex, ptStartLT, ptStartRD);    ccColor4F clrStart = {1, 0, 0, 1};    ccDrawSolidRect(ptStartLT, ptStartRD, clrStart);    // 结束点    CCPoint ptEndLT;    CCPoint ptEndRD;    GetRectPointByIndex(m_nEndIndex, ptEndLT, ptEndRD);    ccColor4F clrEnd = {0, 1, 0, 1};    ccDrawSolidRect(ptEndLT, ptEndRD, clrEnd);    // 障碍    for (int i = 0; i < (int)m_vecGrid.size(); i++)    {        if (m_vecGrid[i]->gf == GRID_FLAG_OBSTACLE)        {            CCPoint ptObstacleLT;            CCPoint ptObstacleRD;            GetRectPointByIndex(i, ptObstacleLT, ptObstacleRD);            ccColor4F clrObstacle = {0, 0, 1, 1};            ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);        }    }}

这里,我只介绍几个比较重要的函数,其他的就不赘述了,资源已上传到CSDN,但还没显示出来,等显示出来了,再把链接发到此处,有疑问的童鞋留言哈

。。。。。。。。

啊,我还是直接上源码吧

 

#ifndef __HELLOWORLD_SCENE_H__#define __HELLOWORLD_SCENE_H__#include "cocos2d.h"#include "Box2D/Box2D.h"#include "SimpleAudioEngine.h"USING_NS_CC;enum STEP{    STEP_DEFAULT    = 0,    STEP_STARTPOINT = 1,    STEP_ENDPOINT   = 2,};enum GRID_FLAG{    GRID_FLAG_DEFAULT   = 0,        // 默认可通过    GRID_FLAG_OBSTACLE  = 1,        // 障碍};const int GRID_SIDELEN = 20;    // 不能为0//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 recommand to return the exactly class pointer    static cocos2d::CCScene* scene();        // a selector callback    void menuCloseCallback(CCObject* pSender);    // implement the "static node()" method manually    CREATE_FUNC(HelloWorld);public:    virtual void draw();    virtual bool ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent);private:    void InitMap();private:    int GetRow();    int GetCol();    int GetIndexByPoint(CCPoint pt);    void GetRectPointByIndex(int nIndex, CCPoint &ptLT, CCPoint &ptRD);private:    int m_nStartIndex;    int m_nEndIndex;    struct ST_GRID    {        ST_GRID()  { gf = GRID_FLAG_DEFAULT; }        GRID_FLAG gf;    };    typedef vector
VEC_GRID; VEC_GRID m_vecGrid; struct NODE { NODE() {nIndex = 0; nG = 0; pParent = NULL;} int nIndex; int nG; NODE* pParent; }; vector
m_vecPath; // 寻路的路径public: void FindPath();private: int GetIndexByDir(int nIndex, int nDir); int GetGByIndex(int nStartIndex, int nEndIndex); int GetHByIndex(int nIndex); NODE *InTable(int nIndex, vector
&vecTbl);private: int m_nStep;};#endif // __HELLOWORLD_SCENE_H__

 

 

 

#include "HelloWorldScene.h"using namespace cocos2d;CCScene* HelloWorld::scene(){    CCScene * scene = NULL;    do     {        // 'scene' is an autorelease object        scene = CCScene::create();        CC_BREAK_IF(! scene);        // 'layer' is an autorelease object        HelloWorld *layer = HelloWorld::create();        CC_BREAK_IF(! layer);        // add layer as a child to scene        scene->addChild(layer);    } while (0);    // return the scene    return scene;}// on "init" you need to initialize your instancebool HelloWorld::init(){    bool bRet = false;    do     {        //        // super init first        //        CC_BREAK_IF(! CCLayer::init());        //        // add your codes below...        //        // 1. Add a menu item with "X" image, which is clicked to quit the program.        // Create a "close" menu item with close icon, it's an auto release object.        CCMenuItemImage *pCloseItem = CCMenuItemImage::create(            "CloseNormal.png",            "CloseSelected.png",            this,            menu_selector(HelloWorld::menuCloseCallback));        CC_BREAK_IF(! pCloseItem);        // Place the menu item bottom-right conner.        pCloseItem->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width - 20, 20));        // Create a menu with the "close" menu item, it's an auto release object.        CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);        pMenu->setPosition(CCPointZero);        CC_BREAK_IF(! pMenu);        // Add the menu to HelloWorld layer as a child layer.        //this->addChild(pMenu, 1);        // 2. Add a label shows "Hello World".        // Create a label and initialize with string "Hello World".        CCLabelTTF* pLabel = CCLabelTTF::create("Hello World", "Arial", 24);        CC_BREAK_IF(! pLabel);        // Get window size and place the label upper.         CCSize size = CCDirector::sharedDirector()->getWinSize();        pLabel->setPosition(ccp(size.width / 2, size.height - 50));        // Add the label to HelloWorld layer as a child layer.        //this->addChild(pLabel, 1);        // 3. Add add a splash screen, show the cocos2d splash image.        CCSprite* pSprite = CCSprite::create("map.jpg");        CC_BREAK_IF(! pSprite);        // Place the sprite on the center of the screen        pSprite->setPosition(ccp(size.width/2, size.height/2));        // Add the sprite to HelloWorld layer as a child layer.        this->addChild(pSprite, -1);        setTouchEnabled(true);        CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,false);        m_nStep = STEP_STARTPOINT;        m_nStartIndex = -1;        m_nEndIndex = -1;        InitMap();        bRet = true;    } while (0);    return bRet;}void HelloWorld::menuCloseCallback(CCObject* pSender){    // "close" menu item clicked    CCDirector::sharedDirector()->end();}//void HelloWorld::InitMap(){    srand((unsigned int)time(NULL));    for (int i = 0; i < GetRow() * GetCol(); i++)    {        int nRandFlag = ((int)(CCRANDOM_0_1() * 10)) % 4 == 0 ? GRID_FLAG_OBSTACLE : GRID_FLAG_DEFAULT;        ST_GRID* pGrid = new ST_GRID;        if (!pGrid)        {            return ;        }        pGrid->gf = (GRID_FLAG)nRandFlag;        m_vecGrid.push_back(pGrid);    }}//void HelloWorld::FindPath(){    vector
vecClose; vector
vecOpen; if (m_nStartIndex == -1 || m_nEndIndex == -1) { return ; } m_vecPath.clear(); // 先添加开始点 NODE* pNode = new NODE; pNode->nIndex = m_nStartIndex; vecClose.push_back(pNode); int nStep = 0; while(true) { if (nStep++ >= 200) { break; } NODE* pNextNode = vecClose[vecClose.size() - 1]; if (!pNextNode) { break; } if (pNextNode->nIndex == m_nEndIndex) { break; } for (int i = 0; i < 8; i++) { int nIndex = GetIndexByDir(pNextNode->nIndex, i); if (-1 == nIndex) { continue; } if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE) // 障碍 { continue; } if (InTable(nIndex, vecClose) != NULL) // 在close表里 { continue; } NODE* pNode = InTable(nIndex, vecOpen); // 在open表里 if (pNode) { int nNewG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex); if (pNode->nG > nNewG) { pNode->nG = nNewG; pNode->pParent = pNextNode; } continue; } // 新搜索到的格子 pNode = new NODE; pNode->nIndex = nIndex; pNode->nG = pNextNode->nG + GetGByIndex(pNextNode->nIndex, pNode->nIndex); pNode->pParent = pNextNode; vecOpen.push_back(pNode); } int nMinF = 0xFFFFFF; pNextNode = NULL; int nNextNodeIndex = 0; for (int i = 0; i < (int)vecOpen.size(); i++) { NODE* pNode = vecOpen[i]; if (!pNode) { continue; } int nH = GetHByIndex(pNode->nIndex); int nF = nH + pNode->nG; if (nF < nMinF) { nMinF = nF; pNextNode = pNode; nNextNodeIndex = i; } } if (nNextNodeIndex >= 0 && nNextNodeIndex < (int)vecOpen.size()) { vecClose.push_back(pNextNode); vecOpen.erase(vecOpen.begin() + nNextNodeIndex); } } // 寻路结束,找最优路径 pNode = vecClose[vecClose.size() - 1]; while (pNode) { m_vecPath.push_back(pNode); pNode = pNode->pParent; }}//int HelloWorld::GetIndexByDir(int nIndex, int nDir){ if (nIndex < 0 || nIndex >= (int)m_vecGrid.size()) { return -1; } int nRow = nIndex / GetCol(); int nCol = nIndex % GetCol(); switch(nDir) { case 0: // 上 nRow += 1; break; case 1: // 右上 nRow += 1; nCol +=1; break; case 2: // 右 nCol += 1; break; case 3: // 右下 nRow -= 1; nCol += 1; break; case 4: // 下 nRow -= 1; break; case 5: // 左下 nRow -= 1; nCol -= 1; break; case 6: // 左 nCol -= 1; break; case 7: // 左上 nRow += 1; nCol -= 1; break; default: break; } if (nRow < 0 || nRow >= GetRow() || nCol < 0 || nCol >= GetCol()) { return -1; } return nRow * GetCol() + nCol;}//int HelloWorld::GetGByIndex(int nStartIndex, int nEndIndex){ int nStartRow = nStartIndex / GetCol(); int nStartCol = nStartIndex % GetCol(); int nEndRow = nEndIndex / GetCol(); int nEndCol = nEndIndex % GetCol(); if (nStartRow == nEndRow || nStartCol == nEndCol) { return 10; } return 14;}//int HelloWorld::GetHByIndex(int nIndex){ int nRow = nIndex / GetCol(); int nCol = nIndex % GetCol(); int nEndRow = m_nEndIndex / GetCol(); int nEndCol = m_nEndIndex % GetCol(); return (abs(nEndRow - nRow) + abs(nEndCol - nCol))*10;}//HelloWorld::NODE *HelloWorld::InTable(int nIndex, vector
&vecTbl){ for (int i = 0; i < (int)vecTbl.size(); i++) { if (nIndex == vecTbl[i]->nIndex) { return vecTbl[i]; } } return NULL;}//int HelloWorld::GetRow(){ CCSize size = CCDirector::sharedDirector()->getWinSize(); return size.height / GRID_SIDELEN;}//int HelloWorld::GetCol(){ CCSize size = CCDirector::sharedDirector()->getWinSize(); return size.width / GRID_SIDELEN;}//int HelloWorld::GetIndexByPoint(CCPoint pt){ pt.x = pt.x > (int)pt.x ? pt.x + 1 : pt.x; pt.y = pt.y > (int)pt.y ? pt.y + 1 : pt.y; return (int)pt.y / GRID_SIDELEN * GetCol() + (int)pt.x / GRID_SIDELEN;}//void HelloWorld::draw(){ // 画背景表格 CCSize size = CCDirector::sharedDirector()->getWinSize(); for (int i = 0; i < GetRow(); i++) { ccDrawLine(ccp(0, i * GRID_SIDELEN), ccp(size.width, i * GRID_SIDELEN)); } for (int i = 0; i < GetCol(); i++) { ccDrawLine(ccp(i * GRID_SIDELEN, 0), ccp(i * GRID_SIDELEN, size.height)); } // 画特殊格子颜色 // 寻路得到的路径 for (int i = 0; i < (int)m_vecPath.size(); i++) { CCPoint ptObstacleLT; CCPoint ptObstacleRD; GetRectPointByIndex(m_vecPath[i]->nIndex, ptObstacleLT, ptObstacleRD); ccColor4F clrObstacle = {0, 1, 1, 1}; ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle); } // 开始点 CCPoint ptStartLT; CCPoint ptStartRD; GetRectPointByIndex(m_nStartIndex, ptStartLT, ptStartRD); ccColor4F clrStart = {1, 0, 0, 1}; ccDrawSolidRect(ptStartLT, ptStartRD, clrStart); // 结束点 CCPoint ptEndLT; CCPoint ptEndRD; GetRectPointByIndex(m_nEndIndex, ptEndLT, ptEndRD); ccColor4F clrEnd = {0, 1, 0, 1}; ccDrawSolidRect(ptEndLT, ptEndRD, clrEnd); // 障碍 for (int i = 0; i < (int)m_vecGrid.size(); i++) { if (m_vecGrid[i]->gf == GRID_FLAG_OBSTACLE) { CCPoint ptObstacleLT; CCPoint ptObstacleRD; GetRectPointByIndex(i, ptObstacleLT, ptObstacleRD); ccColor4F clrObstacle = {0, 0, 1, 1}; ccDrawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle); } }}//void HelloWorld::GetRectPointByIndex(int nIndex, CCPoint &ptLT, CCPoint &ptRD){ ptLT.x = nIndex % GetCol() * GRID_SIDELEN; ptLT.y = nIndex / GetCol() * GRID_SIDELEN; ptRD.x = ptLT.x + GRID_SIDELEN; ptRD.y = ptLT.y + GRID_SIDELEN;}//bool HelloWorld::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent){ if (!pTouch) { return false; } int nIndex = GetIndexByPoint(pTouch->getLocation()); if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE) { return false; } if (STEP_STARTPOINT == m_nStep) { m_nStartIndex = nIndex; m_nStep = STEP_ENDPOINT; } else if (STEP_ENDPOINT == m_nStep) { m_nEndIndex = nIndex; m_nStep = STEP_STARTPOINT; FindPath(); } return true;}

 

哎,想来想去,还是直接上源码比较直截了当。。。。。寻路效果如下,红色是起点,绿色是终点,蓝色是障碍物,浅蓝色是最终寻路路径:

 

 

 

你可能感兴趣的文章
[剑指offer] 变态跳台阶
查看>>
首次公开!阿里搜索中台开发运维一体化实践
查看>>
面象对象设计6大原则之四:接口隔离原则
查看>>
Ansible介绍、安装、远程执行命令、拷贝文件或者目录、远程执行脚本
查看>>
异常中要了解的Throwable类中的几个方法
查看>>
【前端,干货】react and redux教程学习实践(二)。
查看>>
Fiddler 抓包浅析(二)
查看>>
Ant + Tomcat + Jenkins 实现自动化部署
查看>>
奥迪明年将在美推出汽车共享车队
查看>>
万达网络首席架构师:回复雷盈CEO,致敬区块链创业者!
查看>>
阿里达摩院布局“中国芯”,自研AI芯片性价比超同类40倍
查看>>
ShowDoc v2.4.10 发布,IT 团队的在线 API 文档工具
查看>>
Macaca入门篇(iOS)
查看>>
甲骨文宣布开源 GraphPipe,一种机器学习模型的新标准
查看>>
使用Eclipse开发Java应用并部署到SAP云平台SCP上去
查看>>
区块链:重新定义世界,崛起于草根的“颠覆性”技术
查看>>
为保证太空超级计算机可长期使用,惠普宣布已成功为其加电
查看>>
SqlMap 初尝试
查看>>
Python学习笔记-邮件模块SMTP
查看>>
限制优化时间和事件数的最佳方法
查看>>