您的位置:首页 > 编程语言

“AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (中)

2012-04-08 16:27 260 查看
上一部分提到了节点(Node),代价(Cost),估价公式等基本概念,有了这些知识铺垫
就可以正式开启寻路之旅了!



如上图,这是一个5行8列的网格,黄色节点为起点,红色节点为终点,黑色节点为障碍物(节点)。

寻路过程可以这样考虑:

1、先以起点为中心,向周边扩张一圈,同时计算出周边节点(最多有8个)的单步代价g(即从中心点移动到相邻格子的代价:水平或垂直为1,对角为1.4);然后再计算周边每个节点到终点的估算代价h(利用上一部分最后讲到的估算公式),从而得出周围每个节点的总代价 f = g+h

2、同时准备二个数组,一个称为开放列表(open),一个称为封闭列表(closed),把周边节点放入open数组当中,然后利用数组排序对周边节点的f值从小到大排序,从而找出最小代价的节点----代价最小,意味着应该向这个节点移动。然后再把本轮计算中的中心点放入close数组(也就是意味着该既然找到了下一步,这一步就不用再考虑了)

3、把第2步中得到的代价最小的节点做为中心点,同时从open数组中删除该节点(因为既然已经找到了正确的一步,这一个节点就不用参与下次的排序了),继续向外扩展一圈,得到新的周边节点,同样计算g值和h值,但有一点要注意:因为现在的中心不是刚才的起点了,所以g值的计算实际由二部分组成,一部分为本次中心节点距离起点的g值(记为g1),一部分为本次中心节点到周围节点的g值(记为g2),最终每个周边节点的g
= g1 + g2;而且还有一个要注意的地方:节点的相对位置可能发生变化,比如在前一轮中某些节点相对于前一轮中心节点处于水平或垂直位置,而在本轮计算中,由于中心点向前移动了一位,所以同样的节点在本轮计算时,可能会变成对中心节点的对角节点(即g2由1变成1.4),反之亦然;h值计算跟以前一样,类似前面的处理,得到最终代价f = g + h,同样:把新的周边节点放入open数组(如果还没有放入的话),对于在第2步中就已经放入open数组或close数组的节点,因为g值有可能发生变化了,所以也要重新比较本次得到的总代价f,跟上次计算得到的总代价f,如果本次计算的f值更小,则以本次计算的f值为准(把上一轮节点的f,h,g值更新掉),然后同样的处理,对open数组进行f值从小到大排序,得到代价最小的新节点,做为下一轮计算的中心。(当然下一轮计算前,同样要把已经找到的代价最小的节点从open数组中删除,同时把本次计算的中心点放入close数组)

4、按照前面的处理,如此这般反复下去,路径会一直不断的延伸下去(当然延伸的过程可能会有曲折或反复),但因为我们一直是在取代价最小的节点,所以总体来讲,肯定是越来越靠近终点。

5、在上面反复处理的过程中,一旦发现本轮计算的中心点就是终点,那么恭喜你,我们走到终点了!

好象看起来比较复杂,但其实想通了,就是不断的计算g,h,f,然后不断的对open数组的f值排序,然后找到最小f的节点,向前不断推进!为了方便测试每轮计算中各周边节点g,h,f,我写了一个粗糙的测试程序(很多参数要手动调整):

View Code?
跑一下,能得到下列输出结果:

x= 1 ,y= 0 ,f= 8.7 ,g= 2.4 ,h= 6.2

x= 1 ,y= 1 ,f= 7.8 ,g= 2.0 ,h= 5.8

x= 1 ,y= 2 ,f= 7.8 ,g= 2.4 ,h= 5.4

x= 2 ,y= 0 ,f= 7.2 ,g= 2.0 ,h= 5.2

x= 2 ,y= 1 ,f= 5.8 ,g= 1.0 ,h= 4.8

x= 2 ,y= 2 ,f= 6.4 ,g= 2.0 ,h= 4.4

x= 3 ,y= 0 ,f= 6.7 ,g= 2.4 ,h= 4.2

x= 3 ,y= 1 ,f= 5.8 ,g= 2.0 ,h= 3.8

x= 3 ,y= 2 ,f= 5.8 ,g= 2.4 ,h= 3.4

ok,还有一个重要问题,按上面的处理,我们就算走到了终点,也得到最后一个中心点(其实就是终点),如何把路径给构建(还原)出来呢?因为我们在处理过程中,并没有其它变量用来保存中间计算的结果(即每次找出的最小代价结节)?另外:就算用一个变量做为中介来保存每轮计算中的最佳节点,前面也提到了,向周边探讨寻路的过程中,完全有可能出现曲折反复的过程,难道最终找到的路径还要往回绕个圈(或打个结)走吗?如果是这样,那就违背了我们上一部分里寻找最佳(最短)路径的初衷。

其实这个问题不难处理:上一部分提到了一个父节点的概念! 在每轮计算过程中,相对于中心点周围的相邻节点而言,中心节点就是其它节点的父节点(也可理解为周边节点全都指向它!)如果每轮计算中找到最小代价节点后,把它的父节点指向为中心节点(也就是上一轮找到的最小代价节点),这样到最后走到终点时,利用父节点指向,从终点反向指向起点就能得到最佳路径。

无图无真相,还是上一张图吧



当然,上图只向前推进了二步,有耐心的同学可以根据前面给出的测试程序把剩下的步骤全部画出来(我自己画了几乎一整天),总体大概要经过13轮计算才能最终走到终点(因为中间有很多轮计算都会出现反复)

注:不知道有人注意到没有,在第一轮计算结束时,周边节点中有二个节点x2y1,x2y2,它们的总代价都是最小值5.8,为什么应该选择x2y1,而非x2y2呢?其实这个取决于循环的顺序,在遍历周边节点时,通常是用二重循环来处理的,如果按先x后y的顺序遍历,open数组排序后,x2y1就会排在第一个,如果是按先y后x的顺序遍历,open数组排序后,x2y2就会排在第一个,所以这个其实无所谓,完全取决于你的循环是怎么写的。(本文中采用的是先x后y的顺序遍历)

好了,该AStar.as类出场了,刚才的分析过程全部封装在里面了:

View Code?
为了方便调试输出信息,我还在Node.as中增加了一个toString方法

View Code?
为了方便测试,又弄了一个类GridView.as,把画格子,高亮显示open数组/closed数组,画路径等操作封装起来了:

View Code?
正式测试:

View Code?
黄色显示的是open列表,灰色显示的是closed列表,在每个节点上点击,可以在把相应节点切换为障碍物节点或普通节点.

当然这里面有一个小bug,不知道您看出来没有,下回再详解可能存在的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐