Cocos2d-x 寻路算法之一 距离优先

原创 2013年12月05日 09:06:41

1.效果图


寻路这块在游戏中一直很重要,花了点时间研究了下这个问题,主要参考的是《Data Structures For Game Programmers》,其他的算法用普通Console演示就行了,寻路算法还是用一个界面比较好,最近在学Cocos2d-x,就用它了。用到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.Front
if NotMarked( Child )
Queue.Enqueue( Child )
Mark( Child )
end if
end For
Queue.Dequeue()
End While
End 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.寻路动态图:




我只是简单地在起点和终点间加入了一个不可通过的墙,通过查看蓝色的区域会发现这个算法很慢。目标在右边,这个算法上下左右都找,虽然找到了也太浪费资源了吧?下篇我们来看看其他的寻路算法。


5.项目下载:

(请用7z解压,开发工具vs2010)

 http://www.waitingfy.com/?attachment_id=828


A*算法应用可以看下这篇文章《贪吃蛇 AI 的实现 snake AI

相关文章推荐

cocos2dx 3.x 实现 A星(A*)(A-star)算法自动寻路(一)

自动寻路里面的说的最多的就是A星寻路了,但是网上找了些博客大家写的有点简略,导致对于刚接触的人来说理解的不够清楚。在这里我将用大量的图片一步一步地列出A星算法的寻路过程。A星算法对于大地图的效率不高,...

如何在Cocos2D游戏中实现A*寻路算法(一)

大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供...
  • mydo
  • mydo
  • 2015年11月22日 10:28
  • 1358

如何实现A星寻路算法 Cocos2d-x 3.0 beta2

本文实践自 Johann Fradj 的文章《How To Implement A* Pathfinding with Cocos2D Tutorial》,文中使用Cocos2D,我在这里使用Coco...
  • akof1314
  • akof1314
  • 2014年02月17日 13:50
  • 10811

Cocos2d-x 寻路算法之三 A Star

1.A Star 寻路算法介绍: 看过之前的两篇文章:《Cocos2d-x 寻路算法之二 离目的地的距离优先》,《Cocos2d-x 寻路算法之一 距离优先》的读者知道,这两种寻路算法都有问题,前...

Cocos2d-x 寻路算法之二 离目的地的距离优先

1.介绍: Figure 1 接上一篇《Cocos2d-x 寻路算法之一 距离优先》,看这个图,我们发现这个寻路算法有点傻,明明终点在右侧却每个方向都找。难道没有其他办法了吗?从现实生活中...

cocos2d-x学习日志(14) --A星寻路算法之45度地图

一、A星搜索 他就是一种启发性的算法,根据现在到达这个位置的步数及之后的“估计步数”,即f=g+h,f是整个从起点到终点的代价,g是从起点到我们目前位置的步数,h是从目前位置到终点的估计值,注意这...

菜鸟福利 A星寻路算法 cocos2d-x实现

这篇blog是翻译自iOS Tutorial Team的成员 Johann Fradj发,他目前是一位全职的资深iOS开发工程师。他是HotApps Factory的创始人,该公司开发了AppCook...

如何实现A星寻路算法 Cocos2d-x 3.0 beta2

本文实践自 Johann Fradj 的文章《How To Implement A* Pathfinding with Cocos2D Tutorial》,文中使用Cocos2D,我在这里使用Coco...

cocos2d-x上的A*寻路

  • 2013年09月17日 21:30
  • 308KB
  • 下载

cocos2d-x寻路

  • 2015年04月14日 17:44
  • 13.09MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Cocos2d-x 寻路算法之一 距离优先
举报原因:
原因补充:

(最多只允许输入30个字)