寻路(AStar/A星/A*)算法

一提到“A*算法”,可能很多人都有"如雷贯耳"的感觉。用最白话的语言来讲:把游戏中的某个角色放在一个网格环境中,并给定一个目标点和一些障碍物,如何让角色快速“绕过障碍物”找出通往目标点的路径。(如下图)

寻路(AStar/A星/A*)算法 <wbr>(上)

在寻路过程中,角色总是不停从一个格子移动到另一个相邻的格子,如果单纯从距离上讲,移动到与自身斜对角的格子走的距离要长一些,而移动到与自身水平或垂直方面平行的格子,则要近一些。为了描述这种区别,先引入二个概念:

节点(Node):每个格子都可以称为节点。

代价(Cost):描述角色移动到某个节点时所走的距离(或难易程度)。

寻路(AStar/A星/A*)算法 <wbr>(上)

如上图,如果每水平或垂直方向移动相邻一个节点所花的代价记为1,则相邻对角节点的代码为1.4(即2的平方根--勾股定理)

通常寻路过程中的代价用f,g,h来表示

g代表(从指定节点到相邻)节点本身的代价--即上图中的1或1.4

h代表从指定节点到目标节点(根据不同的估价公式--后面会解释估价公式)估算出来的代价。

而 f = g + h 表示节点的总代价,为了方便后面的代码描述,这里把节点封装成一个类Node.as

01 package   
02     public class Node
03     {
04         public var x:int;
05         public var y:int;
06         public var f:Number;
07         public var g:Number;
08         public var h:Number;
09         public var walkable:Boolean=true;//是否可穿越(通常把障碍物节点设置为false)
10         public var parent:Node;
11         public var costMultiplier:Number=1.0;//代价因子
12   
13         public function Node(x:int, y:int)
14         {
15             this.x=x;
16             this.y=y;
17         }
18     }
19 }

注意:这里有二个新的东东walkable和parent。

通常障碍物本身也可以看成是由若干个不可通过的节点所组成,所以walkable实际上是用来标记该节点是否为障碍物(节点)。

另外:在考查从一个节点移动到另一个节点时,总是拿自身节点周围的8个相邻节点来说事儿,相对于周边的节点来讲,自身节点称为它们的父节点(parent).

前面一直在提“网格,网格”,干脆把它也封装成类Grid.as

01 package
02 {
03   
04     public class Grid
05     {
06         private var _startNode:Node;//开始节点
07         private var _endNode:Node;//目标节点
08         private var _nodes:Array;//节点数组
09         private var _numCols:int;//列数
10         private var _numRows:int;//行数
11   
12         public function Grid(numCols:int, numRows:int)
13         {
14             _numCols=numCols;
15             _numRows=numRows;
16             _nodes=new Array();
17             for (var i:int=0; i < _numCols; i++)
18             {
19                 _nodes[i]=new Array();
20                 for (var j:int=0; j < _numRows; j++)
21                 {
22                     _nodes[i][j]=new Node(i, j);
23                 }
24             }
25         }
26   
27         public function getNode(x:int, y:int):Node
28         {
29             return _nodes[x][y] as Node;
30         }
31   
32   
33         public function setEndNode(x:int, y:int):void
34         {
35             _endNode=_nodes[x][y] as Node;
36         }
37   
38   
39         public function setStartNode(x:int, y:int):void
40         {
41             _startNode=_nodes[x][y] as Node;
42         }
43   
44   
45         public function setWalkable(x:int, y:int, value:Boolean):void
46         {
47             _nodes[x][y].walkable=value;
48         }
49   
50   
51         public function get endNode():Node
52         {
53             return _endNode;
54         }
55   
56   
57         public function get numCols():int
58         {
59             return _numCols;
60         }
61   
62   
63         public function get numRows():int
64         {
65             return _numRows;
66         }
67   
68   
69         public function get startNode():Node
70         {
71             return _startNode;
72         }
73     }
74 }

然而,在寻路的过程中“条条道路通罗马”,路径通常不止一条,只不过所花的代价不同而已

寻路(AStar/A星/A*)算法 <wbr>(上)

如上图,如果按照黄色路径走,所花的总代价是14,而按照粉红色路径走,所花的总代价是16,所以我们要做的事情,就是要尽最大努力找一条代价最小的路径。

但是,“好事总多磨”,即使是代价相同的最佳路径,也有可能出现不同的走法:

 寻路(AStar/A星/A*)算法 <wbr>(上)

上图中三种不同的走法,总代价都是4.8,就上图而言,最佳路径(最小代价)用肉眼就能很快找出来,但是用代码如何估算起点与终点之间的代价呢?

01 //曼哈顿估价法
02 private function manhattan(node:Node):Number
03 {
04     return Math.abs(node.x - _endNode.x) * _straightCost + Math.abs(node.y + _endNode.y) * _straightCost;
05 }
06   
07 //几何估价法
08 private function euclidian(node:Node):Number
09 {
10     var dx:Number=node.x - _endNode.x;
11     var dy:Number=node.y - _endNode.y;
12     return Math.sqrt(dx * dx + dy * dy) * _straightCost;
13 }
14   
15 //对角线估价法
16 private function diagonal(node:Node):Number
17 {
18     var dx:Number=Math.abs(node.x - _endNode.x);
19     var dy:Number=Math.abs(node.y - _endNode.y);
20     var diag:Number=Math.min(dx, dy);
21     var straight:Number=dx + dy;
22     return _diagCost * diag + _straightCost * (straight - 2 * diag);
23 }

上面的代码给出了三种基本的估价算法(也称估价公式),其算法示意图如下:

寻路(AStar/A星/A*)算法 <wbr>(上)

如上图,对于“曼哈顿算法”最贴切的描述莫过于孙燕姿唱过的那首成名曲“直来直往”,笔直的走,然后转个弯,再笔直的继续。

“几何算法”的最好解释就是“勾股定理”,算出起点与终点之间的直线距离,然后乘上代价因子。

“对角算法”综合了以上二种算法,先按对角线走,一直走到与终点水平或垂直平行后,再笔直的走。

我们可以针对刚才的情况做下测试:

01 package
02 {
03     import flash.display.Sprite;
04   
05     public class GridTest extends Sprite
06     {
07         private var _endNode:Node;
08         private var _startNode:Node;
09         private var _straightCost:Number=1.0;
10         private var _diagCost:Number = 1.4;
11   
12   
13         public function GridTest()
14         {
15             var g:Grid=new Grid(5, 5);
16             g.setStartNode(0, 3);
17             g.setEndNode(4, 1);
18               
19             _endNode = g.endNode;
20             _startNode = g.startNode;
21               
22             var c1:Number = manhattan(_startNode);//8 
23             var c2:Number = euclidian(_startNode);//4.47213595499958
24             var c3:Number = diagonal(_startNode);//4.8
25               
26             trace(c1,c2,c3);
27         }
28   
29         //曼哈顿估价法
30         private function manhattan(node:Node):Number
31         {
32             return Math.abs(node.x - _endNode.x) * _straightCost + Math.abs(node.y - _endNode.y) * _straightCost;
33         }
34   
35         //几何估价法
36         private function euclidian(node:Node):Number
37         {
38             var dx:Number=node.x - _endNode.x;
39             var dy:Number=node.y - _endNode.y;
40             return Math.sqrt(dx * dx + dy * dy) * _straightCost;
41         }
42   
43         //对角线估价法
44         private function diagonal(node:Node):Number
45         {
46             var dx:Number=Math.abs(node.x - _endNode.x);
47             var dy:Number=Math.abs(node.y - _endNode.y);
48             var diag:Number=Math.min(dx, dy);
49             var straight:Number=dx + dy;
50             return _diagCost * diag + _straightCost * (straight - 2 * diag);
51         }
52     }
53 }

从输出结果可以看到“对角线估价法”跟肉眼预测的实际结果完全一致,总代价为4.8,以后默认情况下就用它了,不过这里提醒一下:这种代价是大概估计出来的,没有考虑到障碍物的因素,并非寻路过程中的实际代价,所以这也是“价计算公式”而非“代价计算公式”得名的由来。



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

寻路(AStar/A星/A*)算法 <wbr>(中)

如上图,这是一个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,我写了一个粗糙的测试程序(很多参数要手动调整):

 
01 package
02 {
03     import flash.display.Sprite;
04   
05   
06     public class GridTest extends Sprite
07     {
08         private var _endNode:Node;//终点
09         private var _startNode:Node;//起点    
10         private var _centerNode:Node;//本次计算的中心点
11         private var _straightCost:Number=1.0;
12         private var _diagCost:Number=Math.SQRT2;
13   
14         public function GridTest()
15         {
16             var g:Grid=new Grid(8, 5);//生成一个5行8列的网格
17             g.setStartNode(1, 1);//设置起点
18             g.setEndNode(6, 3); //设置终点
19   
20             _endNode=g.endNode;
21             _startNode=g.startNode;         
22             _centerNode = g.getNode(2,1);//大家可以调整此中心点位置,以观察周边节点的f,h,g值
23               
24             //这里借用了ascb第三方库,对数字进行格式化,取小数点后一个小数(如果大家没有ascb官方库,也可以直接输出数字)
25             var fmr:NumberFormat = new NumberFormat();
26             fmr.mask = "#.0";
27   
28             var _g1:Number = diagonal(_centerNode,_startNode);//中心点相对起点的g值          
29               
30             //这里的x即为中心点周围节点的x范围(可手动调整)
31             for (var x:uint=1; x <= 3; x++)
32             {
33                 //这里的y即为中心点周围节点的y范围(可手动调整)
34                 for (var y:uint=0; y <= 2; y++)
35                 {
36                     var test:Node=g.getNode(x, y);              
37                     var _h:Number=diagonal(test,_endNode);
38                     var _g2:Number = diagonal(test,_centerNode);
39                     var _g:Number=_g1 + _g2;//计算g值
40                     var _f:Number=_h+_g;
41                     trace("x=", test.x, ",y=", test.y, ",f=", fmr.format(_f), ",g=", fmr.format(_g), ",h=", fmr.format(_h));
42                 }
43             }
44         }
45   
46         //对角线估价法
47         private function diagonal(node:Node,target:Node):Number
48         {
49             var dx:Number=Math.abs(node.x - target.x);
50             var dy:Number=Math.abs(node.y - target.y);
51             var diag:Number=Math.min(dx, dy);
52             var straight:Number=dx + dy;
53             return _diagCost * diag + _straightCost * (straight - 2 * diag);
54               
55     }
56 }

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

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

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

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

寻路(AStar/A星/A*)算法 <wbr>(中)

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

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

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

 
001 package
002 {
003     import flash.display.Sprite;
004   
005     public class AStar extends Sprite
006     {
007         private var _open:Array;//开放列表
008         private var _closed:Array;//封闭列表
009         private var _grid:Grid;
010         private var _endNode:Node;//终点
011         private var _startNode:Node;//起点
012         private var _path:Array;//最终的路径节点
013         // private var _heuristic:Function = manhattan; 
014         // private var _heuristic:Function = euclidian; 
015         private var _heuristic:Function=diagonal; //估计公式
016         private var _straightCost:Number=1.0; //直线代价        
017         private var _diagCost:Number=Math.SQRT2; //对角线代价        
018   
019         public function AStar()
020         {
021   
022         }
023   
024         //判断节点是否开放列表
025         private function isOpen(node:Node):Boolean
026         {
027             for (var i:int=0; i < _open.length; i++)
028             {
029                 if (_open[i] == node)
030                 {
031                     return true;
032                 }
033             }
034             return false;
035         }
036           
037         //判断节点是否封闭列表
038         private function isClosed(node:Node):Boolean
039         {
040             for (var i:int=0; i < _closed.length; i++)
041             {
042                 if (_closed[i] == node)
043                 {
044                     return true;
045                 }
046             }
047             return false;
048         }
049   
050         //对指定的网络寻找路径
051         public function findPath(grid:Grid):Boolean
052         {
053             _grid=grid;
054             _open=new Array();
055             _closed=new Array();
056             _startNode=_grid.startNode;
057             _endNode=_grid.endNode;
058             _startNode.g=0;
059             _startNode.h=_heuristic(_startNode);
060             _startNode.f=_startNode.g + _startNode.h;
061             return search();
062         }
063           
064         //计算周围节点代价的关键处理函数
065         public function search():Boolean
066         {
067             var _t:uint =1;
068             var node:Node=_startNode;
069             //如果当前节点不是终点
070             while (node != _endNode)
071             {
072                 //找出相邻节点的x,y范围
073                 var startX:int=Math.max(0, node.x - 1);
074                 var endX:int=Math.min(_grid.numCols - 1, node.x + 1);
075                 var startY:int=Math.max(0, node.y - 1);
076                 var endY:int=Math.min(_grid.numRows - 1, node.y + 1);               
077                   
078                 //循环处理所有相邻节点
079                 for (var i:int=startX; i <= endX; i++)
080                 {
081                     for (var j:int=startY; j <= endY; j++)
082                     {
083                         var test:Node=_grid.getNode(i, j);                      
084                         //如果是当前节点,或者是不可通过的,则跳过
085                         if (test == node || !test.walkable)
086                         {
087                             continue;
088                         }
089                           
090                         var cost:Number=_straightCost;                      
091                         //如果是对象线,则使用对角代价
092                         if (!((node.x == test.x) || (node.y == test.y)))
093                         {
094                             cost=_diagCost;
095                                               
096                           
097                         //计算test节点的总代价                      
098                         var g:Number=node.g + cost * test.costMultiplier;
099                         var h:Number=_heuristic(test);                      
100                         var f:Number=g + h;                 
101                           
102                           
103                         //如果该点在open或close列表中
104                         if (isOpen(test) || isClosed(test))
105                         {
106                             //如果本次计算的代价更小,则以本次计算为准
107                             if (f<test.f)
108                             {
109                                 trace("\n第",_t,"轮,有节点重新指向,x=",i,",y=",j,",g=",g,",h=",h,",f=",f,",test=",test.toString());                              
110                                 test.f=f;
111                                 test.g=g;
112                                 test.h=h;
113                                 test.parent=node;//重新指定该点的父节点为本轮计算中心点
114                             }
115                         }
116                         else//如果还不在open列表中,则除了更新代价以及设置父节点,还要加入open数组
117                         {
118                             test.f=f;
119                             test.g=g;
120                             test.h=h;
121                             test.parent=node;
122                             _open.push(test);
123                         }
124                     }
125                               
126                 _closed.push(node);//把处理过的本轮中心节点加入close节点               
127                   
128                 //辅助调试,输出open数组中都有哪些节点
129                 for(i=0;i<_open.length;i++){
130                   trace(_open[i].toString());   
131                 }
132                   
133                 if (_open.length == 0)
134                 {
135                     trace("没找到最佳节点,无路可走!");
136                     return false
137                 }
138                 _open.sortOn("f", Array.NUMERIC);//按总代价从小到大排序
139                 node=_open.shift() as Node;//从open数组中删除代价最小的结节,同时把该节点赋值为node,做为下次的中心点
140                 trace("第",_t,"轮取出的最佳节点为:",node.toString());
141                 _t++;
142             }
143             //循环结束后,构建路径
144             buildPath();
145             return true;
146         }
147   
148         //根据父节点指向,从终点反向连接到起点
149         private function buildPath():void
150         {
151             _path=new Array();
152             var node:Node=_endNode;
153             _path.push(node);
154             while (node != _startNode)
155             {
156                 node=node.parent;
157                 _path.unshift(node);
158             }
159         }
160   
161         //曼哈顿估价法
162         private function manhattan(node:Node):Number
163         {
164             return Math.abs(node.x - _endNode.x) * _straightCost + Math.abs(node.y - _endNode.y) * _straightCost;
165         }
166   
167         //几何估价法
168         private function euclidian(node:Node):Number
169         {
170             var dx:Number=node.x - _endNode.x;
171             var dy:Number=node.y - _endNode.y;
172             return Math.sqrt(dx * dx + dy * dy) * _straightCost;
173         }
174   
175         //对角线估价法
176         private function diagonal(node:Node):Number
177         {
178             var dx:Number=Math.abs(node.x - _endNode.x);
179             var dy:Number=Math.abs(node.y - _endNode.y);
180             var diag:Number=Math.min(dx, dy);
181             var straight:Number=dx + dy;
182             return _diagCost * diag + _straightCost * (straight - 2 * diag);
183         }
184   
185         //返回所有被计算过的节点(辅助函数)
186         public function get visited():Array
187         {
188             return _closed.concat(_open);
189         }
190           
191         //返回open数组
192         public function get openArray():Array{
193             return this._open;
194         }
195           
196         //返回close数组
197         public function get closedArray():Array{
198             return this._closed;
199         }
200           
201         public function get path():Array
202         {
203             return _path;
204         }
205     }
206 }

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

 
1 //方便调试输出用的toString函数
2 public function toString():String 
3     var fmr:NumberFormat = new NumberFormat();
4     fmr.mask = "#.0";
5     return "x=" + this.x.toString() + ",y=" + this.y.toString() + ",g=" + fmr.format(this.g) + ",h=" + fmr.format(this.h) + ",f=" + fmr.format(this.f);
6 }

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

 
001 package
002 {
003     import flash.display.Sprite;
004     import flash.events.MouseEvent;
005   
006     public class GridView extends Sprite
007     {
008         private var _cellSize:int=40;
009         private var _grid:Grid;
010   
011         //构造函数
012         public function GridView(grid:Grid)
013         {
014             _grid=grid;
015             drawGrid();
016             findPath();
017             addEventListener(MouseEvent.CLICK, onGridClick);
018         }
019   
020         //画格子
021         public function drawGrid():void
022         {
023             graphics.clear();
024             for (var i:int=0; i < _grid.numCols; i++)
025             {
026                 for (var j:int=0; j < _grid.numRows; j++)
027                 {
028                     var node:Node=_grid.getNode(i, j);
029                     graphics.lineStyle(0);
030                     graphics.beginFill(getColor(node));
031                     graphics.drawRect(i * _cellSize, j * _cellSize, _cellSize, _cellSize);
032                 }
033             }
034         }
035   
036         //取得节点颜色
037         private function getColor(node:Node):uint
038         {
039             if (!node.walkable)
040             {
041                 return 0;
042             }
043             if (node == _grid.startNode)
044             {
045                 return 0xffff00;
046             }
047             if (node == _grid.endNode)
048             {
049                 return 0xff0000;
050             }
051             return 0xffffff;
052         }
053   
054         //网格点击事件
055         private function onGridClick(event:MouseEvent):void
056         {
057             var xpos:int=Math.floor(event.localX / _cellSize);
058             var ypos:int=Math.floor(event.localY / _cellSize);
059             _grid.setWalkable(xpos, ypos, !_grid.getNode(xpos, ypos).walkable);
060             drawGrid();
061             findPath();
062         }
063   
064         //寻找路径
065         private function findPath():void
066         {
067             var astar:AStar=new AStar;
068             if (astar.findPath(_grid))
069             {
070                 showVisited(astar);
071                 showPath(astar);
072             }
073         }
074   
075         //显示open列表与close列表
076         private function showVisited(astar:AStar):void
077         {
078               
079               
080             var opened:Array=astar.openArray;
081             for (var i:int=0; i < opened.length; i++)
082             {
083                 var node:Node = opened[i] as Node;
084                   
085                 graphics.beginFill(0xcccccc);
086                 if (node==_grid.startNode){
087                     graphics.beginFill(0xffff00);
088                 }
089                   
090                 graphics.drawRect(opened[i].x * _cellSize, opened[i].y * _cellSize, _cellSize, _cellSize);
091                 graphics.endFill();
092             }
093               
094             var closed:Array=astar.closedArray;
095             for (i=0; i < closed.length; i++)
096             {
097                 node = opened[i] as Node;
098                   
099                 graphics.beginFill(0xffff00);               
100                   
101                 graphics.drawRect(closed[i].x * _cellSize, closed[i].y * _cellSize, _cellSize, _cellSize);
102                 graphics.endFill();
103             }
104         }
105   
106         //显示路径
107         private function showPath(astar:AStar):void
108         {
109             var path:Array=astar.path;
110             for (var i:int=0; i < path.length; i++)
111             {
112                 graphics.lineStyle(0);
113                 graphics.beginFill(0);
114                 graphics.drawCircle(path[i].x * _cellSize + _cellSize / 2, path[i].y * _cellSize + _cellSize /2, _cellSize / 3);
115             }
116         }
117     }
118 }

正式测试:

 
01 package
02 {
03     import flash.display.Sprite;
04     import flash.display.StageAlign;
05     import flash.display.StageScaleMode;
06     import flash.events.MouseEvent;
07   
08     [SWF(backgroundColor=0xffffff,width=360,height=240)]
09     public class Pathfinding extends Sprite
10     {
11         private var _grid:Grid;
12         private var _gridView:GridView;
13   
14         public function Pathfinding()
15         {
16             stage.align=StageAlign.TOP_LEFT;
17             stage.scaleMode=StageScaleMode.NO_SCALE;
18             _grid=new Grid(8, 5);
19             _grid.setStartNode(1, 1);
20             _grid.setEndNode(6, 3);
21               
22             //设置障碍物
23             _grid.getNode(4,0).walkable = false;
24             _grid.getNode(4,1).walkable = false;
25             _grid.getNode(4,2).walkable = false;
26             _grid.getNode(4,3).walkable = false;
27               
28             _gridView=new GridView(_grid);
29             _gridView.x=20;
30             _gridView.y=20;
31             addChild(_gridView);
32         }
33     }
34 }

 

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

当然这里面有一个小bug,不知道您看出来没有,下次再详解可能存在的问题。


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值