寻路算法 A* (一)
大家好,欢迎到我家来做客。”什么你要请我吃饭,要得嘛“。
你应该怎么到我家来呢,请自行使用百度地图。
但是在游戏中呢,玩家控制的英雄(英雄的当前位置 startGrid )要到要到地图中另一个地方去(以下简称目标位置 goalGrid ),难道你也说百度。(玩笑)。当然做游戏的人都知道用 A* 啦,这个还要说,真是的。
说到 A* 算法相信都不陌生。做游戏的人都多多少少的接触过,而且网上教程也是一大堆。比如非常详细的 http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx。(我开始学习 A* 也是看的这个)。
以下是我个人对 A* 的理解。
首先要明白 A* 是基于地图格子(可以是方形,三角形,六边形等)(每一个格子也是一个节点)寻路的。A* 中最重要的一点是要明白 A* 是启发式的。A* 的目的是要寻找从当前节点到目标节点的最短的路径。
A* 中重要的值 G,H, F
G 值:从 startGrid 开始沿着寻找的路径,移动到当前节点的移动耗费。每次计算现在的当前节点的 G 是通过它的父节点的 G 值加上这个节点本身自己的耗费。不同的节点耗费可能是不同的,比如 平地,山坡,沼泽,河流。通过他们的耗费肯定是不相同的。比如过河要坐船,遇到沼泽要绕行(否则.....)。
H 值:从指定节点到 goalGrid 的估计耗费。H 值的估算有很多方式,可以根据程序自己定义。也可以使用 曼哈顿方法。它计算从当前节点到目标及诶单之间的水平和垂直的节点的数量总和。比如现在的位置 curGrid 到 goalGrid 的 H 值的计算方式就是 H = abs(goalGrid.x - curGrid.x) + abs(goalGrid.y - curGrid.y);
F = G + H; F 即当前位置到目标位置的估计的总的耗费。(这个值最重要,一般来说 F 值越小就越靠近 goalGrid)
open 列表:从 startGrid 开始记录当前节点的周围节点。(借用别人的图了,希望原作者不要介意,如果有必要可以联系我,我会马上删除) 比如绿色的就是当前节点,其他的都是周围节点(这个周围的概念可以自己定义,比如我要模仿象棋中马的前进路线,当然就不是这样的了。)然后将周围的可以通过的节点统统的加入到 open 列表中,注意是可以通过的。然后需要对 open 列表中的所以元素按照 F 值最小排序。(排序方式有很多)
close 列表:记录的是从 startGrid 开始到 goalGrid 的过程中每次循环产生的 open 列表中 F 值最小的节点,这个节点也要从 open 列表中移除。
明白了以上概念,剩下的就是算法的主要部分了。
1、将 startGrid 加入到 open 列表中
2、在 do while 循环中重复一下工作(为什么是 do while 循环?欲知详情请听下回分解)
A、寻找 open 列表中 F 值最小的节点,(要记得将这个节点从 open 列表中移除),将这个节点作为当前节点 curNode
B、把 curNode 加入到 close 列表中
C、对 curNode 相邻的每一个节点做如下处理 (这里是一个 for 循环,相邻的节点一般不会只有一个吧) 临近节点(nearNode)
a、如果 nearNode 已经在 close 列表中,那么继续下一个 continue;
b、如果 nearNode 不在 open 列表中,那么将它添加进去,(这里可以对 open 列表进行排序),并且将curNode 设为 nearNode 的父节点。然后记录下 nearNode 的 G 和 H 值。(F = G + H)
c、如果 nearNode 已经在 open 列表中,如果新的 G 值低于nearNode 的 G 值。(更低的 G 值,意味着耗费越少,现在都是节约为主)。那么将 nearNode 的父节点更改为 curNode。并且重新设置 nearNode 的 G 值。
D、停止
1、如果找到了 goalGrid 并且将 goalGrid 加入到 close 列表中。
2、open 列表已经为空了。还是没有找到 goalGrid。(这个时候 goalGrid 通常是不可到达的。)这个时候路径不存在。
3、如果找到了路径接下来的工作当然就是要保存路径了。从 goalGrid 开始,沿着父节点一个节点一个节点的移动直到回到了 startGrid。这个就是我们程序要的路径。
哈哈,你找到到我家的路径没有。还来不来吃饭。
当然就我们目前的这个算法想想还有好多的问题。
1、存储 open 列表的时候算法不够优秀,如果真的用一个列表来存储,那么每次插入节点到 open 列表中的时候都要从头遍历 open 列表,挨个比较 F 值。直到找到一个比要插入的节点大的才停止。最好的时候是只比较 1 次,可是最差的时候要和 open 列表中所有的元素做比较。如果 open 列表中元素非常的多,这里开销将非常大。那该怎么办呢,当然有优化的方法。
2、程序中还有找不到路径的地方。在游戏中如果我是要移动到的目标是一个障碍物上面,那么肯定是找不到路径。角色一直站在那里不动,什么你不动是什么意思。这个给玩家的体验那是...”什么游戏啊,体验这么差,不玩了“。这个该怎么办呢。
这些问题都将在寻路算法 A* (二)中得到解决。