生活随笔
收集整理的這篇文章主要介紹了
Cocos2d-x 寻路算法解析(一): 距离优先
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
尋路這塊在游戲中一直很重要,花了點(diǎn)時(shí)間研究了下這個(gè)問題,主要參考的是《Data Structures For Game Programmers》,其他的算法用普通Console演示就行了,尋路算法還是用一個(gè)界面比較好,最近在學(xué)Cocos2d-x,就用它了。 1.效果圖 用到Cocos2d-x中的基本畫線段,畫矩形就行了,還有簡(jiǎn)單的sprite拖動(dòng)。這demo建了一個(gè)線條類,繼承CCNode,重寫draw方法就行了。在draw方法中簡(jiǎn)單地調(diào)用ccDrawColor4F函數(shù)來設(shè)置顏色,ccDrawLine來畫線條,非常容易,cocos2d-x這些函數(shù)封裝了opengles中的原始函數(shù),使用非常簡(jiǎn)單。sprite拖動(dòng)可以參考這篇文章《cocos2d-x Touch 事件應(yīng)用的一個(gè)例子 》 ?
1.小人和紅色X都可以用鼠標(biāo)移動(dòng),移到上面的地圖上,表示尋路起點(diǎn)和終點(diǎn)。 2.Distance, Simple Heuristic, Complex Heuristic, A Star分別是4種尋路算法,點(diǎn)擊程序就會(huì)開始演示尋路過程。 3.地圖的格子點(diǎn)擊會(huì)加深顏色,總共4個(gè)等級(jí),白,灰,深灰,黑,表示該格子的通過難度,白色是1,灰是2,深灰是3,黑色是不可通過區(qū)域。 4.”+++”表示加快演示速度,”—”表示降低演示速度。 2. Breadth – First Search算法 顧名思義,有點(diǎn)像呼吸,一層層地?cái)U(kuò)展開來,這個(gè)時(shí)候隊(duì)列(Queue),stl中的deque就派上用場(chǎng)了。deque不懂可以參考這篇文章《C++ Queue Example Rearranging RailRoad Cars》 ?
起點(diǎn)在中心,會(huì)先訪問它的第一個(gè)外圈,再是第二個(gè)?,F(xiàn)在我覺得它更像一顆石頭扔在水面上的效果。 下面是偽代碼: ?
BreadthFirst( Node ) Queue.Enqueue( Node )Mark( Node ) While( Queue.IsNotEmpty ) Process( Queue.Front ) For Each Child of Queue.Front if NotMarked( Child ) Queue.Enqueue( Child ) Mark( Child ) end if end For Queue.Dequeue() End While End Function 復(fù)制代碼
遍歷一個(gè)樹或者圖都可以用這個(gè)方法。在我們這里遇到了點(diǎn)麻煩,因?yàn)槲覀兌贾佬苯堑木嚯x是根號(hào)2的倍數(shù),要比上下左右方向遠(yuǎn),因?yàn)閷ぢ?#xff0c;很重要的因素的距離的長(zhǎng)短。我們需要考慮距離這個(gè)因素了。 3.Distance – First Search ?
起點(diǎn)還在中心,這張圖顯示了每一格到中心的估算距離。如果依靠距離優(yōu)先的算法,下圖是尋路次序: ?
所以我們定義了一個(gè)方向數(shù)組: ?
const int DIRECTION[8][2]={ {0,1},??//north {1,0},??//east {0,-1},??//south {-1,0},??//west {1,1},??//northeast {1,-1},??//southeast {-1,-1},??//southwest {-1,1}??//northwest }; 復(fù)制代碼
這樣通過一個(gè)for循環(huán),就可以訪問它周圍一圈的格子了,而且是按照距離優(yōu)先了,上下左右優(yōu)先,斜角次些。 因?yàn)槭堑貓D,我們這里簡(jiǎn)單定義了一個(gè)2維數(shù)組,非常簡(jiǎn)單用一個(gè)vector就可以模擬了,假定讀者熟悉stl中的vector和C++中的template, 手機(jī)號(hào)交易平臺(tái) 不熟悉可以參考這篇文章《STL Vector》和《C++ 基礎(chǔ)之 “模版函數(shù)”,”類模版”》
#ifndef ARRAY2D_H #define ARRAY2D_H #include <vector> using namespace std; template <class Datatype> class Array2D{ public: ? ? Array2D(int p_width, int p_height):m_array(p_width * p_height), ? ?? ???m_width(p_width),m_height(p_height){ ? ? } ? ? Datatype* Get(int p_x, int p_y)const{ ? ?? ???return m_array[p_y * m_width + p_x]; ? ? } ? ? void Set(int p_x, int p_y, Datatype* data){ ? ?? ???m_array[p_y * m_width + p_x] = data; ? ? } ? ? int Size() const{ ? ?? ???return m_width * m_height; ? ? } ? ? int Width() const{ ? ?? ???return m_width; ? ? } ? ? int Height()const{ ? ?? ???return m_height; ? ? } private: ? ? vector<Datatype*> m_array; ? ? int m_width; ? ? int m_height; }; #endif 復(fù)制代碼
我們還定義了一個(gè)Cell類表示每一個(gè)格子:它有很多屬性,像位置,最短距離到這個(gè)Cell的Cell的位置,是否已經(jīng)處理過,到起點(diǎn)的距離,是否可以通過,還有就是這個(gè)Cell的權(quán)重,表示經(jīng)過難度。我們這里使用了一個(gè)從cocos2d-x中拷來的宏,這樣get和set方法就不用手寫了。 ?
#ifndef _CELL_H #define _CELL_H #define SYNTHESIZE(varType, varName, funName)\ protected: varType varName;\ public: virtual varType get##funName(void) const { return varName; }\ public: virtual void set##funName(varType var){ varName = var; } class Cell{ public: ? ? Cell():_marked(false),_distance(0),_lastX(-1),_lastY(-1), ? ?? ???_x(-1),_y(-1),_passable(true),_weight(1),_drawProgress(false){ ? ? } ? ? SYNTHESIZE(int, _x, X);? ?? ?? ?? ?? ?? ?? ???//start at left bottom ? ? SYNTHESIZE(int, _y, Y);? ?? ?? ?? ?? ?? ?? ???//start at left bottom ? ? SYNTHESIZE(int, _lastX, LastX);? ?? ?? ?? ?? ?//store the nearest cell's location related this cell ? ? SYNTHESIZE(int, _lastY, LastY);? ?? ?? ?? ?? ?//store the nearest cell's location related this cell ? ? SYNTHESIZE(bool, _marked, Marked);? ?? ?? ?? ?//whether this cell process or not ? ? SYNTHESIZE(float, _distance, Distance);? ?? ? //distance between this cell and start ? ? SYNTHESIZE(bool, _passable, Passable);? ?? ???//whether this call can pass ? ? SYNTHESIZE(int, _drawProgress, DrawProgress); //just for draw the path finding progress ? ? inline void setWeight(int weight){ ? ?? ???if(weight > 4){ ? ?? ?? ?? ?_weight = 1; ? ?? ???}else{ ? ?? ?? ?? ?_weight = weight; ? ?? ?? ?? ?setPassable(weight == 4 ? false : true); ? ?? ???} ? ? } ? ? inline int getWeight()const{ return _weight;} private: ? ? int _weight;? ?? ?? ?? ???//default is 1, 4 means this cell is impassable. ? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? //distance have relationship with weight }; #endif 復(fù)制代碼
核心算法如下:事先需要了解的知識(shí):因?yàn)槲覀冃枰凑兆疃叹嚯x優(yōu)先尋路,所以一個(gè)優(yōu)先隊(duì)列就需要了,這里簡(jiǎn)單地使用了heap,對(duì)heap不了解的可以看下這篇文章《HeapSort(堆排序 C++) 》,下面還用上了C++中的函數(shù)指針,可以參考這篇文章《C++ 函數(shù)指針 函數(shù)名作為參數(shù) 》,為什么要用函數(shù)指針呢?看完整個(gè)尋路算法系列你就知道了。 語言解釋: 先把起點(diǎn)Cell加入到heap中,對(duì)這個(gè)Cell的周圍8個(gè)Cell進(jìn)行處理,主要是更新他們到起點(diǎn)的距離和記錄最短距離到這個(gè)Cell的Cell的位置。每次找到一個(gè)新的Cell, 1.如果還沒處理過,標(biāo)上處理過標(biāo)志,更新他們到起點(diǎn)的距離和記錄最短距離到這個(gè)Cell的Cell的位置,再把這個(gè)Cell加入到堆中,重新形成一個(gè)堆,這樣開始很容易得到離起點(diǎn)最近的點(diǎn)。 2.如果處理過,看下新的距離是不是比老的距離短,如果短,更新上面的提到的兩點(diǎn)。 不斷處理,直到訪問了所有的點(diǎn)或者找到終點(diǎn)了。 下面是代碼:整個(gè)尋路算法的核心代碼。 ?
typedef bool (*compareTwoCells)(Cell *c1, Cell *c2); bool compareTwoCellsByDistance(Cell *c1, Cell *c2){ ? ? if(c1->getDistance() <= c2->getDistance()){ ? ?? ???return false; ? ? }else{ ? ?? ???return true; ? ? } } void HelloWorld::startPathFinding(compareTwoCells compareMethod, int startX,int startY,int goalX,int goalY){ ? ? Cell *startCell = _m_Map.Get(startX, startY); ? ? vector<Cell*> vecCells; ? ? vecCells.push_back(startCell); ? ? make_heap(vecCells.begin(),vecCells.end(),compareMethod); ? ? startCell->setMarked(true); ? ? Cell *nowProcessCell; ? ? while(vecCells.size() != 0){ ? ?? ???pop_heap(vecCells.begin(),vecCells.end(),compareMethod); ? ?? ???nowProcessCell = vecCells.back(); ? ?? ???vecCells.pop_back(); ? ?? ???if(nowProcessCell->getX() == _goalX && nowProcessCell->getY() == _goalY){//the goal is reach ? ?? ?? ?? ?return; ? ?? ???} ? ?? ???for(int i = 0; i < 8; ++i){ //check eight direction ? ?? ?? ?? ?int indexX = nowProcessCell->getX() + DIRECTION[i][0]; ? ?? ?? ?? ?int indexY = nowProcessCell->getY() + DIRECTION[i][1]; ? ?? ?? ?? ?if(indexX >= 0 && indexX < xLineCount && indexY >= 0 && indexY < yLineCount ? ?? ?? ?? ?? ? && _m_Map.Get(indexX,indexY)->getPassable() == true){//check is a OK cell or not ? ?? ?? ?? ?? ?? ???Cell *cell = _m_Map.Get(indexX,indexY); ? ?? ?? ?? ?? ?? ???float beforeDistance = DISTANCE[i] * cell->getWeight() + _m_Map.Get(nowProcessCell->getX(), ? ?? ?? ?? ?? ?? ?? ?? ?nowProcessCell->getY())->getDistance();//calculate the distance ? ?? ?? ?? ?? ?? ???if(cell->getMarked() == false){ ? ?? ?? ?? ?? ?? ?? ?? ?cell->setMarked(true); ? ?? ?? ?? ?? ?? ?? ?? ?cell->setLastX(nowProcessCell->getX()); ? ?? ?? ?? ?? ?? ?? ?? ?cell->setLastY(nowProcessCell->getY()); ? ?? ?? ?? ?? ?? ?? ?? ?cell->setDistance(beforeDistance); ? ?? ?? ?? ?? ?? ?? ?? ?vecCells.push_back(cell);//only push the unmarked cell into the vector ? ?? ?? ?? ?? ?? ?? ?? ?push_heap(vecCells.begin(),vecCells.end(),compareMethod); ? ?? ?? ?? ?? ?? ???}else{// if find a lower distance, update it ? ?? ?? ?? ?? ?? ?? ?? ?if(beforeDistance < cell->getDistance()){ ? ?? ?? ?? ?? ?? ?? ?? ?? ? cell->setDistance(beforeDistance); ? ?? ?? ?? ?? ?? ?? ?? ?? ? cell->setLastX(nowProcessCell->getX()); ? ?? ?? ?? ?? ?? ?? ?? ?? ? cell->setLastY(nowProcessCell->getY()); ? ?? ?? ?? ?? ?? ?? ?? ?? ? make_heap(vecCells.begin(),vecCells.end(),compareMethod);//distance change,so make heap again ? ?? ?? ?? ?? ?? ?? ?? ?} ? ?? ?? ?? ?? ?? ???} ? ?? ?? ?? ?} ? ?? ???} ? ? } } startPathFinding(compareTwoCellsByDistance,_playerX,_playerY,_goalX,_goalY);//demo 復(fù)制代碼
4.尋路動(dòng)態(tài)圖: ?
我只是簡(jiǎn)單地在起點(diǎn)和終點(diǎn)間加入了一個(gè)不可通過的墻,通過查看藍(lán)色的區(qū)域會(huì)發(fā)現(xiàn)這個(gè)算法很慢。目標(biāo)在右邊,這個(gè)算法上下左右都找,雖然找到了也太浪費(fèi)資源了吧?下篇我們來看看其他的尋路算法。
總結(jié)
以上是生活随笔 為你收集整理的Cocos2d-x 寻路算法解析(一): 距离优先 的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔 推薦給好友。