若是你還不熟悉A*尋路,請先看下這篇文章http://blog.csdn.net/dssdss123/article/details/11494065
java
1、先介紹幾個函數和結構:node
一、virtual void draw()app
這個函數跟與MFC上單文檔裏的OnDraw函數很像,這裏只是少了dc,這個函數會一直被調用,無需刷新,也就是說,你無需像在MFC上同樣調用Invalidate或者InvalidateRectiphone
二、virtual void ccTouchBegan(CCTouch* pTouch, CCEvent* pEvent)函數
這個函數是下響應觸摸的,當你點擊屏幕時,就會進到這個函數。要使這個函數有效,你須要在init中調用
setTouchEnabled(true); // 容許該層響應觸摸
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, false); // 註冊單點觸摸ui
在這個例子中,咱們不須要多點觸摸this
三、ccColor4F結構spa
這個結構在ccDrawSolidRect函數中將會使用到,ccDrawSolidRect是畫某種顏色的矩形,對應在MFC中,咱們使用的是FillSolidRect。ccColor4F是RGBA的結構,RGB是三顏色,紅綠藍,最後一個alpha值,他表示這個顏色的透明度。爲1是徹底不透明,0時則徹底透明。.net
2、實現code
一、去掉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);
二、初始化地圖
聲明結構表示格子掩碼等一些信息,咱們的例子中,只需掩碼,因此結構以下:
struct ST_GRID { ST_GRID() { gf = GRID_FLAG_DEFAULT; } GRID_FLAG gf; }; typedef vector<ST_GRID*> 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); } }
三、尋路
定義一個結構,用於尋路過程當中,記錄每一個格子的信息
struct NODE { NODE() {nIndex = 0; nG = 0; pParent = NULL;} int nIndex; int nG; NODE* pParent; }; vector<NODE*> m_vecPath; // 尋路的路徑
下面開始尋路
void HelloWorld::FindPath() { vector<NODE*> vecClose; // close表 vector<NODE*> 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; } }
四、展現到界面上
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<ST_GRID*> VEC_GRID; VEC_GRID m_vecGrid; struct NODE { NODE() {nIndex = 0; nG = 0; pParent = NULL;} int nIndex; int nG; NODE* pParent; }; vector<NODE*> 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<NODE*> &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 instance bool 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<NODE*> vecClose; vector<NODE*> 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<NODE*> &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; }
哎,想來想去,仍是直接上源碼比較直截了當。。。。。尋路效果以下,紅色是起點,綠色是終點,藍色是障礙物,淺藍色是最終尋路路徑: