在上一篇里,我们学习了“自主角色”的一些基本行为:寻找(seek)、避开(flee)、到达(arrive)、追捕(pursue)、躲避(evade)、漫游(wander)。这一篇将继续学习其它更复杂,更高级的行为。
原作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
一、对象回避(object avoidance)
对象回避的正式解释为:角色预测出对象的行动路径,然后避开他们。
也可以通俗的描述为:假如有一个"灰太狼抓喜羊羊"的游戏场景,“喜羊羊"在草地上四处游荡的时候要尽量避免被随处找羊的"灰太狼"抓住。好象听起来并不复杂,跟前面提到的"避开(flee)"或"躲避(evade)"甚至第一章提到的碰撞检测差不多,只要调头走开、改变路线甚至检测这二者是否发生碰撞即可。
但如果仔细考虑的话,这个并不象上面想的这么简单,如果“羊”跟“狼”的距离足够远(或者“狼”运动得太慢),以至于“狼”在预测的时间范围内根本不可能抓住“羊”,那么“羊”可能根本无需调整方向(或仅需做小的调整);如果“狼”突然出现在离“羊”很近的地方,“羊”做出的反应是急转90度,换个方向跑开。(问:为什么不是转180度反方向跑呢?答:如果大家经常看动物世界里非洲草原上“猎豹追羚羊"的片段,应该就能理解了,大多数情况下,急转弯比反向跑,更能有效避开觅食者)另外,该行为的另一个特征是预测可能要发生的碰撞,而非实际发生的碰撞,所以碰撞检测也不太适合。
ok,直接看算法示意图吧:
首先把目标(障碍)物体认为是一个有半径范围的圆形对象(当然这是一种简化问题的理想模型);
然后得出自己与目标的距离向量difference;
接下来将自身的速度向量单位化,得到单位向量header(即长度为1,且与速度同方向的向量);
计算header与difference的点积(也叫点乘、内积、标量积),得到一个值dotProd = |header| * |difference| * cos(θ) = |difference| * cos(θ) (注:header为单位向量,大小为1,所以可省去),很明显如果该值小于0,则表示障碍物就在前方,准备做调整;
将header放大一个系数,模拟出自身的触角feeler(相当于物体自身向前方伸出去一个触须试探试探);
将difference投影在feeler上,得到向量projection,并进一步得到距离dist(即projection末端与difference末端的距离);
如果dist小于圆半径,且projection长度小于feeler的长度(即触角碰到了目标了),则转90度逃开;
1 private var _avoidDistance:Number=300;//发现障碍物的有效视野 2 private var _avoidBuffer:Number=20;//机车在准备避开时,自身和障碍物间的预留距离。 3 4 public function set avoidDistance(value:Number):void { 5 _avoidDistance=value; 6 } 7 public function get avoidDistance():Number { 8 return _avoidDistance; 9 } 10 11 public function set avoidBuffer(value:Number):void { 12 _avoidBuffer=value; 13 } 14 public function get avoidBuffer():Number { 15 return _avoidBuffer; 16 } 17 18 //对象回避 19 public function avoid(circles: Array):void { 20 for (var i: int=0; i < circles.length; i++) { 21 var circle:Circle=circles[i] as Circle; 22 var heading:Vector2D=_velocity.clone().normalize(); 23 // 障碍物和机车间的位移向量 24 var difference:Vector2D=circle.position.subtract(_position); 25 var dotProd:Number=difference.dotProd(heading); 26 // 如果障碍物在机车前方 27 if (dotProd>0) { 28 // 机车的“触角” 29 var feeler:Vector2D=heading.multiply(_avoidDistance); 30 // 位移在触角上的映射 31 var projection:Vector2D=heading.multiply(dotProd); 32 // 障碍物离触角的距离 33 var dist:Number=projection.subtract(difference).length; 34 // 如果触角(在算上缓冲后)和障碍物相交 35 // 并且位移的映射的长度小于触角的长度 36 // 我们就说碰撞将要发生,需改变转向 37 if (dist < circle.radius + _avoidBuffer && projection.length < feeler.length) { 38 // 计算出一个转90度的力 39 var force:Vector2D=heading.multiply(_maxSpeed); 40 force.angle+=difference.sign(_velocity)*Math.PI/2; 41 // 通过离障碍物的距离,调整力度大小,使之足够小但又能避开 42 force=force.multiply(1.0-projection.length/feeler.length); 43 // 叠加于转向力上 44 _steeringForce=_steeringForce.add(force); 45 // 刹车——转弯的时候要放慢机车速度,离障碍物越接近,刹车越狠。 46 _velocity=_velocity.multiply(projection.length/feeler.length); 47 } 48 } 49 } 50 }
将以上代码加入SteeredVehicle.as,当然,在测试前还要有一个Circle类来模拟障碍物
1 package { 2 import flash.display.Sprite; 3 public class Circle extends Sprite { 4 private var _radius:Number; 5 private var _color:uint; 6 private var _vx:Number; 7 private var _vy:Number; 8 9 public function Circle(radius:Number, color:uint = 0x000000) { 10 _radius=radius; 11 _color=color; 12 graphics.lineStyle(0, _color); 13 graphics.beginFill(_color); 14 graphics.drawCircle(0, 0, _radius); 15 graphics.endFill(); 16 } 17 18 public function get radius():Number { 19 return _radius; 20 } 21 22 public function get position():Vector2D { 23 return new Vector2D(x, y); 24 } 25 26 public function get vx():Number { 27 return _vx; 28 } 29 30 public function set vx(value:Number):void { 31 _vx = value; 32 } 33 34 public function get vy():Number { 35 return _vy; 36 } 37 38 public function set vy(value:Number):void { 39 _vy = value; 40 } 41 } 42 }
测试:
1 package { 2 import flash.display.Sprite; 3 import flash.display.StageAlign; 4 import flash.display.StageScaleMode; 5 import flash.events.Event; 6 7 public class AvoidTest extends Sprite { 8 private var _vehicle:SteeredVehicle; 9 private var _circles:Array; 10 private var _numCircles:int=5; 11 12 public function AvoidTest():void { 13 stage.align=StageAlign.TOP_LEFT; 14 stage.scaleMode=StageScaleMode.NO_SCALE; 15 _vehicle = new SteeredVehicle(0xff0000); 16 17 _vehicle.edgeBehavior=Vehicle.BOUNCE; 18 addChild(_vehicle); 19 20 //初始化障碍物 21 _circles = new Array(); 22 for (var i:int = 0; i < _numCircles; i++) { 23 var c:Circle=new Circle(Math.random()*30+15,0x0000ff); 24 c.x=Math.random()*stage.stageWidth; 25 c.y=Math.random()*stage.stageHeight; 26 c.vx=Math.random()-0.5; 27 c.vy=Math.random()-0.5; 28 29 if (c.x<c.radius) { 30 c.x=c.radius; 31 } else if (c.x>stage.stageWidth-c.radius) { 32 c.x=stage.stageWidth-c.radius; 33 } 34 35 if (c.y<c.radius) { 36 c.y=c.radius; 37 } else if (c.y>stage.stageHeight-c.radius) { 38 c.y=stage.stageHeight-c.radius; 39 } 40 41 addChild(c); 42 _circles.push(c); 43 } 44 addEventListener(Event.ENTER_FRAME, onEnterFrame); 45 } 46 47 private function onEnterFrame(e:Event):void { 48 49 _vehicle.wander(); 50 51 //处理障碍物的运动以及边界反弹 52 for (var i:int = 0; i < _numCircles; i++) { 53 var c:Circle=_circles[i] as Circle; 54 c.x+=c.vx; 55 c.y+=c.vy; 56 if (c.x<c.radius) { 57 c.x=c.radius; 58 c.vx*=-1; 59 } else if (c.x>stage.stageWidth-c.radius) { 60 c.x=stage.stageWidth-c.radius; 61 c.vx*=-1; 62 } 63 if (c.y<c.radius) { 64 c.y=c.radius; 65 c.vy*=-1; 66 } else if (c.y>stage.stageHeight-c.radius) { 67 c.y=stage.stageHeight-c.radius; 68 c.vy*=-1; 69 } 70 } 71 _vehicle.avoid(_circles); 72 _vehicle.update(); 73 } 74 } 75 }
二、路径跟随(path following)
对于玩过星际之类游戏的朋友们,这种行为应该最熟悉了。随便选一个神族的狂热者(俗称叉叉兵),然后在几个指定的位置点击一下,它们就会沿着指定的位置来回巡逻。这就是路径跟随:角色尽可能的沿着指定的路径移动。
在算法上的处理很简单:用数组保存一组位置(每个位置其实就是一个Vector2D对象),然后加一个索引变量(指针),用于指示当前移动到了哪一个位置,最终将机车以seek行为移动以下一个位置。
但有一个要注意的细节:seek行为会让机车最终在目标位置来回反复运动停不下来,为了让代码能知道机车已经经过了当前位置,接下来应该去下一个位置,需要引入一个距离阈值,用于判断机车是否已经接近目标点。
1 private var _pathIndex:int=0;//路径索引 2 private var _pathThreshold:Number=20;//路径跟随中的距离阈值 3 4 public function set pathIndex(value:int):void { 5 _pathIndex=value; 6 } 7 public function get pathIndex():int { 8 return _pathIndex; 9 } 10 public function set pathThreshold(value:Number):void { 11 _pathThreshold=value; 12 } 13 public function get pathThreshold():Number { 14 return _pathThreshold; 15 } 16 17 public function set avoidDistance(value:Number):void { 18 _avoidDistance=value; 19 } 20 public function get avoidDistance():Number { 21 return _avoidDistance; 22 } 23 24 public function set avoidBuffer(value:Number):void { 25 _avoidBuffer=value; 26 } 27 public function get avoidBuffer():Number { 28 return _avoidBuffer; 29 } 30 31 //路径跟随 32 public function followPath(path:Array,loop:Boolean=false):void { 33 var wayPoint:Vector2D=path[_pathIndex]; 34 if (wayPoint==null) { 35 return; 36 } 37 if (_position.dist(wayPoint)<_pathThreshold) { 38 if (_pathIndex>=path.length-1) { 39 if (loop) { 40 _pathIndex=0; 41 } 42 } else { 43 _pathIndex++; 44 } 45 } 46 if (_pathIndex>=path.length-1&&! loop) { 47 arrive(wayPoint); 48 } else { 49 seek(wayPoint); 50 } 51 }
测试:
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 public class PathTest extends Sprite { 9 private var _vehicle:SteeredVehicle; 10 private var _path:Array; 11 public function PathTest() { 12 stage.align=StageAlign.TOP_LEFT; 13 stage.scaleMode=StageScaleMode.NO_SCALE; 14 _vehicle=new SteeredVehicle ; 15 addChild(_vehicle); 16 _path=new Array ; 17 stage.addEventListener(MouseEvent.CLICK,onClick); 18 addEventListener(Event.ENTER_FRAME,onEnterFrame); 19 } 20 private function onEnterFrame(event:Event):void { 21 _vehicle.followPath(_path,true); 22 _vehicle.update(); 23 } 24 private function onClick(event:MouseEvent):void { 25 graphics.lineStyle(0,0,.25); 26 if (_path.length==0) { 27 graphics.moveTo(mouseX,mouseY); 28 } 29 graphics.lineTo(mouseX,mouseY); 30 graphics.drawCircle(mouseX,mouseY,10); 31 graphics.moveTo(mouseX,mouseY); 32 _path.push(new Vector2D(mouseX,mouseY)); 33 } 34 } 35 }
拿鼠标在上面随便点一下,就能看到效果了
三、群落(flock)行为
群落行为是指类似鸟群这样的复合行为。它由三个子行为组成:
分离(separation):鸟群中每个角色都试着和相邻角色保持一定的距离(即:一只鸟与其它鸟太靠近时,主动退让一定的距离,以避免碰到)
凝聚(cohesion):每个角色尽量不掉队,不落下太远(即:尽量向鸟群靠拢)
队列(alignment):每个角色尽可能与相邻角色行动于同一方向(即:每只鸟的速度方向可能不完全相同,但大致跟队伍的总体方向一致)
借用一句李建忠老师名言:原代码就是最好的设计! 直接上代码吧:
1 private var _inSightDist:Number=200;//视野距离 2 private var _tooCloseDist:Number=60;//防止群落靠得太近的安全距离 3 4 //群落行为 5 public function flock(vehicles:Array):void { 6 var averageVelocity:Vector2D=_velocity.clone();//平均速度变量 7 var averagePosition:Vector2D=new Vector2D ;//平均位置变量 8 var inSightCount:int=0;//在视野中的机车数量 9 10 for (var i:int=0; i<vehicles.length; i++) { 11 var vehicle:Vehicle=vehicles[i] as Vehicle; 12 if (vehicle!=this&&inSight(vehicle)) { //如果其它机车在视野中 13 //累加速度与位置 14 averageVelocity=averageVelocity.add(vehicle.velocity); 15 averagePosition=averagePosition.add(vehicle.position); 16 //如果其它机车太靠近,则避开(即分离行为[separation]的体现) 17 if (tooClose(vehicle)) { 18 flee(vehicle.position); 19 } 20 inSightCount++; //累加在视野中的机车数 21 } 22 } 23 if (inSightCount>0) { 24 //计算平均位置 25 averageVelocity=averageVelocity.divide(inSightCount); 26 averagePosition=averagePosition.divide(inSightCount); 27 seek(averagePosition);//向中心位置靠拢(即凝聚行为[cohesion]的体现) 28 _steeringForce.add(averageVelocity.subtract(_velocity));//根据平均速度校准自身速度(即队列[alignment]行为的体现) 29 } 30 } 31 32 public function set inSightDist(vaule:Number):void { 33 _inSightDist=vaule; 34 } 35 public function get inSightDist():Number { 36 return _inSightDist; 37 } 38 public function set tooCloseDist(value:Number):void { 39 _tooCloseDist=value; 40 } 41 public function get tooCloseDist():Number { 42 return _tooCloseDist; 43 } 44 45 //判断(身后的其它)机车是否在视野范围内 46 public function inSight(vehicle:Vehicle):Boolean { 47 if (_position.dist(vehicle.position)>_inSightDist) { 48 return false; 49 } 50 51 //---->start 下面这一段代码甚至去掉也行,不过去掉后,群落的行为将有所不同 52 var heading:Vector2D=_velocity.clone().normalize(); 53 var difference:Vector2D=vehicle.position.subtract(_position); 54 var dotProd:Number=difference.dotProd(heading); 55 if (dotProd<0) { 56 return false; 57 } 58 //<-----end 59 60 61 return true; 62 } 63 64 public function tooClose(vehicle:Vehicle):Boolean { 65 return _position.dist(vehicle.position)<_tooCloseDist; 66 }
重点关注下inSight方法,它直接影响到群落的行为,示意图如下:
先检测二只鸟的距离是否足够近,然后仅关注身后的其它鸟。
测试代码:
1 package { 2 import flash.display.Sprite; 3 import flash.display.StageAlign; 4 import flash.display.StageScaleMode; 5 import flash.events.Event; 6 public class FlockTest extends Sprite { 7 private var _vehicles:Array; 8 private var _numVehicles:int=20; 9 public function FlockTest() { 10 stage.align=StageAlign.TOP_LEFT; 11 12 stage.scaleMode=StageScaleMode.NO_SCALE; 13 _vehicles=new Array ; 14 for (var i:int=0; i<_numVehicles; i++) { 15 var vehicle:SteeredVehicle=new SteeredVehicle(Math.random()*0xffffff); 16 vehicle.position=new Vector2D(Math.random()*stage.stageWidth,Math.random()*stage.stageHeight); 17 vehicle.velocity=new Vector2D(Math.random()*20-10,Math.random()*20-10); 18 vehicle.edgeBehavior=Vehicle.BOUNCE; 19 _vehicles.push(vehicle); 20 addChild(vehicle); 21 } 22 addEventListener(Event.ENTER_FRAME,onEnterFrame); 23 } 24 private function onEnterFrame(event:Event):void { 25 for (var i:int=0; i<_numVehicles; i++) { 26 _vehicles[i].flock(_vehicles); 27 _vehicles[i].update(); 28 } 29 } 30 } 31 }
如果把inSight中检测其它鸟是否在身后的代码去掉,即简化成:
1 public function inSight(vehicle:Vehicle):Boolean { 2 if (_position.dist(vehicle.position)>_inSightDist) { 3 return false; 4 } 5 return true; 6 }
预测一下最终的效果:这样相当于只要距离小于阈值的其它鸟,其速度和位置都会被计算在内,最终整个群落将始终聚集在一定的范围内,不会发生分离,从而体现出了另外一种群落效果。
最后,给出Vehicle.as及SteeredVehicle.as的完整代码
1 package { 2 import flash.display.Sprite; 3 4 public class Vehicle extends Sprite { 5 //边界行为:是屏幕环绕(wrap),还是反弹{bounce} 6 protected var _edgeBehavior:String=WRAP; 7 //质量 8 protected var _mass:Number=1.0; 9 //最大速度 10 protected var _maxSpeed:Number=10; 11 //坐标 12 protected var _position:Vector2D; 13 //速度 14 protected var _velocity:Vector2D; 15 16 //边界行为常量 17 public static const WRAP:String="wrap"; 18 public static const BOUNCE:String="bounce"; 19 20 public function Vehicle(color:uint=0xffffff) { 21 _position=new Vector2D ; 22 _velocity=new Vector2D ; 23 draw(color); 24 } 25 26 27 protected function draw(color:uint=0xffffff):void { 28 graphics.clear(); 29 graphics.lineStyle(0); 30 graphics.beginFill(color); 31 graphics.moveTo(10,0); 32 graphics.lineTo(-10,5); 33 graphics.lineTo(-10,-5); 34 graphics.lineTo(10,0); 35 graphics.endFill(); 36 } 37 38 39 public function update():void { 40 41 //设置最大速度 42 _velocity.truncate(_maxSpeed); 43 44 //根据速度更新坐标向量 45 _position=_position.add(_velocity); 46 47 //处理边界行为 48 if (_edgeBehavior==WRAP) { 49 wrap(); 50 } else if (_edgeBehavior==BOUNCE) { 51 bounce(); 52 } 53 54 //更新x,y坐标值 55 x=position.x; 56 y=position.y; 57 58 //处理旋转角度 59 rotation=_velocity.angle*180/Math.PI; 60 } 61 62 //反弹 63 private function bounce():void { 64 if (stage!=null) { 65 if (position.x>stage.stageWidth) { 66 position.x=stage.stageWidth; 67 velocity.x*=-1; 68 } else if (position.x<0) { 69 position.x=0; 70 velocity.x*=-1; 71 } 72 if (position.y>stage.stageHeight) { 73 position.y=stage.stageHeight; 74 velocity.y*=-1; 75 } else if (position.y<0) { 76 position.y=0; 77 velocity.y*=-1; 78 } 79 } 80 } 81 82 //屏幕环绕 83 private function wrap():void { 84 if (stage!=null) { 85 if (position.x>stage.stageWidth) { 86 position.x=0; 87 } 88 if (position.x<0) { 89 position.x=stage.stageWidth; 90 } 91 if (position.y>stage.stageHeight) { 92 position.y=0; 93 } 94 if (position.y<0) { 95 position.y=stage.stageHeight; 96 } 97 } 98 } 99 100 //下面的都是属性定义 101 102 103 public function set edgeBehavior(value:String):void { 104 _edgeBehavior=value; 105 } 106 107 public function get edgeBehavior():String { 108 return _edgeBehavior; 109 } 110 111 112 public function set mass(value:Number):void { 113 _mass=value; 114 } 115 116 public function get mass():Number { 117 return _mass; 118 } 119 120 public function set maxSpeed(value:Number):void { 121 _maxSpeed=value; 122 } 123 124 public function get maxSpeed():Number { 125 return _maxSpeed; 126 } 127 128 public function set position(value:Vector2D):void { 129 _position=value; 130 x=_position.x; 131 y=_position.y; 132 } 133 134 public function get position():Vector2D { 135 return _position; 136 } 137 138 public function set velocity(value:Vector2D):void { 139 _velocity=value; 140 } 141 142 public function get velocity():Vector2D { 143 return _velocity; 144 } 145 146 override public function set x(value:Number):void { 147 super.x=value; 148 _position.x=x; 149 } 150 151 override public function set y(value:Number):void { 152 super.y=value; 153 _position.y=y; 154 } 155 } 156 }
1 package { 2 import flash.display.Sprite; 3 4 //(具有)转向(行为的)机车 5 public class SteeredVehicle extends Vehicle { 6 private var _maxForce:Number=1;//最大转向力 7 private var _steeringForce:Vector2D;//转向速度 8 private var _arrivalThreshold:Number=100;//到达行为的距离阈值(小于这个距离将减速) 9 private var _wanderAngle:Number=0; 10 private var _wanderDistance:Number=10; 11 private var _wanderRadius:Number=5; 12 private var _wanderRange:Number=1; 13 14 private var _avoidDistance:Number=300;//发现障碍物的有效视野 15 private var _avoidBuffer:Number=20;//机车在准备避开时,自身和障碍物间的预留距离。 16 17 private var _pathIndex:int=0;//路径索引 18 private var _pathThreshold:Number=20;//路径跟随中的距离阈值 19 20 private var _inSightDist:Number=200;//视野距离 21 private var _tooCloseDist:Number=60;//防止群落靠得太近的安全距离 22 23 //群落行为 24 public function flock(vehicles:Array):void { 25 var averageVelocity:Vector2D=_velocity.clone();//平均速度变量 26 var averagePosition:Vector2D=new Vector2D ;//平均位置变量 27 var inSightCount:int=0;//在视野中的机车数量 28 29 for (var i:int=0; i<vehicles.length; i++) { 30 var vehicle:Vehicle=vehicles[i] as Vehicle; 31 if (vehicle!=this&&inSight(vehicle)) { //如果其它机车在视野中 32 //累加速度与位置 33 averageVelocity=averageVelocity.add(vehicle.velocity); 34 averagePosition=averagePosition.add(vehicle.position); 35 //如果其它机车太靠近,则避开(即分离行为[separation]的体现) 36 if (tooClose(vehicle)) { 37 flee(vehicle.position); 38 } 39 inSightCount++; //累加在视野中的机车数 40 } 41 } 42 if (inSightCount>0) { 43 //计算平均位置 44 averageVelocity=averageVelocity.divide(inSightCount); 45 averagePosition=averagePosition.divide(inSightCount); 46 seek(averagePosition);//向中心位置靠拢(即凝聚行为[cohesion]的体现) 47 _steeringForce.add(averageVelocity.subtract(_velocity));//根据平均速度校准自身速度(即队列[alignment]行为的体现) 48 } 49 } 50 51 public function set inSightDist(vaule:Number):void { 52 _inSightDist=vaule; 53 } 54 public function get inSightDist():Number { 55 return _inSightDist; 56 } 57 public function set tooCloseDist(value:Number):void { 58 _tooCloseDist=value; 59 } 60 public function get tooCloseDist():Number { 61 return _tooCloseDist; 62 } 63 64 //判断(身后的其它)机车是否在视野范围内 65 public function inSight(vehicle:Vehicle):Boolean { 66 if (_position.dist(vehicle.position)>_inSightDist) { 67 return false; 68 } 69 70 //---->start 下面这一段代码甚至去掉也行,不过去掉后,群落的行为将有所不同 71 var heading:Vector2D=_velocity.clone().normalize(); 72 var difference:Vector2D=vehicle.position.subtract(_position); 73 var dotProd:Number=difference.dotProd(heading); 74 if (dotProd<0) { 75 return false; 76 } 77 //<-----end 78 79 80 return true; 81 } 82 83 public function tooClose(vehicle:Vehicle):Boolean { 84 return _position.dist(vehicle.position)<_tooCloseDist; 85 } 86 87 88 public function set pathIndex(value:int):void { 89 _pathIndex=value; 90 } 91 public function get pathIndex():int { 92 return _pathIndex; 93 } 94 public function set pathThreshold(value:Number):void { 95 _pathThreshold=value; 96 } 97 public function get pathThreshold():Number { 98 return _pathThreshold; 99 } 100 101 public function set avoidDistance(value:Number):void { 102 _avoidDistance=value; 103 } 104 public function get avoidDistance():Number { 105 return _avoidDistance; 106 } 107 108 public function set avoidBuffer(value:Number):void { 109 _avoidBuffer=value; 110 } 111 public function get avoidBuffer():Number { 112 return _avoidBuffer; 113 } 114 115 //路径跟随 116 public function followPath(path:Array,loop:Boolean=false):void { 117 var wayPoint:Vector2D=path[_pathIndex]; 118 if (wayPoint==null) { 119 return; 120 } 121 if (_position.dist(wayPoint)<_pathThreshold) { 122 if (_pathIndex>=path.length-1) { 123 if (loop) { 124 _pathIndex=0; 125 } 126 } else { 127 _pathIndex++; 128 } 129 } 130 if (_pathIndex>=path.length-1&&! loop) { 131 arrive(wayPoint); 132 } else { 133 seek(wayPoint); 134 } 135 } 136 137 //对象回避 138 public function avoid(circles:Array):void { 139 for (var i:int=0; i<circles.length; i++) { 140 var circle:Circle=circles[i] as Circle; 141 var heading:Vector2D=_velocity.clone().normalize(); 142 // 障碍物和机车间的位移向量 143 var difference:Vector2D=circle.position.subtract(_position); 144 var dotProd:Number=difference.dotProd(heading); 145 // 如果障碍物在机车前方 146 if (dotProd>0) { 147 // 机车的“触角” 148 var feeler:Vector2D=heading.multiply(_avoidDistance); 149 // 位移在触角上的映射 150 var projection:Vector2D=heading.multiply(dotProd); 151 // 障碍物离触角的距离 152 var dist:Number=projection.subtract(difference).length; 153 // 如果触角(在算上缓冲后)和障碍物相交 154 // 并且位移的映射的长度小于触角的长度 155 // 我们就说碰撞将要发生,需改变转向 156 if (dist<circle.radius+_avoidBuffer&&projection.length<feeler.length) { 157 // 计算出一个转90度的力 158 var force:Vector2D=heading.multiply(_maxSpeed); 159 force.angle+=difference.sign(_velocity)*Math.PI/2; 160 // 通过离障碍物的距离,调整力度大小,使之足够小但又能避开 161 force=force.multiply(1.0-projection.length/feeler.length); 162 // 叠加于转向力上 163 _steeringForce=_steeringForce.add(force); 164 // 刹车——转弯的时候要放慢机车速度,离障碍物越接近,刹车越狠。 165 _velocity=_velocity.multiply(projection.length/feeler.length); 166 } 167 } 168 } 169 } 170 171 //漫游 172 public function wander():void { 173 var center:Vector2D=velocity.clone().normalize().multiply(_wanderDistance); 174 var offset:Vector2D=new Vector2D(0); 175 offset.length=_wanderRadius; 176 offset.angle=_wanderAngle; 177 _wanderAngle+=Math.random()-0.5*_wanderRange; 178 var force:Vector2D=center.add(offset); 179 _steeringForce=_steeringForce.add(force); 180 } 181 182 public function set wanderDistance(value:Number):void { 183 _wanderDistance=value; 184 } 185 public function get wanderDistance():Number { 186 return _wanderDistance; 187 } 188 189 public function set wanderRadius(value:Number):void { 190 _wanderRadius=value; 191 } 192 public function get wanderRadius():Number { 193 return _wanderRadius; 194 } 195 public function set wanderRange(value:Number):void { 196 _wanderRange=value; 197 } 198 public function get wanderRange():Number { 199 return _wanderRange; 200 } 201 202 public function set arriveThreshold(value:Number):void { 203 _arrivalThreshold=value; 204 } 205 public function get arriveThreshold():Number { 206 return _arrivalThreshold; 207 } 208 209 //构造函数 210 public function SteeredVehicle(color:uint=0xffffff) { 211 _steeringForce=new Vector2D ; 212 super(color); 213 } 214 public function set maxForce(value:Number):void { 215 _maxForce=value; 216 } 217 public function get maxForce():Number { 218 return _maxForce; 219 } 220 221 //寻找(Seek)行为 222 public function seek(target:Vector2D):void { 223 var desiredVelocity:Vector2D=target.subtract(_position); 224 desiredVelocity.normalize(); 225 desiredVelocity=desiredVelocity.multiply(_maxSpeed);//注:这里的_maxSpeed是从父类继承得来的 226 var force:Vector2D=desiredVelocity.subtract(_velocity); 227 _steeringForce=_steeringForce.add(force); 228 } 229 230 //避开(flee)行为 231 public function flee(target:Vector2D):void { 232 var desiredVelocity:Vector2D=target.subtract(_position); 233 desiredVelocity.normalize(); 234 desiredVelocity=desiredVelocity.multiply(_maxSpeed); 235 var force:Vector2D=desiredVelocity.subtract(_velocity); 236 _steeringForce=_steeringForce.subtract(force);//这是唯一也seek行为不同的地方,一句话解释:既然发现了目标,那就调头就跑吧! 237 } 238 239 //到达(arrive)行为 240 public function arrive(target:Vector2D):void { 241 var desiredVelocity:Vector2D=target.subtract(_position); 242 desiredVelocity.normalize(); 243 var dist:Number=_position.dist(target); 244 if (dist>_arrivalThreshold) { 245 desiredVelocity=desiredVelocity.multiply(_maxSpeed); 246 } else { 247 desiredVelocity=desiredVelocity.multiply(_maxSpeed*dist/_arrivalThreshold); 248 } 249 var force:Vector2D=desiredVelocity.subtract(_velocity); 250 _steeringForce=_steeringForce.add(force); 251 } 252 253 //追捕(pursue)行为 254 public function pursue(target:Vehicle):void { 255 var lookAheadTime:Number=position.dist(target.position)/_maxSpeed;//假如目标不动,追捕者开足马力赶过去的话,计算需要多少时间 256 var predictedTarget:Vector2D=target.position.add(target.velocity.multiply(lookAheadTime)); 257 seek(predictedTarget); 258 } 259 260 //躲避(evade)行为 261 public function evade(target:Vehicle):void { 262 var lookAheadTime:Number=position.dist(target.position)/_maxSpeed; 263 var predictedTarget:Vector2D=target.position.add(target.velocity.multiply(lookAheadTime)); 264 flee(predictedTarget); 265 } 266 267 268 override public function update():void { 269 _steeringForce.truncate(_maxForce);//限制为最大转向速度,以避免出现突然的大转身 270 _steeringForce=_steeringForce.divide(_mass);//惯性的体现 271 _velocity=_velocity.add(_steeringForce); 272 _steeringForce=new Vector2D ; 273 super.update(); 274 } 275 } 276 }