A*寻路初探

A*(念做A星)算法,它只是描述算法的原理,使你能够在进一步的阅读中理解其余相关的资料
序:搜索区域
假设有人想从A点移动到一墙之隔的B点,以下图,绿色的是起点A,红色是终点B,蓝色方块是中间的墙。
算法

[图1]
你首先注意到,搜索区域被咱们划分红了方形网格。像这样,简化搜索区域,是寻路的第一步。这一方法把搜索区域简化成了一个二维数组。数组的每个元素是网格的一个方块,方块被标记为可经过的和不可经过的。路径被描述为从A到B咱们通过的方块的集合。一旦路径被找到,咱们的人就从一个方格的中心走向另外一个,直到到达目的地。
这些中点被称为“节点”。当你阅读其余的寻路资料时,你将常常会看到人们讨论节点。为何不把他们描述为方格呢?由于有可能你的路径被分割成其余不是方格的结构。他们彻底能够是矩形,六角形,或者其余任意形状。节点可以被放置在形状的任意位置-能够在中心,或者沿着边界,或其余什么地方。咱们使用这种系统,不管如何,由于它是最简单的。
开始搜索
正如咱们处理上图网格的方法,一旦搜索区域被转化为容易处理的节点,下一步就是去引导一次找到最短路径的搜索。在A*寻路算法中,咱们经过从点A开始,检查相邻方格的方式,向外扩展直到找到目标。
咱们作以下操做开始搜索:

   1,从点A开始,而且把它做为待处理点存入一个“开启列表”。开启列表就像一张购物清单。尽管如今列表里只有一个元素,但之后就会多起来。你的路径可能会经过它包含的方格,也可能不会。基本上,这是一个待检查方格的列表。
   2,寻找起点周围全部可到达或者可经过的方格,跳过有墙,水,或其余没法经过地形的方格。也把他们加入开启列表。为全部这些方格保存点A做为“父方格”。当咱们想描述路径的时候,父方格的资料是十分重要的。后面会解释它的具体用途。
   3,从开启列表中删除点A,把它加入到一个“关闭列表”,列表中保存全部不须要再次检查的方格。
在这一点,你应该造成如图的结构。在图中,暗绿色方格是你起始方格的中心。它被用浅蓝色描边,以表示它被加入到关闭列表中了。全部的相邻格如今都在开启列表中,它们被用浅绿色描边。每一个方格都有一个灰色指针反指他们的父方格,也就是开始的方格。
数组

[图2]
接着,咱们选择开启列表中的临近方格,大体重复前面的过程,以下。可是,哪一个方格是咱们要选择的呢?是那个F值最低的。
路径评分
选择路径中通过哪一个方格的关键是下面这个等式:
F = G + H
这里:
    * G = 从起点A,沿着产生的路径,移动到网格上指定方格的移动耗费。
    * H = 从网格上那个方格移动到终点B的预估移动耗费。这常常被称为启发式的,可能会让你有点迷惑。这样叫的缘由是由于它只是个猜想。咱们没办法事先知道路径的长度,由于路上可能存在各类障碍(墙,水,等等)。虽然本文只提供了一种计算H的方法,可是你能够在网上找到不少其余的方法。
咱们的路径是经过反复遍历开启列表而且选择具备最低F值的方格来生成的。文章将对这个过程作更详细的描述。首先,咱们更深刻的看看如何计算这个方程。
正如上面所说,G表示沿路径从起点到当前点的移动耗费。在这个例子里,咱们令水平或者垂直移动的耗费为10,对角线方向耗费为14。咱们取这些值是由于沿对角线的距离是沿水平或垂直移动耗费的的根号2(别怕),或者约1.414倍。为了简化,咱们用10和14近似。比例基本正确,同时咱们避免了求根运算和小数。这不是只由于咱们怕麻烦或者不喜欢数学。使用这样的整数对计算机来讲也更快捷。你不就就会发现,若是你不使用这些简化方法,寻路会变得很慢。
既然咱们在计算沿特定路径通往某个方格的G值,求值的方法就是取它父节点的G值,而后依照它相对父节点是对角线方向或者直角方向(非对角线),分别增长14和10。例子中这个方法的需求会变得更多,由于咱们从起点方格之外获取了不止一个方格。
H值能够用不一样的方法估算。咱们这里使用的方法被称为曼哈顿方法,它计算从当前格到目的格之间水平和垂直的方格的数量总和,忽略对角线方向。而后把结果乘以10。这被成为曼哈顿方法是由于它看起来像计算城市中从一个地方到另一个地方的街区数,在那里你不能沿对角线方向穿过街区。很重要的一点,咱们忽略了一切障碍物。这是对剩余距离的一个估算,而非实际值,这也是这一方法被称为启发式的缘由。想知道更多?你能够在这里找到方程和额外的注解。
F的值是G和H的和。第一步搜索的结果能够在下面的图表中看到。F,G和H的评分被写在每一个方格里。正如在紧挨起始格右侧的方格所表示的,F被打印在左上角,G在左下角,H则在右下角。
ide

[图3]
如今咱们来看看这些方格。写字母的方格里,G = 10。这是由于它只在水平方向偏离起始格一个格距。紧邻起始格的上方,下方和左边的方格的G值都等于10。对角线方向的G值是14。
H值经过求解到红色目标格的曼哈顿距离获得,其中只在水平和垂直方向移动,而且忽略中间的墙。用这种方法,起点右侧紧邻的方格离红色方格有3格距离,H值就是30。这块方格上方的方格有4格距离(记住,只能在水平和垂直方向移动),H值是40。你大体应该知道如何计算其余方格的H值了~。
每一个格子的F值,仍是简单的由G和H相加获得
继续搜索
为了继续搜索,咱们简单的从开启列表中选择F值最低的方格。而后,对选中的方格作以下处理:
   4,把它从开启列表中删除,而后添加到关闭列表中。
   5,检查全部相邻格子。跳过那些已经在关闭列表中的或者不可经过的(有墙,水的地形,或者其余没法经过的地形),把他们添加进开启列表,若是他们还不在里面的话。把选中的方格做为新的方格的父节点。
   6,若是某个相邻格已经在开启列表里了,检查如今的这条路径是否更好。换句话说,检查若是咱们用新的路径到达它的话,G值是否会更低一些。若是不是,那就什么都不作。
      另外一方面,若是新的G值更低,那就把相邻方格的父节点改成目前选中的方格(在上面的图表中,把箭头的方向改成指向这个方格)。最后,从新计算F和G的值。若是这看起来不够清晰,你能够看下面的图示。
好了,让咱们看看它是怎么运做的。咱们最初的9格方格中,在起点被切换到关闭列表中后,还剩8格留在开启列表中。这里面,F值最低的那个是起始格右侧紧邻的格子,它的F值是40。所以咱们选择这一格做为下一个要处理的方格。在紧随的图中,它被用蓝色突出显示。
spa

 

[图4]
首先,咱们把它从开启列表中取出,放入关闭列表(这就是他被蓝色突出显示的缘由)。而后咱们检查相邻的格子。哦,右侧的格子是墙,因此咱们略过。左侧的格子是起始格。它在关闭列表里,因此咱们也跳过它。
其余4格已经在开启列表里了,因而咱们检查G值来断定,若是经过这一格到达那里,路径是否更好。咱们来看选中格子下面的方格。它的G值是14。若是咱们从当前格移动到那里,G值就会等于20(到达当前格的G值是10,移动到上面的格子将使得G值增长10)。由于G值20大于14,因此这不是更好的路径。若是你看图,就能理解。与其经过先水平移动一格,再垂直移动一格,还不如直接沿对角线方向移动一格来得简单。
当咱们对已经存在于开启列表中的4个临近格重复这一过程的时候,咱们发现没有一条路径能够经过使用当前格子获得改善,因此咱们不作任何改变。既然咱们已经检查过了全部邻近格,那么就能够移动到下一格了。
因而咱们检索开启列表,如今里面只有7格了,咱们仍然选择其中F值最低的。有趣的是,此次,有两个格子的数值都是54。咱们如何选择?这并不麻烦。从速度上考虑,选择最后添加进列表的格子会更快捷。这种致使了寻路过程当中,在靠近目标的时候,优先使用新找到的格子的偏好。但这可有可无。(对相同数值的不一样对待,致使不一样版本的A*算法找到等长的不一样路径。)
那咱们就选择起始格右下方的格子,如图。.net

[图5]
此次,当咱们检查相邻格的时候,发现右侧是墙,因而略过。上面一格也被略过。咱们也略过了墙下面的格子。为何呢?由于你不能在不穿越墙角的状况下直接到达那个格子。你的确须要先往下走而后到达那一格,循序渐进的走过那个拐角。(注解:穿越拐角的规则是可选的。它取决于你的节点是如何放置的。)
这样一来,就剩下了其余5格。当前格下面的另外两个格子目前不在开启列表中,因而咱们添加他们,而且把当前格指定为他们的父节点。其他3格,两个已经在开启列表中(起始格,和当前格上方的格子,在表格中蓝色高亮显示),因而咱们略过它们。最后一格,在当前格的左侧,将被检查经过这条路径,G值是否更低。没必要担忧,咱们已经准备好检查开启列表中的下一格了。
咱们重复这个过程,知道目标格被添加进开启列表,就如在下面的图中所看到的指针

[图6]
注意,起始格下方格子的父节点已经和前面不一样的。以前它的G值是28,而且指向右上方的格子。如今它的G值是20,指向它上方的格子。这在寻路过程当中的某处发生,当应用新路径时,G值通过检查变得低了-因而父节点被从新指定,G和F值被从新计算。尽管这一变化在这个例子中并不重要,在不少场合,这种变化会致使寻路结果的巨大变化。
那么,咱们怎么肯定这条路径呢?很简单,从红色的目标格开始,按箭头的方向朝父节点移动。这最终会引导你回到起始格,这就是你的路径!看起来应该像图中那样。从起始格A移动到目标格B只是简单的从每一个格子(节点)的中点沿路径移动到下一个,直到你到达目标点。就这么简单。排序

[图7]
A*方法总结
好,如今你已经看完了整个说明,让咱们把每一步的操做写在一块儿:
   1,把起始格添加到开启列表。
   2,重复以下的工做:
      a) 寻找开启列表中F值最低的格子。咱们称它为当前格。
      b) 把它切换到关闭列表。
      c) 对相邻的8格中的每个?
          * 若是它不可经过或者已经在关闭列表中,略过它。反之以下。
          * 若是它不在开启列表中,把它添加进去。把当前格做为这一格的父节点。记录这一格的F,G,和H值。
          * 若是它已经在开启列表中,用G值为参考检查新的路径是否更好。更低的G值意味着更好的路径。若是是这样,就把这一格的父节点改为当前格,而且从新计算这一格的G和F值。若是你保持你的开启列表按F值排序,改变以后你可能须要从新对开启列表排序。
      d) 中止,当你
          * 把目标格添加进了开启列表,这时候路径被找到,或者
          * 没有找到目标格,开启列表已经空了。这时候,路径不存在。
   3.保存路径。从目标格开始,沿着每一格的父节点移动直到回到起始格。这就是你的路径。get

 

如今你已经明白了基本原理,写你的程序的时候还得考虑一些额外的东西数学

维护开启列表:这是A*寻路算法最重要的组成部分。每次你访问开启列表,你都须要寻找F值最低的方格。有几种不一样的方法实现这一点。你能够把路径元素随意保存,当须要寻找F值最低的元素的时候,遍历开启列表。这很简单,可是太慢了,尤为是对长路径来讲。这能够经过维护一格排好序的列表来改善,每次寻找F值最低的方格只须要选取列表的首元素。当我本身实现的时候,这种方法是个人首选。it

 

英文原文

http://www.gamedev.net/reference/articles/article2003.asp

相关文章
相关标签/搜索