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

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

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

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

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

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package {   
     public class Node
     {
         public var x: int ;
         public var y: int ;
         public var f: Number ;
         public var g: Number ;
         public var h: Number ;
         public var walkable: Boolean = true ; //是否可穿越(通常把障碍物节点设置为false)
         public var parent:Node;
         public var costMultiplier: Number = 1.0 ; //代价因子
  
         public function Node(x: int , y: int )
         {
             this .x=x;
             this .y=y;
         }
     }
}

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

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

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

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

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

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

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

 

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//曼哈顿估价法
private function manhattan(node:Node): Number
{
     return Math.abs(node.x - _endNode.x) * _straightCost + Math.abs(node.y + _endNode.y) * _straightCost;
}
  
//几何估价法
private function euclidian(node:Node): Number
{
     var dx: Number =node.x - _endNode.x;
     var dy: Number =node.y - _endNode.y;
     return Math.sqrt(dx * dx + dy * dy) * _straightCost;
}
  
//对角线估价法
private function diagonal(node:Node): Number
{
     var dx: Number =Math.abs(node.x - _endNode.x);
     var dy: Number =Math.abs(node.y - _endNode.y);
     var diag: Number =Math.min(dx, dy);
     var straight: Number =dx + dy;
     return _diagCost * diag + _straightCost * (straight - 2 * diag);
}

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

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

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

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

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值