生活随笔
收集整理的這篇文章主要介紹了
Cocos2d-x 寻路算法解析(一): 距离优先
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
尋路這塊在游戲中一直很重要,花了點時間研究了下這個問題,主要參考的是《Data Structures For Game Programmers》,其他的算法用普通Console演示就行了,尋路算法還是用一個界面比較好,最近在學Cocos2d-x,就用它了。
1.效果圖
用到Cocos2d-x中的基本畫線段,畫矩形就行了,還有簡單的sprite拖動。這demo建了一個線條類,繼承CCNode,重寫draw方法就行了。在draw方法中簡單地調用ccDrawColor4F函數來設置顏色,ccDrawLine來畫線條,非常容易,cocos2d-x這些函數封裝了opengles中的原始函數,使用非常簡單。sprite拖動可以參考這篇文章《cocos2d-x Touch 事件應用的一個例子 》
?
1.小人和紅色X都可以用鼠標移動,移到上面的地圖上,表示尋路起點和終點。
2.Distance, Simple Heuristic, Complex Heuristic, A Star分別是4種尋路算法,點擊程序就會開始演示尋路過程。
3.地圖的格子點擊會加深顏色,總共4個等級,白,灰,深灰,黑,表示該格子的通過難度,白色是1,灰是2,深灰是3,黑色是不可通過區域。
4.”+++”表示加快演示速度,”—”表示降低演示速度。
2. Breadth – First Search算法
顧名思義,有點像呼吸,一層層地擴展開來,這個時候隊列(Queue),stl中的deque就派上用場了。deque不懂可以參考這篇文章《C++ Queue Example Rearranging RailRoad Cars》
?
起點在中心,會先訪問它的第一個外圈,再是第二個。現在我覺得它更像一顆石頭扔在水面上的效果。
下面是偽代碼:
?
BreadthFirst( Node )Queue.Enqueue( Node )Mark( Node )While( Queue.IsNotEmpty )Process( Queue.Front )For Each Child of Queue.Frontif NotMarked( Child )Queue.Enqueue( Child )Mark( Child )end ifend ForQueue.Dequeue()End WhileEnd Function 復制代碼
遍歷一個樹或者圖都可以用這個方法。在我們這里遇到了點麻煩,因為我們都知道斜角的距離是根號2的倍數,要比上下左右方向遠,因為尋路,很重要的因素的距離的長短。我們需要考慮距離這個因素了。
3.Distance – First Search
?
起點還在中心,這張圖顯示了每一格到中心的估算距離。如果依靠距離優先的算法,下圖是尋路次序:
?
所以我們定義了一個方向數組:
?
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}; 復制代碼
這樣通過一個for循環,就可以訪問它周圍一圈的格子了,而且是按照距離優先了,上下左右優先,斜角次些。
因為是地圖,我們這里簡單定義了一個2維數組,非常簡單用一個vector就可以模擬了,假定讀者熟悉stl中的vector和C++中的template,手機號交易平臺不熟悉可以參考這篇文章《STL Vector》和《C++ 基礎之 “模版函數”,”類模版”》
#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 復制代碼
我們還定義了一個Cell類表示每一個格子:它有很多屬性,像位置,最短距離到這個Cell的Cell的位置,是否已經處理過,到起點的距離,是否可以通過,還有就是這個Cell的權重,表示經過難度。我們這里使用了一個從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 復制代碼
核心算法如下:事先需要了解的知識:因為我們需要按照最短距離優先尋路,所以一個優先隊列就需要了,這里簡單地使用了heap,對heap不了解的可以看下這篇文章《HeapSort(堆排序 C++) 》,下面還用上了C++中的函數指針,可以參考這篇文章《C++ 函數指針 函數名作為參數 》,為什么要用函數指針呢?看完整個尋路算法系列你就知道了。
語言解釋:
先把起點Cell加入到heap中,對這個Cell的周圍8個Cell進行處理,主要是更新他們到起點的距離和記錄最短距離到這個Cell的Cell的位置。每次找到一個新的Cell,
1.如果還沒處理過,標上處理過標志,更新他們到起點的距離和記錄最短距離到這個Cell的Cell的位置,再把這個Cell加入到堆中,重新形成一個堆,這樣開始很容易得到離起點最近的點。
2.如果處理過,看下新的距離是不是比老的距離短,如果短,更新上面的提到的兩點。
不斷處理,直到訪問了所有的點或者找到終點了。
下面是代碼:整個尋路算法的核心代碼。
?
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 復制代碼
4.尋路動態圖:
?
我只是簡單地在起點和終點間加入了一個不可通過的墻,通過查看藍色的區域會發現這個算法很慢。目標在右邊,這個算法上下左右都找,雖然找到了也太浪費資源了吧?下篇我們來看看其他的尋路算法。
總結
以上是生活随笔為你收集整理的Cocos2d-x 寻路算法解析(一): 距离优先的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。