布尔囧囧

导航

 

前一部分的最后,我们给出了一个寻路的示例,在大多数情况下,运行还算良好,但是有一个小问题,如下图:

很明显,障碍物已经把路堵死了,但是小球仍然穿过对角线跑了出来!

问题在哪里:我们先回顾一下AStar.as中用于判断的if语句

1 //如果是当前节点,或者是不可通过的,则跳过
2 if (test == node || !test.walkable)
3 {
4     continue;
5 }

在这个判断中,并没有规定说不允许走对象线。来看看如何修正:

在以node为中心考查四周节点时,如果遇到水平和垂直方向都是障碍物时,既使对角节点是可穿越的普通节点,也不能通过。所以只要再加二个条件判断即可

1 //如果是当前节点,或者是不可通过的,且排除水平和垂直方向都是障碍物节点时的特例情况
2 if (test == node || !test.walkable || !_grid.getNode(node.x, test.y).walkable || !_grid.getNode(test.x, node.y).walkable)
3 {
4     continue;
5 }

再运行一下:

 

一切正常了!

前面提到的这些示例,终点与目标点都是固定的,但在实际游戏中,正好相反,比如"星际",选定一个农民后,在地图上随便点击一下,农民就能自动找到去目标点的路径。

  1 package
  2 {
  3     import flash.display.Sprite;
  4     import flash.display.StageAlign;
  5     import flash.display.StageScaleMode;
  6     import flash.events.Event;
  7     import flash.events.MouseEvent;
  8  
  9     [SWF(width=600,height=600)]
 10     public class Game extends Sprite
 11     {
 12         private var _cellSize:int=20;
 13         private var _grid:Grid;
 14         private var _player:Sprite;
 15         private var _index:int;
 16         private var _path:Array;
 17  
 18         public function Game()
 19         {
 20             stage.align=StageAlign.TOP_LEFT;
 21             stage.scaleMode=StageScaleMode.NO_SCALE;
 22             makePlayer();
 23             makeGrid();
 24             stage.addEventListener(MouseEvent.CLICK, onGridClick);
 25         }
 26  
 27         /** 生成一个player角色(简单起见,就是一个圈) */
 28         private function makePlayer():void
 29         {
 30             _player=new Sprite();
 31             _player.graphics.beginFill(0xff0000);
 32             _player.graphics.drawCircle(0, 0, 5);
 33             _player.graphics.endFill();
 34             _player.x=Math.random() * 600;
 35             _player.y=Math.random() * 600;
 36             addChild(_player);
 37         }
 38  
 39         /** 生成网格,并随机放置一些障碍 */
 40         private function makeGrid():void
 41         {
 42             _grid=new Grid(30, 30);
 43             for (var i:int=0; i < 200; i++)
 44             {
 45                 _grid.setWalkable(Math.floor(Math.random() * 30), Math.floor(Math.random() * 30), false);
 46             }
 47             drawGrid();
 48         }
 49  
 50         /** 画网格线以及为障碍物填充颜色*/
 51         private function drawGrid():void
 52         {
 53             graphics.clear();
 54             for (var i:int=0; i < _grid.numCols; i++)
 55             {
 56                 for (var j:int=0; j < _grid.numRows; j++)
 57                 {
 58                     var node:Node=_grid.getNode(i, j);
 59                     graphics.lineStyle(0);
 60                     graphics.beginFill(getColor(node));
 61                     graphics.drawRect(i * _cellSize, j * _cellSize, _cellSize, _cellSize);
 62                 }
 63             }
 64         }
 65  
 66         /** 返回节点颜色 */
 67         private function getColor(node:Node):uint
 68         {
 69             if (!node.walkable)
 70                 return 0;
 71             if (node == _grid.startNode)
 72                 return 0xcccccc;
 73             if (node == _grid.endNode)
 74                 return 0xff0000;
 75             return 0xffffff;
 76         }
 77  
 78         /** 鼠标点击时随机设置终点,并以player当前位置做为起点 */
 79         private function onGridClick(event:MouseEvent):void
 80         {
 81             var xpos:int=Math.floor(mouseX / _cellSize);
 82             var ypos:int=Math.floor(mouseY / _cellSize);
 83             _grid.setEndNode(xpos, ypos);
 84             xpos=Math.floor(_player.x / _cellSize);
 85             ypos=Math.floor(_player.y / _cellSize);
 86             _grid.setStartNode(xpos, ypos);
 87             drawGrid();
 88             findPath();
 89         }
 90  
 91         /** 寻路 */
 92         private function findPath():void
 93         {
 94             var astar:AStar=new AStar();
 95             if (astar.findPath(_grid))
 96             {
 97                 _path=astar.path;
 98                 _index=0;
 99                 addEventListener(Event.ENTER_FRAME, onEnterFrame);
100             }
101         }
102  
103         /**每帧的动画处理*/
104         private function onEnterFrame(event:Event):void
105         {
106             var targetX:Number=_path[_index].x * _cellSize + _cellSize / 2;
107             var targetY:Number=_path[_index].y * _cellSize + _cellSize / 2;
108              
109             //把经过的点,涂上黄色
110             var passedNode:Node=_path[_index];
111             graphics.lineStyle(0);
112             graphics.beginFill(0xffff00);
113             graphics.drawRect(passedNode.x * _cellSize, passedNode.y * _cellSize, _cellSize, _cellSize);
114              
115             var dx:Number=targetX - _player.x;
116             var dy:Number=targetY - _player.y;
117             var dist:Number=Math.sqrt(dx * dx + dy * dy);
118             if (dist < 1)
119             {
120                 _index++;//索引加1,即取一个路径节点
121                 if (_index >= _path.length)//达到最后一个节点时,移除ENTER_FRAME监听
122                 {
123                     removeEventListener(Event.ENTER_FRAME, onEnterFrame);
124                 }
125             }
126             else
127             {
128                 _player.x+=dx * .5;
129                 _player.y+=dy * .5;
130             }
131         }
132     }
133 }
View Code

 

拿鼠标在空白节点上随便点点,看看会发生些什么? 

考虑最后一个问题:实际游戏地图中有平地,有高坡,有沙地,有雪地...不同的路面状况,行走的难度(即代价)应该不同吧?而我们刚才的所有示例中,对所有可穿越的节点都是平等对待的。如何区分出不同情况的地形呢?

关注一下:Node.as中的

1 public var costMultiplier:Number=1.0;//代价因子

以及AStar.as中的

1 //计算test节点的总代价                      
2 var g:Number=node.g + cost * test.costMultiplier;

聪明的你一定看出端倪了!没错,costMultiplier就是代价的权重因子,如果让每个节点的权重因子不同,就能体现出不同地形的行走难度程度。

  1 package
  2 {
  3     import flash.display.Sprite;
  4     import flash.events.MouseEvent;
  5  
  6     public class GridView2 extends Sprite
  7     {
  8         private var _cellSize:int = 20;
  9         private var _grid:Grid;
 10                  
 11         public function GridView2(grid:Grid)
 12         {
 13             _grid = grid;
 14             for(var i:int = 0; i < _grid.numCols; i++)
 15             {
 16                 for(var j:int = 0; j < _grid.numRows; j++)
 17                 {
 18                     //为每个节点设置不同的“代价权重因子”
 19                     var mult:Number = Math.sin(i * .50) + Math.cos(j * .2 + i * .05);
 20                     _grid.getNode(i, j).costMultiplier = Math.abs(mult) + 1;
 21                 }
 22             }
 23             drawGrid();
 24             findPath();
 25             addEventListener(MouseEvent.CLICK, onGridClick);
 26         }
 27          
 28         //画网格
 29         public function drawGrid():void
 30         {
 31             graphics.clear();
 32             for(var i:int = 0; i < _grid.numCols; i++)
 33             {
 34                 for(var j:int = 0; j < _grid.numRows; j++)
 35                 {
 36                     var node:Node = _grid.getNode(i, j);
 37                     graphics.lineStyle(0);
 38                     graphics.beginFill(getColor(node));
 39                     graphics.drawRect(i * _cellSize, j * _cellSize, _cellSize, _cellSize);
 40                 }
 41             }
 42         }
 43          
 44         //取得单元格的颜色(与权重因子关联,costMultiplier越小,颜色越深)
 45         private function getColor(node:Node):uint
 46         {
 47             if(!node.walkable) return 0;
 48             if(node == _grid.startNode) return 0x666666;
 49             if(node == _grid.endNode) return 0x666666;
 50             var shade:Number = 300 - 70 * node.costMultiplier;
 51             return shade << 16 | shade << 8 | shade;
 52         }
 53          
 54         //单元格点击时,切换节点为普通节点或障碍物节点
 55         private function onGridClick(event:MouseEvent):void
 56         {
 57             var xpos:int = Math.floor(event.localX / _cellSize);
 58             var ypos:int = Math.floor(event.localY / _cellSize);
 59              
 60             _grid.setWalkable(xpos, ypos, !_grid.getNode(xpos, ypos).walkable);
 61             drawGrid();
 62             findPath();
 63         }
 64          
 65         //找路
 66         private function findPath():void
 67         {
 68             var astar:AStar = new AStar();
 69             if(astar.findPath(_grid))
 70             {
 71                 //showVisited(astar);
 72                 showPath(astar);
 73             }
 74         }
 75          
 76          
 77         private function showVisited(astar:AStar):void
 78         {
 79             var visited:Array = astar.visited;
 80             for(var i:int = 0; i < visited.length; i++)
 81             {
 82                 graphics.beginFill(0xcccccc);
 83                 graphics.drawRect(visited[i].x * _cellSize, visited[i].y * _cellSize, _cellSize, _cellSize);
 84                 graphics.endFill();
 85             }
 86         }
 87          
 88          
 89         private function showPath(astar:AStar):void
 90         {
 91             var path:Array = astar.path;
 92             for(var i:int = 0; i < path.length; i++)
 93             {
 94                 graphics.lineStyle(0);
 95                 graphics.beginFill(0);
 96                 graphics.drawCircle(path[i].x * _cellSize + _cellSize / 2,
 97                     path[i].y * _cellSize + _cellSize / 2,
 98                     _cellSize / 3);
 99             }
100         }
101     }
102 }
View Code

跟上一部分里的GridView.as比较起来,GridView2.as在构造函数里根据sin与cos函数,为节点设置了不同的权重因子,而且在节点着色上,深色的代价要比浅色的代价大,测试一下:

 1 package
 2 {
 3     import flash.display.Sprite;
 4     import flash.display.StageAlign;
 5     import flash.display.StageScaleMode;
 6     import flash.events.MouseEvent;
 7  
 8     [SWF(backgroundColor=0xffffff,width=440,height=440)]
 9     public class Pathfinding_2 extends Sprite
10     {
11         private var _grid:Grid;
12         private var _gridView:GridView2;
13  
14         public function Pathfinding_2()
15         {
16             stage.align=StageAlign.TOP_LEFT;
17             stage.scaleMode=StageScaleMode.NO_SCALE;
18             _grid=new Grid(20, 20);
19             _grid.setStartNode(1, 1);
20             _grid.setEndNode(18, 18);
21              
22              
23              
24              
25             _gridView=new GridView2(_grid);
26             _gridView.x=20;
27             _gridView.y=20;
28             addChild(_gridView);
29         }
30     }
31 }
View Code

可以看出,调整权重因子后,路径尽量在靠近浅色的区域前进!可能这样对比还不强烈,把上面测试代码中的GridView2换回GridView,对比看下没有权重因子干扰时的路径

 

 

 

 

当然,在具体游戏开发过程中,A*算法还要结合其它很多技术(比如加载地图,配合地图设置权重因子,把地图分配到网格单元等)才能最终做出不错的游戏,我们在这里只是讨论寻路算法的原理,其它方面留给大家自行去完善吧.

作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
 
标签: 动画flashflexas3寻路A*A星
posted on 2015-08-14 14:56  布尔囧囧  阅读(252)  评论(0编辑  收藏  举报