A *路径搜索入门
帕特里克·莱斯特发表于2003年10月8日下午8点33人工智能
如果您发现文中有错误或问题(丢失的影像或文件,受损代码,不正确的文本格式等),导致无法阅读它时,请联系编辑,以便能更正。感谢您帮助我们改善这个资源。
更新于2005年7月18日
这篇文章已被翻译成阿尔巴尼亚语,中文,法语,德语,葡萄牙语,俄语和西班牙语。欢迎其他的翻译。在这篇文章的末尾有联系的电子邮件地址。
对于初学者, A *(读A-星,译者注:老外读A-star)算法可能有些复杂。虽然在网上有很多解释A *算法的文章,大多数写给有基础的。这篇是给纯新手的。(译者记:各位新手如果有看不懂的,对不起是翻译的错。)
本文并不试图成为这个主题的权威著作。相反,是阐述原理并准备让你能去阅读所有其他的材料及明白他们在说什么。在本文的末尾提供一些较好,能进一步阅读的资料的链接。
最后,这篇文章不是具体的方案。你应该能够接受在文中出现的任何计算机语言。就如同你所期望的那样,不管怎样, 在本文的末尾,有一个演示程序的链接。演示包中有两个版本:一个是C++,一个是Blitz Basic(http://www.blitzbasic.com/Home/_index_.php)。如果想运行A *的程序,也有可执行文件的。
可是我们正在超越自己。从头开始吧...
■简介:搜索区域
假设要想从A点到达B点。中间有一堵墙把AB两点隔开。如面所示,用绿色表示起点A,用红色表示终点B,并用蓝色表示中间的那堵墙。
[图1]
首先,把搜索区域分割成由方格组成的网格。这叫简化搜索区域,是路径搜索的第一步。这种方法把搜索区域简化成一个二维数组。数组中的每一个元素代表了网格里的一个方格,方格被记录为能走和不能走。从A点到B点所经过的方格叫路径。一旦路径被找到,就可以从一个方格的中心移到下一个的中心,直到到达目标。
这些中心点被称为“节点”。在看别的路径搜索资料时,经常会看到讨论节点。为什么不叫它们方格呢?因为真的有可能不把路径搜索区域分割成方格。可以是矩形,六边形,三角形或任何形状。节点可以用任何形状表示- 在中心或者沿着边缘,或其他任何地方。不过,因为方格是最简单的,我们使用方格。
■开始搜索
一旦把搜索区域简化成可管理数量的节点,就像上面所说的网格布局,下一步就是进行搜索来找到最短路径。从A点开始,检查相邻的方格并向外普及搜索,直到找到目标。
执行以下操作开始搜索:
1.从起点A开始,并将它添加到 “开启列表”。开启列表有点像一张购物单。尽管现在列表里只有一个项目,但以后会多起来。它包含了可能是你想要的,也可能不是。基本上,这是需要被检查的方格的列表。
2.寻找起点相邻的所有可到达或能走的方格,忽略有墙,水或其他非法的地形。把它们添加到开启列表。对于每个方格,保存A点作为它们的“父”。当要追溯路径时,父是很重要的。稍后会解释它。
3.从开启列表删除起点A,并将它添加到不需要再次查找的“关闭列表”。
在这一点上,你应该有类似下面插图的印象。在该图中,位于中心的深绿色方格就是开始方格。它是轮廓为浅蓝色,以指示它已被添加到关闭列表。对在开启列表的所有与它相邻的方格进行检查,并且用浅绿色来框记它们。每个方格都有一个灰色的指针指回它们的父,也就是开始方格。
[图2]
下一步,我们选择了开启列表上的一个相邻方格,并或多或少地重复前面的过程,如下所述。但是,我们选择哪方格呢?一个具有最小F值的。
■路径评分
在算出的路径时,下面的公式是确定要使用的方格的关键:
F = G + H
这里
G =从起点A沿着生成的路径移动到一个给定方格上的运行成本。
H =从给定方格移动到终点B的估计运行成本。这通常称为启发式,这可能有点混乱。因为是一个估测的所以这样称呼。在找到路径之前,我们真的不知道实际的距离,因为各种各样的事情都在途中(墙,水等)。在本教程中给出一个计算H的方法,但在网上的其他文章里能找到许多计算H方法。
通过反复遍历开启列表,选择具有最小F值的方格来生成我们的路径。在本文中这个过程将有进一步更详细的说明。首先来仔细看看如何用公式计算。
如上所述,G是从起始点移动到给定点所生成路径的运动成本。在这个例子中,我们将指定每个水平或垂直移动方格成本为10,对角线移动的成本为14。我们使用这些数字是因为沿斜边移动是2的平方根(不要害怕),是水平或垂直移动的大约1.414倍。我们使用10和14是为了简单起见。比例大致是正确的,又能避免计算平方根和小数。这不只是因为我们是愚笨的,不喜欢数学。采用这些数字是让计算更快,太快了。你很快就会发现,如果你不使用这些捷径,路径搜索可能会很缓慢的。
由于我们计算的G值是沿特定的路径到给定的方格,这个办法是找出那个方格的父的G值,然后加10或14取决于它从父的移动是正交(非对角线)还是对角线。在这个例子中这个方法的需要将进一步变得明显一点,因为我们从开始方格开始得到一个以上的方格。
H可以用各种方式估计。在这里使用曼哈顿方法,计算从当前方格到目标方格水平和垂直方向移动方格的总数,忽略对角运动,忽略用这种方式可能的任何障碍。然后,将总数乘以10,水平或垂直移动一格的成本。这是(可能)被称为曼哈顿方法,因为它像计算城市街区的数量从一个地方到另一个地方,并不能沿对角穿过。
阅读本说明,您可能已经猜到了启发式仅仅是当前方格与目标之间的剩余距离的一个“像乌鸦飞似的”(译者注:直线距离)粗略的估计。不是这样的。我们实际上试图估计沿路径的剩余距离(通常是更远)。估计越是接近实际剩余距离,就是越快的算法。如果高估了这个距离,那么,它不能保证给初的最短路径。在这样的情况下,我们有所谓的“不可接受启发式”。
从技术上讲,在这个例子中,曼哈顿方法是不可接受的,因为它稍稍高估了剩下的距离。但是我们会用也无妨,因为它是一个更容易理解我们意图的,因为它只是一个轻微的高估。在极少的情况下,得到的路径不是最短的,这将是逼近最短。想了解更多?你可以在这里(http://www.policyalmanac.org/games/heuristics.htm)找到关于启发式相同的或附加说明。
F是G加H的和。搜索的第一个步骤的结果可以从下面的说明中看出。在F,G和H的值被写入在每个方格。正如在紧挨着开始方格右侧的方格上,F被打印在左上角,G被打印在左下角,而H被打印在右下角。
[图3]
那么,来看看其中一些方格。在有字母的方格上,G = 10,这是因为它是在一个水平方向的距离起始方格仅有一个方格。紧邻在起始方格的上方,下方,以及左边的方格有相同的G值10,对角线方格G值为14。
H值是通过估计到红色目标方格的曼哈顿距离,计算方式是仅有水平和垂直方向移动,并忽略墙上的。使用这种方法,从这个方格开始直接向右3个格就是红格,H为30。(译者注:直线距离)那么高于这个方格的方格的H值是4格距离(记住,只能水平或垂直移动)的一个H值为40。你也许可以看到了为其他方格计算H值的方法。
每个方格的F值,再次,只是简单地把G和H加在一起的计算结果。
■继续搜索
要继续搜索,我们简单地选择在开启列表中具有最小F值的方格。然后,我们用选定方格作以下事情:
1.把它从开启列表取出,并添加到关闭列表。
2.检查所有的相邻方格。忽略那些在关闭列表里或不能走的(墙,水或其他非法地形),如果它们还不在开启列表中,添加到开启列表。将选定方格作为新方格的“父”。
3.如果相邻的方格已经在开启列表,查看沿这条路径到那个方格是否是好的。换句话说,检查看看如果我们使用当前方格到那里,方格的G值是否是较低,。如果不是,什么也不做。
另一方面,如果新路径的G值较低,改变相邻方格的父到选定方格(图中上方,改变指针的方向指向在所选择的方格)。最后,重新计算那个方格的F值和G值。如果这似乎令人困惑,在后面你会看到它的说明。
好吧,看看这是如何工作的。最初的9个方格,在起始方格被放到关闭列表后还有8个在开启列表。其中,具有最低F值的是一个紧邻起点右侧的方格,其F值为40。因此,选择这个方格作为下一个方格。如下图所示高亮的蓝色的部分。
[图4]
首先,从开启列表中删除它,并把它添加到关闭列表(这就是它以高亮蓝色显示的原因)。然后检查相邻的方格。好了,这个方格的相邻右边的是墙上的方格,忽略它。紧挨着左边的是开始方格。在关闭列表上,所以也忽略它。
其余4个方格已经在开启列表中,所以需要检查如果使用那些方格作为路径是否比使用这个方格到那里更好,将G值作为参照点。来看看在选择方块右上的这个方格吧。它的G值是14,如果我们经由当前方格到达那里,G值将等于20(10,现在方格的G值,再加上10来垂直移到它的上面)。G值20比14高,所以这不是一个更好的路径。如果你看一下图能更好的理解这些。从开始方格方格沿对角线移动一个方格到那里更直接些,而不是水平移动一个方格,再垂直移动一个方格。
当对在开启列表中的4个相邻方格重复作这个过程,会发现没有路径比当前的方格有提高,因此不改变任何东西。所以,现在,看了所有的相邻方格,用这个方格作完检查了,并准备移动到下一个方格。
那么,通过开启列表,现在减到7个方格的列表,我们再选择一个具有最小F值。有趣的是,在这种情况下,有两个方格的F值是54,那么我们选择哪一个呢?这其实并不重要。为了快速的目的,选择您最后一个添加到开启列表中的方格可以更快些。这种偏向的搜索比慢慢找搜索到更有赞成,当你更接近目标时。但它其实并不重要。 (不同的处理造成了两个版本的A *可能找到不同的等长路径。)
因此,我们选择开始方格右下的方格,如下图所示。
[图5]
这一次,检查相邻的方格,会发现,一到右边立即是一堵墙上的方格,所以忽略。同样运用刚才上面的。还忽略墙下方的方格。为什么呢?因为你不能让方格直接从当前方格切割穿越附近的墙角。在这个过程中你真的需要往下走,然后再挪动那个方格,绕着墙角移动。 (注:切割墙角的规则是可选的依赖于你的节点如何放置。)
那剩下5个方格。另外两个方格低于当前方格尚未在开启列表中,所以将它们添加并把当前方格变成它们的父。其他3个方格,有2个已经在关闭列表(开始方格,和一个略高于目前的方格上,在蓝色突出两个图中),所以忽略它们。而最后的方格,眼前的当前方格左侧,进行检查,如果你去通过当前方格到那里看是否有低的G值。没有方格了。所以,就大功告成了,已经检查完开启列表中的下一个方格。
重复这个过程,直到添加目标方格到关闭列表,此时它看起来像下面的插图。
[图6]
在上图中,需要注意的是开始方格下两格方格的父已经改变。之前,它的G值为28,指回它右上的方格。现在它有一个值为20,并指向它上面方格。这发生在沿路径搜索,对G值被检查,并采用了值小一些的作为新的路径- 这样父被切换,G值和F值也重新计算。在这个例子中虽然这种变化似乎并不太重要,有很多可能的情况,在确定到达目标的最佳路径时,持续的检查将会产生很大的差异。
那么,如何确定路径呢?简单,刚开始在红色的目标方格,并努力向父方格后移,下面的箭头。这最终会把你带回到开始方格,这就是你的路径。它应该看起来像下面的插图。移动从开始方格A到目标方格B就是从路径上每一个方格(节点)的中心移动到下一个方格的中心的问题,直到到达目标。
[图7]
■A *方法总结
好了,现在通过解释,在一个地方,展示一步一步的方法(译者注:在这儿,把每个步骤整理一下):
添加开始方格(或节点)到开启列表。
重复以下操作:
a) 寻找开启列表上最小F值的方格。将它作为当前方格。
b) 切换到关闭列表。
c) 对于当前方格临近的8个方格的每一个....(For Each)
如果不能走,或者如果它在关闭列表上,忽略它。否则,执行以下操作。
如果不在开启列表中,把它添加到开启列表。使当前方格成为这个方格的父。记录的方格F值,G值和H值。
如果在开启列表了,检查看看采用G值来衡量这个路径到那个方格是否是更好的。更低的G值意味着这是一个更好的路径。如果是这样,把方格的父改为当前方格,并重新计算方格的G值和F值。如果你保持开启列表按F值排序,由于这个变化你可能需重存列表。
d)当你停止:
在目标方格添加到关闭列表的情况下,路径已经被发现(见下面的注),或无法找到目标方格,并且开启列表是空的。在这种情况下,不存在路径。
保存路径。从目标方格往回走,从每个方格移到其父,直到到达开始方格。这是想要的路径。
注:在早期版本的文章中,有人建议,当目标方格(或节点)已经添加到开启列表,而不是关闭的列表,你就可以停下来。这样做会更快,它几乎总是会给你的最短路径,但并非总是如此。有些情况下,当从第二移动到最后一个节点到最后的(目标)节点的运动成本可能有明显变化时,这样做可能产生影响--例如,如果河流在两个节点之间交叉的情况下。
■小“愤”
请原谅题外话,但值得指出的是,当在网上阅读的A *路径搜索,并在各类论坛上的各种讨论时,偶尔会看到有人提到某些代码不是A *。对于A *使用方法,需要包含上面讨论到的元素 -- 特别是开放列表和关闭列表和路径采用F值,G值和H值。有很多其他的路径搜索算法,通常被认为是最好的方法但不是A *。在本文的末尾有布莱恩斯托特的讨论,包括他们的很多一些利弊引用的文章。有时替代品在某些情况下更好,但你应该明白你是正在进入的。(译者注:别被误导熏心)好了,爽了。回到话题。
■实施上的注意事项
现在您了解了基本的方法,当你编写自己的程序时,有一些额外的事情要考虑。下面给出我用C ++和Blitz Basic编写的程序,用其他语言也同样有效。
1.其他单元(防止碰撞):如果你碰巧仔细看我的演示代码,你会注意到它完全忽略了屏幕上的其他单元。这些单元彼此穿过。根据游戏,这可能是可以的,可能是不可以的。如果在路径搜索算法里想考虑其他单元,并让它们彼此走动,我建议你只考虑停止的单元或路径搜索附近计算路径的单元,用不能走来处理他们的当前位置。对于正在移动的相邻单元,可以通过处罚沿着各自路径的节点阻止碰撞,从而促使路径寻找单元找到替代路径。(在#2有更多的描述)。
如果选择考虑正在移动的单元和路径寻找上不相邻的其他单元,将需要开发一种方法来预测他们会在任何给定的时间点,使他们能够得到适当的回避。否则,你很可能会有用曲折来避免什么也没有的其他单元的奇怪的路径。
你还会,当然,需要开发一些碰撞检测代码,因为无论多么好的路径在它的计算时,事情可能随时间而改变。当发生碰撞时的单位必须要么计算新的路径,或者如果其他单元是移动且不是一个迎面碰撞,在当前路径继续之前等待其他单位通过。
这些技巧可能是足以让你开始。如果您想了解更多,这里有一些你可能会发现有用的链接:
角色的转向行为:
http://www.red3d.com/cwr/steer/
克雷格雷诺的方向盘上的工作是从路径搜索有点不同,但它可以与路径搜索集成,以做出更完整的移动和防撞系统。
计算机游戏中的长短转向:
http://ducati.doc.ntu.ac.uk/uksim/uksim%2704/Papers/Simon%20Tomlinson-%2004-20/paper04-20%20CR.pdf
在转向和路径搜索的一个有趣的调查。这是一个PDF文件。
协调机组运行:
http://www.gamasutra.com/features/game_design/19990122/movement_01.htm
由帝国时代的设计师戴夫·乍的两部分组成的第一系列在形成和基于组的运动。
实现协调运动:
http://www.gamasutra.com/view/feature/3314/implementing_coordinated_movement.php
戴夫·乍的两部分组成的第二系列。
2.多样的地形成本:在本教程和我附随的程序中,地形仅仅是两件事情之一 – 能走和不能走。但是,如果虽然有能走的地形,但是需要更高的移动成本?沼泽,丘陵,地下城的楼梯,等等 - 这些都是能走的地形的例子,但成本比平坦的开阔地要高。类似地,道路可能具有较低的运行成本比周围的地形。
这个问题很容易通过任何给定节点的G值加地形成本的计算处理来实现。简单地添加一个额外的成本到这样的节点。A *路径搜索算法是用来寻找最低成本路径,应该很容易地处理这个问题。在我描述的简单的例子中,当地形只有能走和不能走,A *将查找最短,最直接的路径。但是,在可变成本的地形环境中,以最少的成本路径可能行走了较长的距离 - 就像绕过的沼泽道路,而不是直接通过它。
一个有趣的附加考虑是专业人士称之为“影响映射” 。正如上述的可变成本的地形,你可以创建一个额外的积分系统,并将其应用到AI的路径。试想一下,你有一些穿过山区的单元的地图。每当计算机发送通某人通过路径时,它被重击。如果你愿意,你可以创建一个发生大量屠杀来处罚节点的影响地图。这会教电脑偏袒安全的路径,并帮助它避免愚蠢的情况下,它通过一个特定的路径不断派遣军队,只是因为它是短(但更危险)。
另一种可能的用法是惩罚沿着路径附近移动中的节点。A *的其中一个缺点是,当一组的单元全都尝试找到的类似位置的路径,通常有一个显著程度的重叠,与一个或多个单元试图利用相同或类似的路线到它们的目的地。添加一个已经被其他单元“叫停”点球节点将有助于确保一定程度的分离,并减少碰撞。不要把这样的节点视为不能走,然而因为你还愿意多个单元能够依次挤过狭窄的通道,如果有必要的。此外,你应该只处罚路径搜索附近的单元,不是所有的路径,否则你会得到避免单元的单元的奇怪躲避行为,都远不及他们当时的路径。此外,你应该只惩罚节点沿着一条当前或未来的路径的一部分,而不是已经访问过并留下以前的路径节点。
3.处理未探索区域:你曾经玩过一款PC游戏,计算机总是准确的知道路该如何走,即使地图还没有被探索?根据不同的游戏,总得到那样好的路径搜索是不现实的。幸运的是,这是很容易处理的问题。
答案是创建一个独立的“已知的通过性”数组为每个各种各样的玩家以及电脑对手的(每个玩家,不是每一个单元 - 那将需要大量的计算机内存)。每个数组将包含有关该玩家已探索区域的信息,与地图的其他部分直到证明假设为是可行的。使用这种方法,单元将漫步死角,使类似的错误选择,直到他们发现周围的路。一旦地图被探索,然而,路径搜索会正常工作。
4.更平滑的路径:虽然A *会自动给出最短,成本最低的路径,它不会自动给出看起来最平滑的路径。看一看例子(图7)计算的最终路径。在该路径中,第一个步骤之后是到开始方格的右侧。如果第一步是从起点的正下方,我们的路径不更更平滑了些吗?
有几种方法来解决这个问题。当你正在计算路径,你可以处罚那里些方向有变化的节点,增加了处罚他们的G值扣分。或者,你可以在计算后浏览你的路径,寻找在选择相邻节点的地方会给你一个看起来更好的路径。欲了解更多关于整个问题,看看向更加逼真路径搜索(Toward More Realistic Pathfinding),一个(免费的,但需要注册)在Gamasutra.com上Macro Pinter的文章。
5.非方格搜索区域:在我们的例子中,我们使用了一个简单的二维方格布局。你并不需要使用这种方法。你可以使用不规则的形状区域。想想风险的棋盘游戏,以及在游戏中的国家。你可以设计一个路径搜索方案给像那样的游戏。要做到这一点,你需要创建一个表,用于存储毗邻的国家,并与移动从一个国家到下一个相关的G值。你还需要拿出用于估计H.其他一切会被处理一样在上面的例子中的方法。而不是使用相邻的方格,在增加新的项目到开启列表时在表中你会简单地查找到相邻国家。
同样,你可以为一个固定的地形图创建路径的路标系统。路标通常走过的路径上的点,也许在一个地牢的道路或关键的隧道上。作为游戏设计者,你能预先指定这些路点。两个路标会被认为是“相邻”彼此是否有它们之间的直线路径上没有障碍。由于在风险的例子,您将节省在某种类型的查找表这个邻接信息,并用它生成新的开启列表项目时。那么你会记录相关的G值(可能通过使用节点间的直线距离)和H成本(可能使用从节点到目标的直线距离)。一切将继续如常。
阿米特·帕特尔还写了一个简短的文章钻研一些替代品。对于使用非方形的搜索区域上等距RPG地图搜索的另一个例子,看看我的文章两层的A *路径搜索(http://www.policyalmanac.org/games/twoTiered.htm)。
6.一些超速提示:当你开发自己的A *程序,或者改编我写的,你最终会发现路径搜索使用你的CPU时间大幅大块,特别是如果你对相当大数量的路径搜索单元的板和一个相当大的地图。如果你在网上读过的东西了,你会发现,这是真实的,即使设计像星际争霸或帝国时代游戏的专业人士。如果你看到的东西开始放缓,由于路径搜索,这里有一些想法,可能会加快速度:
■考虑一个小地图或更少的单位。
永远不要做一次几个单元的路径搜索。相反,把它们放在一个队列,它们分布在几个游戏循环。如果你的游戏在运行时,比如说,每秒40个周期运行,没有人会注意到。但他们会发现,当在同一时间一群单元都在计算路径时游戏似乎在一段时间放慢每一次。
请考虑使用更大的方格(或者任何你正在使用的形状)为您的地图。这减少了搜索以找到的路径的节点的总数。如果你有雄心,可以设计出了用于在不同的情况下,这取决于路径的长度的两个或更多个路径搜索系统。这是专业人士做的,使用大面积的长路径,然后切换到更精细的使用较小的方格/地区搜索,当你接近目标。如果你有兴趣在这个概念,看看我的文章两个层次的A *路径搜索。
对于更长的路径,考虑修订是硬连接到游戏预先计算好的路径。
考虑预处理地图找出哪些领域是从地图的其余部分无法访问。我把这些领域的“孤岛”。在现实中,他们可以是岛屿或者其他任何地区,是另有围墙关闭,无法访问。其中A *的缺点之一是,如果你告诉它来寻找路径等方面,它会搜索整个地图,停车,只有当每平方访问/节点已通过打开和关闭名单处理。这会浪费大量的CPU时间。它可以通过预先确定哪些地区是不可访问(通过洪水填充或类似的程序),记录在某种类型的阵列信息,然后在开始路径搜索前检查它来预防。
在拥挤的,迷宫似的环境中,考虑节点标记不随地导致的死角。这些区域可以手动预先指定的地图编辑器,或者如果你有雄心的,你可以开发一个算法,自动识别等领域。在给定的死胡同区域节点的任何集合可以赋予一个唯一的识别号码。然后路径搜索时,只停下来考虑一个死胡同区域节点,如果起始位置或目的地恰好是在特定的死胡同区问题,你可以放心地忽略所有的死角。
7.维护开启列表:这实际上是A *路径搜索算法中最耗费时间的元素之一。您可以访问开启列表时,都需要找到具有最小F值的方格。有几种方法可以做到这一点。根据需要,你可以保存路径项目,每次当你需要找到最小F值的方格时,简单的遍历整个列表。这是简单的,但对于长路径很慢。这可以通过维护一个排序的列表,每次需要最小F-成本方形时间只需抓住了第一个项目从名单得到改善。当我写我的程序,这是我用第一种方法。
这将工作得相当好为小地图,但它不是最快答案。严重的A *程序员谁想要真正的速度使用一种叫做二进制堆,这是我在我的代码中使用。在我的经验,这种方法将是至少2-3倍的速度在大多数情况下,并且在几何形状更快(快10+次)上较长的路径。如果你主动去寻找更多关于二叉堆,看看我的文章,在A *路径搜索使用二进制堆(http://www.policyalmanac.org/games/binaryHeaps.htm)。
另一种可能的瓶颈是你的方式明确和维护路径搜索调用之间的数据结构。我个人更喜欢存储所有阵列。虽然节点可以生成,记录并保存在一个动态的,面向对象的方式,我发现,创建和删除这些对象所需的时间量增加了额外的开销,不必要的水平会减慢速度。如果你使用数组,不过,你需要调用之间干净的东西了。你会想在这种情况下的最后一件事就是花零时间做完一切了在调用路径搜索后,特别是如果你有一个大的地图。
我避免这种开销通过创建一个二维数组称为whichList(X,Y),其指定在每个节点上我的地图作为任一开启列表或关闭列表上。路径搜索的尝试之后,我不归零数组。相反,我在每一个路径搜索呼叫复位onClosedList和onOpenList的价值观,每个路径尝试寻找类似+5什么都递增。通过这种方式,算法可以放心地忽略垃圾从以前的路径搜索的尝试遗留任何数据。我也喜欢存放F,G和H阵列的成本值。在这种情况下,我只是写在任何预先存在的价值和不打扰清除阵列时,我做的。
在多个阵列存储数据占用更多的内存,虽然如此,有一个权衡。最终,你应该使用什么方法,你是最舒服的。
8. Dijkstra的算法:当A *通常被认为是最好的路径搜索算法(见上面的小愤),存在至少一个其它的算法有其用途 - Dijkstra算法。 Dijkstra的是基本相同的A *,除了没有启发式(H始终为0)。因为它没有启发式,它通过在每一个方向同样扩大了搜索。正如你可能想象的,因为这Dijkstra算法通常是结束了探索一个更大的区域之前目标被发现。这通常使得它比A *慢。
那么,为什么使用它?有时候,我们不知道我们的目标位置是。假设你有一个需要去获得某种资源的一些资源收集装置。它可能知道几个资源区域,但它希望去最近的一个。在这里,Dijkstra的比A *更好,因为我们不知道哪一个是最接近的。我们唯一的选择是重复使用A *查找到每一个的距离,然后选择这条道路。可能有无数类似的情况,我们知道那种位置,我们可能会寻找的,想找到最近的一个,但不知道它在哪里或哪一个可能是最接近的。
■延伸阅读
好了,现在你已具备了基础知识和一些先进的概念。在这一点上,我建议你到涉水我的源代码。该软件包里有两个版本,一个是C ++的,一个是Blitz Basic的。两个版本都有大量注释,相对来说应该是相当容易跟上理解。这里是链接。
http://www.blitzcoder.com/cgi-bin/showcase/showcase_showentry.pl?id=turtle177604062002002208&comments=no"
(译者注:这个链接现在已经不能用了,请用:http://www.policyalmanac.org/games/AStar.zip)
演示代码:A *探路者(2维)1.9版
如果您没有接触到C ++或Blitz Basic,两个小的EXE文件可以在C ++版本中找到。Blitz Basic版本可以通过在Blitz Basic网站下载Blitz Basic 3D(不加Blitz Plus)的免费试用版本来运行。
你也应该考虑通过以下网页阅读。如果你已经阅读本教程,现在他们要容易明白得多。
Amit的A *页:
http://www-cs-students.stanford.edu/~amitp/gameprog.html#Paths
这是一个由阿米特帕特尔写的有非常广泛引用的页面,如果你还没有看过本文,就看它可能有点混乱。非常值得一试。尤其是Amit自己的想法的话题。
智能移动:智能路径查找:
http://www.gamasutra.com/view/feature/3215/myths_and_facts_in_avoiding_.php
本文由布赖恩·斯托特在需要注册阅读的Gamasutra.com发布的。注册是免费的,很值得去看看,更不用说那些可用的资源。由布莱恩用Delphi编写的帮助我学习A *程序,它是我的A *程序背后的灵感。它也介绍了一些A *替代品。
地形分析:
http://www.gamasutra.com/features/gdcarchive/2000/pottinger.doc
这是一种先进的,且很有趣,文章由涉猎全面的工作室的专家Dave Pottinger撰写。这家伙协调过帝国时代和世纪国王的开发。不要指望在这里明白了一切,但它是一个有趣的文章,可能会给你一些自己的想法。它包括Mipmapping(译者注:三维计算机图形的贴图渲染中有一个常用的技术),影响映射,还有其它一些高级AI /路径搜索的概念进行了。
其他一些关于路径搜索的网站值得一试(译者注:基本已不能访问):
aiGuru: Pathfinding
Game AI Resource:Pathfinding
GameDev.net:Pathfinding
好了,就是这样。我很想到你使用这些概念编写程序。我可以通过邮箱收到。
在置笔之前,祝你好运!