A*寻路算法 教程(二)

继续搜索

为了继续搜索,我们简单的从开放列表中选择具有最小F值的方格,然后对选中的方格进行如下操作:

4.
将其从开放列表中移除,并加到封闭列表中。

5.
检验所有的相邻方格,忽略那些不可通过的或者已经在封闭列表里的方格。如果这个相邻方格不在开放列表中,就把它添加进去。并将当前选定方格设为新添方格的父方格。

6.
如果某个相邻方格已经在开放列表中了(意味着已经探测过,而且已经设置过父方格――译者),就看看有没有到达那个方格的更好的路径。也就是说,如果从当前选中方格到那个方格,会不会使那个方格的G值更小。如果不能,就不进行任何操作。
相反的,如果新路径的G值更小,就将该相邻方格的父方格重设为当前选中方格。(在上图中是改变其指针的方向为指向选中方格。最后,重新计算那个相邻方格的F和G值。如果你看糊涂了,下面会有图解说明。


好啦,咱们来看看具体点的例子。在初始时的9个方块中,当开始方格被加到封闭列表后,开放列表里还剩8个方格。在这八个方格当中,位于起点方格右边的那个方格具有最小的F值40。所以我们选择这个方格作为下一个中心方格。下图中它以高亮的蓝色表示。

A*寻路算法 教程(二)

[图四]
 

首先,我们将选中的方格从开放列表中移除,并加入到封闭列表中(所以用亮蓝色标记)。然后再检验它的相邻节点。那么在它紧邻的右边的方格都是墙,所以不管它们。左边挨着的是起始方格,而起始方格已经在封闭列表中了,所以我们也不管它。

其他四个方格已经在开放列表中,那么我们就要检验一下如果路径经由当前选中方格到那些方格的话会不会更好,当然,是用G值作为参考。来看看选中方格右上角的那一个方格,它当前的G值是14,如果我们经由当前节点再到达那个方格的话,G值会是20(到当前方格的G值是10,然后向上移动一格就再加上10)。为20的G值比14大,因此这样的路径不会更好。你看看图就会容易理解些。显然从起始点沿斜角方向移动到那个方格比先水平移动一格再垂直移动一格更直接。

当我们按如上过程依次检验开放列表中的所有四个方格后,会发现经由当前方格的话不会形成更好的路径,那我们就保持目前的状况不变。现在我们已经处理了所有相邻方格,准备到下一个方格吧。

我们再遍历一下开放列表,目前只有7个方格了。我们挑个F值最小的吧。有趣的是,目前这种情况下,有两个F值为54的方格。那我们怎么选择呢?其实选哪个都没关系,要考虑到速度的话,选你最近加到开放列表中的那一个会更快些。当离目的地越来越近的时候越偏向于选最后发现的方格。实际上这个真的没关系(对待这个的不同造成了两个版本的A*算法得到等长的不同路径)。

那我们选下面的那个好了,就是起始方格右边的,下图所示的那个。

A*寻路算法 教程(二)

[图五]
 

这一次,在我们检验相邻方格的时候发现右边紧挨的那个是墙,就不管它了。上面挨着的那个也同样忽略。还有右边墙下面那个方格我们也不管。为什么呢?因为你不可能切穿墙角直接到达那个格子。实际上你得先向下走然后再通过那个方格。这个过程中是绕着墙角走。(注意:穿过墙角的这个规则是可选的,取决于你的节点是如何放置的。)

那么还剩下其他五个相邻方格。当前方格的下面那两个还不在开放列表中,那我们把它们加进去并且把当前方格作为它们的父方格。其他三个中有两个已经在封闭列表中了(两个已经在图中用亮蓝色标记了,起始方格,上面的方格),所以就不用管了。最后那个,当前方格左边挨着的,要检查一下经由当前节点到那里会不会降低它的G值。结果不行,所以我们又处理完毕了,然后去检验开放列表中的下一个格子。

重复这个过程直到我们把目的方格加入到开放列表中了,那时候看起来会像下图这个样子。

A*寻路算法 教程(二)

[图六]
 

注意到没?起始方格下两格的位置,那里的格子已经和前一张图不一样了。之前它的G值是28并且指向右上方的那个方格。现在它的G值变成了20并且指向了正上方的方格。这个改变是在搜索过程中,它的G值被核查时发现在某个新路径下可以变得更小时发生的。然后它的父方格也被重设并且重新计算了G值和F值。在本例中这个改变看起来好像不是很重要,但是在很多种情况下这种改变会使到达目标的最佳路径变得非常不同。

那么我们怎样来自动得出实际路径的呢?很简单,只要从红色目标方格开始沿着每一个方格的指针方向移动,依次到达它们的父方格,最终肯定会到达起始方格。那就是你的路径!如下图所示。从A方格到B方格的移动就差不多是沿着这个路径从每个方格中心(节点)移动到另一个方格中心,直到抵达终点。简单吧!

A*寻路算法 教程(二)

[图七]
 

A*算法总结

1.
将开始节点放入开放列表(开始节点的F和G值都视为0);

2.
重复一下步骤:

            i.
在开放列表中查找具有最小F值的节点,并把查找到的节点作为当前节点;

           ii.
当前节点从开放列表删除, 加入到封闭列表;

         iii.
当前节点相邻的每一个节点依次执行以下步骤:

1.
如果该相邻节点不可通行或者该相邻节点已经在封闭列表中,则什么操作也不执行,继续检验下一个节点;

2.
如果该相邻节点不在开放列表中,则将该节点添加到开放列表中, 并将该相邻节点的父节点设为当前节点,同时保存该相邻节点的G和F值;

3.
如果该相邻节点在开放列表中, 则判断若经由当前节点到达该相邻节点的G值是否小于原来保存的G值,若小于,则将该相邻节点的父节点设为当前节点,并重新设置该相邻节点的G和F值.

        iv.
循环结束条件:

终点节点被加入到开放列表作为待检验节点时, 表示路径被找到,此时应终止循环;
或者当开放列表为空,表明已无可以添加的新节点,而已检验的节点中没有终点节点则意味着路径无法被找到,此时也结束循环;
3.
终点节点开始沿父节点遍历, 并保存整个遍历到的节点坐标,遍历所得的节点就是最后得到的路径;

你可能感兴趣的:(算法,F#)