本文目的是对A*寻路算法所生成的路径进行一些人性化的调整,使其看起来不至于太机械化。关于A*算法的原理与实现,读者可以阅读其他资料,这里不再详细阐述。
如何写估价函数
A*寻路算法本质上是一个有方向性的广度优先搜索算法,它使用一个估价函数,来估测可能的最短路径,在每一次搜索迭代完成后,选取其邻接点中最优的一个(即,距离终点最近的一个点),作为下一次迭代的起点。如此反复,直到找到终点。下面先列出估价函数的常规写法:
设i点到起点的价值为S,到终点的估价为E,i点的总估价G等于S+E。S的值是确定的:
- S = parent.S + 1(i点是其父节点的水平或垂直方向上的邻接点)
- 或
- S = parent.S + sqrt(2))(i点是其父节点斜方向上的邻接点)
E点的值需要估算。精确一点的写法:
- 水平距离:dx = abs(ix - ex)
- 垂直距离:dy = abs(iy - ey)
- 需要斜着走过的距离:v1 = min(dx, dy) * sqrt(2)
- 需要直线走过的距离:v2 = max(dx, dy) - min(dx, dy)
- E = v1 + v2
粗略的写法:
- E = abs(ix - ex) + abs(iy - ey)
如何避免转向抖动
A*寻路得到的结果是最优的,但不是唯一的,这源于两点之间最近的路线可能不只一条。那么问题就产生了,两条最佳路线距离都相等的情况下,哪一条会更好?
(红色是障碍,白色可通行,黑色是搜索路径)
如上图所示,是A* 8方向搜索得到的两条距离相等的路线,但是左图的路线在中间位置发生了“拐弯”,要比右图的路线多一个“拐弯”。如果路线上拐弯太多,人物行走的过程中,会出现频繁转向,从而出现“抖动”现象。所以,我们判定右图路线优于左图路线。针对这一问题,我们可以通过修改估价函数,来选择“拐弯”更少的路线。
拐弯的问题,可以简化成先尽可能的向一个方向走,然后再考虑转向。进一步简化成,点越接近起点或是终点,越优先考虑。我们给E加上一个干扰值factor,
- factor = min(abs(ix - sx), abs(ix - ex)) + min(abs(iy - sy), abs(iy - ey))
- factor *= 0.01
factor的值不能过大,否则会造成搜索结果不是最短距离,因此适当的给factor乘上一个缩放系数。
如何远离障碍物
A*寻路的效果是抄近道,走捷径。但对于游戏体验来说,这并不完全是件好事,放着宽阔的马路不走,非得走悬崖峭壁,一不小心就跌落万丈深渊,或者卡在岩石边上。那么,我们该如何避免这些现象呢?同样,我们可以通过修改估计函数做到。
我们给每一块可走区域都加上一个干扰值,越靠近障碍的可走区域,其干扰值越大。干扰值计算方法:
- factor = 0
- for(x = -n; x <= n; ++x)
- {
- for(y = -n; y <= n; ++y)
- {
- if(isObstacle(ix + x, iy + y))
- {
- factor += n - min(abs(x), abs(y))
- }
- }
- }
我们甚至可以根据地表的材质来增加干扰值,比如山路和沼泽地带明显比马路的干扰值大。
后记
总之,我们可以调节估价函数来达到不同的效果。但是,也不能随意修改,不良的估价函数,会增加搜索成本。