标签:
在前一部分的最后,我们给出了一个寻路的示例,在大多数情况下,运行还算良好,但是有一个小问题,如下图:
很明显,障碍物已经把路堵死了,但是小球仍然穿过对角线跑了出来!
问题在哪里:我们先回顾一下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 }
拿鼠标在空白节点上随便点点,看看会发生些什么?
考虑最后一个问题:实际游戏地图中有平地,有高坡,有沙地,有雪地...不同的路面状况,行走的难度(即代价)应该不同吧?而我们刚才的所有示例中,对所有可穿越的节点都是平等对待的。如何区分出不同情况的地形呢?
关注一下: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 }
跟上一部分里的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 }
可以看出,调整权重因子后,路径尽量在靠近浅色的区域前进!可能这样对比还不强烈,把上面测试代码中的GridView2换回GridView,对比看下没有权重因子干扰时的路径
当然,在具体游戏开发过程中,A*算法还要结合其它很多技术(比如加载地图,配合地图设置权重因子,把地图分配到网格单元等)才能最终做出不错的游戏,我们在这里只是讨论寻路算法的原理,其它方面留给大家自行去完善吧.
“AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (下)
标签:
原文地址:http://www.cnblogs.com/xiyuxiyu/p/4730003.html