本文源码下载地址:http://download.csdn.net/detail/sun2043430/5907609(第一版)
http://download.csdn.net/detail/sun2043430/5909315(第二版)
https://github.com/sun2043430/A_Star_Algorithm (github下载地址,推荐到这里下载,这里是最新版)
本文地址:http://blog.csdn.net/sunnianzhong/article/details/9899359
最近回头温习云风的书,看到A*寻路算法,这个算法也是我一直想学习实现一下的。趁着周末有空练习练习。
网上关于A*算法的文章、代码和示例已经相当多了,有很多文章写的都很好,还有很多国外的网页用JS动态演示了A*算法寻路的过程。我找到的一些资源如下:
1 http://www.raywenderlich.com/zh-hans/21503/a%E6%98%9F%E5%AF%BB%E8%B7%AF%E7%AE%97%E6%B3%95%E4%BB%8B%E7%BB%8D
点评:生动的例子,讲解的非常浅显,易上手的示例。(入门级)
2 http://theory.stanford.edu/~amitp/GameProgramming/
点评:长篇大作。非常详细地介绍了A*算法的原理,演化过程,启发式,算法实现,A*实现中各种数据结构的优劣,A*算法变种,实际使用A*算法会遇到的问题。(深入级)
3 http://www.ccg.leeds.ac.uk/people/j.macgill/xaStar/
点评:使用java脚本制作的动态演示A*寻路算法的网页。演示了经典A*算法和Fudge A*算法 的动态寻路过程。(很有参考价值)
我用MFC模拟了经典A*算法的动态寻路过程,基本上模仿了这个网页。
感谢以上文章的作者和云风的书!
A*算法是Dijkstra算法和贪婪算法的综合,Dijkstra算法的缺点在于从起点全方位360地向外做广度优先搜索,导致遍历节点太多,速度较慢,优点是能够保证找到最优路径。贪婪算法总是选择看起来最优的路线前进,优点是速度很快,缺点是有可能掉入陷阱,而走冤枉路。而A*算法采用启发式的方式,综合了二者的优点,且依然能够保证找到最优路径。
以一个最简单的例子来说明一下A*算法的启发式寻路过程,顺便介绍一些A*算法中的概念。
以上图为例,红色为起点,绿色是终点。我们假设每一个方块只能直接到达其上下左右四个方块,不可以走斜线。每一个方块有3个值f,g,h(左上角是f值,左下角是g值,右下角是h值),其中:
首先,从起点出发,可以往上下右3个反向走,所以上下右3个方块的g值为1。但是上下两个方块到终点的启发式步数h是7(横向6步,竖向1步),右边方块的启发式步数h是5。将g和h相加得到f值。
那么接下来,我们就选择f值最小的方块(右边的方块)来继续寻路过程……具体算法我们在下面给出。
1 开两个空队列OPEN,CLOSE
2 将起点的g值设为0,并计算出对应的h,f值,将起点放入OPEN队列
3 从OPEN队列中取出f值最小的节点放入临时队列TempQueue(一开始就是起点这一个点;另外f值最小的可能不止一个,如有多个则都取出)(取出的意思是OPEN队列中就不再保存这些节点了)
4 如果TempQueue队列为空,也就意味着OPEN队列为空,说明已经遍历完整个地图,此时应该退出寻路过程【退出条件1:地图遍历完,走不到终点】
5 将TempQueue中的所有节点加入到CLOSE队列中
6 遍历TempQueue中的每一个节点Node
处理Node的每一个邻居节点Neighbor
如果Neighbor既不在OPEN队列也不在CLOSE队列中,计算出Neighbor节点的g,h,f值(g值等于Node的g值加一个单位),设置Neighbor的父节点为Node,并将Neighbor加入到OPEN队列
如果Neighbor在CLOSE队列中则不处理(在动态的游戏中可能需要看情况做处理,简单情况下无需处理)
如果Neighbor在OPEN队列中,则用Neighbor之前的f值和现在计算出来的f值进行比较。如果现在计算出来的f值更小,则更新Neighbor的f值,并重新设置Neighbor的父节点为Node
如果Neighbor就是终点,则寻路过程结束,从终点根据父节点的指向到CLOSE队列中去找父节点,可倒推出完整的最短路径【退出条件2:走到终点】
7 回到步骤3继续处理
void CAStar::FindPath()
{
m_bFind = false;
vector minVec;
while (!m_bFind)
{
minVec.clear();
GetMinFromOpen(minVec);
if (minVec.empty())
break;
m_close.insert(m_close.end(), minVec.begin(), minVec.end());
for (vector::iterator it = minVec.begin(); it != minVec.end(); it++)
DoNeighbors(*it);
if (m_bFind)
GetFinalShortestPath();
if (m_CallBack) m_CallBack(m_pArg);
}
}
DEMO程序运行界面如下
在右边3个单选按钮中选择一个,然后可在左边设置起点、终点或者障碍物。
点击开始寻路,则开始动态演示寻路过程。
其中:
红色方块为起点
绿色方块为终点
蓝色方块为OPEN队列中的点
黄色方块为CLOSE队列中的点
紫色方块为最短路径
计算过f,g,h值的节点,其f值在左上角;g值在左下角;h值在右下角。
请原谅本人糟糕的MFC编程。画面预显示做的很不好,不知道该在哪个消息中预绘制图形,而且因为没有使用双缓冲绘图,所以画面闪烁厉害。另外,寻路过程没有使用多线程,所以寻路过程中画面会卡住不能移动,这些方面希望在下一版中能够改进。另外我打算在下一个版本中加入可斜向移动的功能。
本文地址:http://blog.csdn.net/sunnianzhong/article/details/9899359
1 增加了斜向移动的功能,对应的每一步移动的权值进行了修改,平行或竖直移动一格的权值是5,斜向移动的权值是7。斜向移动时需要判断是否会穿过障碍物方块。如下的情况是不允许的。
1 | 2 |
3 | 4 |
2 采用内存DC缓冲方式,画面先画到内存DC,再一次性 BitBlt 贴到控件上,使得画面不再闪烁。
3 寻路过程放到一个独立的线程中去运算,使得主界面不再被卡住。
4 增加寻路过程中的暂停、继续功能。更方便查看中间状态。