在上一篇里,我们学习了“自主角色”的一些基本行为:寻找(seek)、避开(flee)、到达(arrive)、追捕(pursue)、躲避(evade)、漫游(wander)。这一篇将继续学习其它更复杂,更高级的行为。
一、对象回避(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度逃开;
private var _avoidDistance:Number=300;//发现障碍物的有效视野 private var _avoidBuffer:Number=20;//机车在准备避开时,自身和障碍物间的预留距离。 public function set avoidDistance(value:Number):void { _avoidDistance=value; } public function get avoidDistance():Number { return _avoidDistance; } public function set avoidBuffer(value:Number):void { _avoidBuffer=value; } public function get avoidBuffer():Number { return _avoidBuffer; } //对象回避 public function avoid(circles: Array):void { for (var i: int=0; i < circles.length; i++) { var circle:Circle=circles[i] as Circle; var heading:Vector2D=_velocity.clone().normalize(); // 障碍物和机车间的位移向量 var difference:Vector2D=circle.position.subtract(_position); var dotProd:Number=difference.dotProd(heading); // 如果障碍物在机车前方 if (dotProd>0) { // 机车的“触角” var feeler:Vector2D=heading.multiply(_avoidDistance); // 位移在触角上的映射 var projection:Vector2D=heading.multiply(dotProd); // 障碍物离触角的距离 var dist:Number=projection.subtract(difference).length; // 如果触角(在算上缓冲后)和障碍物相交 // 并且位移的映射的长度小于触角的长度 // 我们就说碰撞将要发生,需改变转向 if (dist < circle.radius + _avoidBuffer && projection.length < feeler.length) { // 计算出一个转90度的力 var force:Vector2D=heading.multiply(_maxSpeed); force.angle+=difference.sign(_velocity)*Math.PI/2; // 通过离障碍物的距离,调整力度大小,使之足够小但又能避开 force=force.multiply(1.0-projection.length/feeler.length); // 叠加于转向力上 _steeringForce=_steeringForce.add(force); // 刹车——转弯的时候要放慢机车速度,离障碍物越接近,刹车越狠。 _velocity=_velocity.multiply(projection.length/feeler.length); } } } }
将以上代码加入SteeredVehicle.as,当然,在测试前还要有一个Circle类来模拟障碍物
package { import flash.display.Sprite; public class Circle extends Sprite { private var _radius:Number; private var _color:uint; private var _vx:Number; private var _vy:Number; public function Circle(radius:Number, color:uint = 0x000000) { _radius=radius; _color=color; graphics.lineStyle(0, _color); graphics.beginFill(_color); graphics.drawCircle(0, 0, _radius); graphics.endFill(); } public function get radius():Number { return _radius; } public function get position():Vector2D { return new Vector2D(x, y); } public function get vx():Number { return _vx; } public function set vx(value:Number):void { _vx = value; } public function get vy():Number { return _vy; } public function set vy(value:Number):void { _vy = value; } } }
测试:
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; public class AvoidTest extends Sprite { private var _vehicle:SteeredVehicle; private var _circles:Array; private var _numCircles:int=5; public function AvoidTest():void { stage.align=StageAlign.TOP_LEFT; stage.scaleMode=StageScaleMode.NO_SCALE; _vehicle = new SteeredVehicle(0xff0000); _vehicle.edgeBehavior=Vehicle.BOUNCE; addChild(_vehicle); //初始化障碍物 _circles = new Array(); for (var i:int = 0; i < _numCircles; i++) { var c:Circle=new Circle(Math.random()*30+15,0x0000ff); c.x=Math.random()*stage.stageWidth; c.y=Math.random()*stage.stageHeight; c.vx=Math.random()-0.5; c.vy=Math.random()-0.5; if (c.x<c.radius) { c.x=c.radius; } else if (c.x>stage.stageWidth-c.radius) { c.x=stage.stageWidth-c.radius; } if (c.y<c.radius) { c.y=c.radius; } else if (c.y>stage.stageHeight-c.radius) { c.y=stage.stageHeight-c.radius; } addChild(c); _circles.push(c); } addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(e:Event):void { _vehicle.wander(); //处理障碍物的运动以及边界反弹 for (var i:int = 0; i < _numCircles; i++) { var c:Circle=_circles[i] as Circle; c.x+=c.vx; c.y+=c.vy; if (c.x<c.radius) { c.x=c.radius; c.vx*=-1; } else if (c.x>stage.stageWidth-c.radius) { c.x=stage.stageWidth-c.radius; c.vx*=-1; } if (c.y<c.radius) { c.y=c.radius; c.vy*=-1; } else if (c.y>stage.stageHeight-c.radius) { c.y=stage.stageHeight-c.radius; c.vy*=-1; } } _vehicle.avoid(_circles); _vehicle.update(); } } }
二、路径跟随(path following)
对于玩过星际之类游戏的朋友们,这种行为应该最熟悉了。随便选一个神族的狂热者(俗称叉叉兵),然后在几个指定的位置点击一下,它们就会沿着指定的位置来回巡逻。这就是路径跟随:角色尽可能的沿着指定的路径移动。
在算法上的处理很简单:用数组保存一组位置(每个位置其实就是一个Vector2D对象),然后加一个索引变量(指针),用于指示当前移动到了哪一个位置,最终将机车以seek行为移动以下一个位置。
但有一个要注意的细节:seek行为会让机车最终在目标位置来回反复运动停不下来,为了让代码能知道机车已经经过了当前位置,接下来应该去下一个位置,需要引入一个距离阈值,用于判断机车是否已经接近目标点。
private var _pathIndex:int=0;//路径索引 private var _pathThreshold:Number=20;//路径跟随中的距离阈值 public function set pathIndex(value:int):void { _pathIndex=value; } public function get pathIndex():int { return _pathIndex; } public function set pathThreshold(value:Number):void { _pathThreshold=value; } public function get pathThreshold():Number { return _pathThreshold; } public function set avoidDistance(value:Number):void { _avoidDistance=value; } public function get avoidDistance():Number { return _avoidDistance; } public function set avoidBuffer(value:Number):void { _avoidBuffer=value; } public function get avoidBuffer():Number { return _avoidBuffer; } //路径跟随 public function followPath(path:Array,loop:Boolean=false):void { var wayPoint:Vector2D=path[_pathIndex]; if (wayPoint==null) { return; } if (_position.dist(wayPoint)<_pathThreshold) { if (_pathIndex>=path.length-1) { if (loop) { _pathIndex=0; } } else { _pathIndex++; } } if (_pathIndex>=path.length-1&&! loop) { arrive(wayPoint); } else { seek(wayPoint); } }
测试:
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; import flash.events.MouseEvent; public class PathTest extends Sprite { private var _vehicle:SteeredVehicle; private var _path:Array; public function PathTest() { stage.align=StageAlign.TOP_LEFT; stage.scaleMode=StageScaleMode.NO_SCALE; _vehicle=new SteeredVehicle ; addChild(_vehicle); _path=new Array ; stage.addEventListener(MouseEvent.CLICK,onClick); addEventListener(Event.ENTER_FRAME,onEnterFrame); } private function onEnterFrame(event:Event):void { _vehicle.followPath(_path,true); _vehicle.update(); } private function onClick(event:MouseEvent):void { graphics.lineStyle(0,0,.25); if (_path.length==0) { graphics.moveTo(mouseX,mouseY); } graphics.lineTo(mouseX,mouseY); graphics.drawCircle(mouseX,mouseY,10); graphics.moveTo(mouseX,mouseY); _path.push(new Vector2D(mouseX,mouseY)); } } }
拿鼠标在上面随便点一下,就能看到效果了
三、群落(flock)行为
群落行为是指类似鸟群这样的复合行为。它由三个子行为组成:
分离(separation):鸟群中每个角色都试着和相邻角色保持一定的距离(即:一只鸟与其它鸟太靠近时,主动退让一定的距离,以避免碰到)
凝聚(cohesion):每个角色尽量不掉队,不落下太远(即:尽量向鸟群靠拢)
队列(alignment):每个角色尽可能与相邻角色行动于同一方向(即:每只鸟的速度方向可能不完全相同,但大致跟队伍的总体方向一致)
借用一句李建忠老师名言:原代码就是最好的设计! 直接上代码吧:
private var _inSightDist:Number=200;//视野距离 private var _tooCloseDist:Number=60;//防止群落靠得太近的安全距离 //群落行为 public function flock(vehicles:Array):void { var averageVelocity:Vector2D=_velocity.clone();//平均速度变量 var averagePosition:Vector2D=new Vector2D ;//平均位置变量 var inSightCount:int=0;//在视野中的机车数量 for (var i:int=0; i<vehicles.length; i++) { var vehicle:Vehicle=vehicles[i] as Vehicle; if (vehicle!=this&&inSight(vehicle)) { //如果其它机车在视野中 //累加速度与位置 averageVelocity=averageVelocity.add(vehicle.velocity); averagePosition=averagePosition.add(vehicle.position); //如果其它机车太靠近,则避开(即分离行为[separation]的体现) if (tooClose(vehicle)) { flee(vehicle.position); } inSightCount++; //累加在视野中的机车数 } } if (inSightCount>0) { //计算平均位置 averageVelocity=averageVelocity.divide(inSightCount); averagePosition=averagePosition.divide(inSightCount); seek(averagePosition);//向中心位置靠拢(即凝聚行为[cohesion]的体现) _steeringForce.add(averageVelocity.subtract(_velocity));//根据平均速度校准自身速度(即队列[alignment]行为的体现) } } public function set inSightDist(vaule:Number):void { _inSightDist=vaule; } public function get inSightDist():Number { return _inSightDist; } public function set tooCloseDist(value:Number):void { _tooCloseDist=value; } public function get tooCloseDist():Number { return _tooCloseDist; } //判断(身后的其它)机车是否在视野范围内 public function inSight(vehicle:Vehicle):Boolean { if (_position.dist(vehicle.position)>_inSightDist) { return false; } //---->start 下面这一段代码甚至去掉也行,不过去掉后,群落的行为将有所不同 var heading:Vector2D=_velocity.clone().normalize(); var difference:Vector2D=vehicle.position.subtract(_position); var dotProd:Number=difference.dotProd(heading); if (dotProd<0) { return false; } //<-----end return true; } public function tooClose(vehicle:Vehicle):Boolean { return _position.dist(vehicle.position)<_tooCloseDist; }
重点关注下inSight方法,它直接影响到群落的行为,示意图如下:
先检测二只鸟的距离是否足够近,然后仅关注身后的其它鸟。
测试代码:
package { import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.Event; public class FlockTest extends Sprite { private var _vehicles:Array; private var _numVehicles:int=20; public function FlockTest() { stage.align=StageAlign.TOP_LEFT; stage.scaleMode=StageScaleMode.NO_SCALE; _vehicles=new Array ; for (var i:int=0; i<_numVehicles; i++) { var vehicle:SteeredVehicle=new SteeredVehicle(Math.random()*0xffffff); vehicle.position=new Vector2D(Math.random()*stage.stageWidth,Math.random()*stage.stageHeight); vehicle.velocity=new Vector2D(Math.random()*20-10,Math.random()*20-10); vehicle.edgeBehavior=Vehicle.BOUNCE; _vehicles.push(vehicle); addChild(vehicle); } addEventListener(Event.ENTER_FRAME,onEnterFrame); } private function onEnterFrame(event:Event):void { for (var i:int=0; i<_numVehicles; i++) { _vehicles[i].flock(_vehicles); _vehicles[i].update(); } } } }
如果把inSight中检测其它鸟是否在身后的代码去掉,即简化成:
public function inSight(vehicle:Vehicle):Boolean { if (_position.dist(vehicle.position)>_inSightDist) { return false; } return true; }
预测一下最终的效果:这样相当于只要距离小于阈值的其它鸟,其速度和位置都会被计算在内,最终整个群落将始终聚集在一定的范围内,不会发生分离,从而体现出了另外一种群落效果。
最后,给出Vehicle.as及SteeredVehicle.as的完整代码
package { import flash.display.Sprite; public class Vehicle extends Sprite { //边界行为:是屏幕环绕(wrap),还是反弹{bounce} protected var _edgeBehavior:String=WRAP; //质量 protected var _mass:Number=1.0; //最大速度 protected var _maxSpeed:Number=10; //坐标 protected var _position:Vector2D; //速度 protected var _velocity:Vector2D; //边界行为常量 public static const WRAP:String="wrap"; public static const BOUNCE:String="bounce"; public function Vehicle(color:uint=0xffffff) { _position=new Vector2D ; _velocity=new Vector2D ; draw(color); } protected function draw(color:uint=0xffffff):void { graphics.clear(); graphics.lineStyle(0); graphics.beginFill(color); graphics.moveTo(10,0); graphics.lineTo(-10,5); graphics.lineTo(-10,-5); graphics.lineTo(10,0); graphics.endFill(); } public function update():void { //设置最大速度 _velocity.truncate(_maxSpeed); //根据速度更新坐标向量 _position=_position.add(_velocity); //处理边界行为 if (_edgeBehavior==WRAP) { wrap(); } else if (_edgeBehavior==BOUNCE) { bounce(); } //更新x,y坐标值 x=position.x; y=position.y; //处理旋转角度 rotation=_velocity.angle*180/Math.PI; } //反弹 private function bounce():void { if (stage!=null) { if (position.x>stage.stageWidth) { position.x=stage.stageWidth; velocity.x*=-1; } else if (position.x<0) { position.x=0; velocity.x*=-1; } if (position.y>stage.stageHeight) { position.y=stage.stageHeight; velocity.y*=-1; } else if (position.y<0) { position.y=0; velocity.y*=-1; } } } //屏幕环绕 private function wrap():void { if (stage!=null) { if (position.x>stage.stageWidth) { position.x=0; } if (position.x<0) { position.x=stage.stageWidth; } if (position.y>stage.stageHeight) { position.y=0; } if (position.y<0) { position.y=stage.stageHeight; } } } //下面的都是属性定义 public function set edgeBehavior(value:String):void { _edgeBehavior=value; } public function get edgeBehavior():String { return _edgeBehavior; } public function set mass(value:Number):void { _mass=value; } public function get mass():Number { return _mass; } public function set maxSpeed(value:Number):void { _maxSpeed=value; } public function get maxSpeed():Number { return _maxSpeed; } public function set position(value:Vector2D):void { _position=value; x=_position.x; y=_position.y; } public function get position():Vector2D { return _position; } public function set velocity(value:Vector2D):void { _velocity=value; } public function get velocity():Vector2D { return _velocity; } override public function set x(value:Number):void { super.x=value; _position.x=x; } override public function set y(value:Number):void { super.y=value; _position.y=y; } } }
package { import flash.display.Sprite; //(具有)转向(行为的)机车 public class SteeredVehicle extends Vehicle { private var _maxForce:Number=1;//最大转向力 private var _steeringForce:Vector2D;//转向速度 private var _arrivalThreshold:Number=100;//到达行为的距离阈值(小于这个距离将减速) private var _wanderAngle:Number=0; private var _wanderDistance:Number=10; private var _wanderRadius:Number=5; private var _wanderRange:Number=1; private var _avoidDistance:Number=300;//发现障碍物的有效视野 private var _avoidBuffer:Number=20;//机车在准备避开时,自身和障碍物间的预留距离。 private var _pathIndex:int=0;//路径索引 private var _pathThreshold:Number=20;//路径跟随中的距离阈值 private var _inSightDist:Number=200;//视野距离 private var _tooCloseDist:Number=60;//防止群落靠得太近的安全距离 //群落行为 public function flock(vehicles:Array):void { var averageVelocity:Vector2D=_velocity.clone();//平均速度变量 var averagePosition:Vector2D=new Vector2D ;//平均位置变量 var inSightCount:int=0;//在视野中的机车数量 for (var i:int=0; i<vehicles.length; i++) { var vehicle:Vehicle=vehicles[i] as Vehicle; if (vehicle!=this&&inSight(vehicle)) { //如果其它机车在视野中 //累加速度与位置 averageVelocity=averageVelocity.add(vehicle.velocity); averagePosition=averagePosition.add(vehicle.position); //如果其它机车太靠近,则避开(即分离行为[separation]的体现) if (tooClose(vehicle)) { flee(vehicle.position); } inSightCount++; //累加在视野中的机车数 } } if (inSightCount>0) { //计算平均位置 averageVelocity=averageVelocity.divide(inSightCount); averagePosition=averagePosition.divide(inSightCount); seek(averagePosition);//向中心位置靠拢(即凝聚行为[cohesion]的体现) _steeringForce.add(averageVelocity.subtract(_velocity));//根据平均速度校准自身速度(即队列[alignment]行为的体现) } } public function set inSightDist(vaule:Number):void { _inSightDist=vaule; } public function get inSightDist():Number { return _inSightDist; } public function set tooCloseDist(value:Number):void { _tooCloseDist=value; } public function get tooCloseDist():Number { return _tooCloseDist; } //判断(身后的其它)机车是否在视野范围内 public function inSight(vehicle:Vehicle):Boolean { if (_position.dist(vehicle.position)>_inSightDist) { return false; } //---->start 下面这一段代码甚至去掉也行,不过去掉后,群落的行为将有所不同 var heading:Vector2D=_velocity.clone().normalize(); var difference:Vector2D=vehicle.position.subtract(_position); var dotProd:Number=difference.dotProd(heading); if (dotProd<0) { return false; } //<-----end return true; } public function tooClose(vehicle:Vehicle):Boolean { return _position.dist(vehicle.position)<_tooCloseDist; } public function set pathIndex(value:int):void { _pathIndex=value; } public function get pathIndex():int { return _pathIndex; } public function set pathThreshold(value:Number):void { _pathThreshold=value; } public function get pathThreshold():Number { return _pathThreshold; } public function set avoidDistance(value:Number):void { _avoidDistance=value; } public function get avoidDistance():Number { return _avoidDistance; } public function set avoidBuffer(value:Number):void { _avoidBuffer=value; } public function get avoidBuffer():Number { return _avoidBuffer; } //路径跟随 public function followPath(path:Array,loop:Boolean=false):void { var wayPoint:Vector2D=path[_pathIndex]; if (wayPoint==null) { return; } if (_position.dist(wayPoint)<_pathThreshold) { if (_pathIndex>=path.length-1) { if (loop) { _pathIndex=0; } } else { _pathIndex++; } } if (_pathIndex>=path.length-1&&! loop) { arrive(wayPoint); } else { seek(wayPoint); } } //对象回避 public function avoid(circles:Array):void { for (var i:int=0; i<circles.length; i++) { var circle:Circle=circles[i] as Circle; var heading:Vector2D=_velocity.clone().normalize(); // 障碍物和机车间的位移向量 var difference:Vector2D=circle.position.subtract(_position); var dotProd:Number=difference.dotProd(heading); // 如果障碍物在机车前方 if (dotProd>0) { // 机车的“触角” var feeler:Vector2D=heading.multiply(_avoidDistance); // 位移在触角上的映射 var projection:Vector2D=heading.multiply(dotProd); // 障碍物离触角的距离 var dist:Number=projection.subtract(difference).length; // 如果触角(在算上缓冲后)和障碍物相交 // 并且位移的映射的长度小于触角的长度 // 我们就说碰撞将要发生,需改变转向 if (dist<circle.radius+_avoidBuffer&&projection.length<feeler.length) { // 计算出一个转90度的力 var force:Vector2D=heading.multiply(_maxSpeed); force.angle+=difference.sign(_velocity)*Math.PI/2; // 通过离障碍物的距离,调整力度大小,使之足够小但又能避开 force=force.multiply(1.0-projection.length/feeler.length); // 叠加于转向力上 _steeringForce=_steeringForce.add(force); // 刹车——转弯的时候要放慢机车速度,离障碍物越接近,刹车越狠。 _velocity=_velocity.multiply(projection.length/feeler.length); } } } } //漫游 public function wander():void { var center:Vector2D=velocity.clone().normalize().multiply(_wanderDistance); var offset:Vector2D=new Vector2D(0); offset.length=_wanderRadius; offset.angle=_wanderAngle; _wanderAngle+=Math.random()-0.5*_wanderRange; var force:Vector2D=center.add(offset); _steeringForce=_steeringForce.add(force); } public function set wanderDistance(value:Number):void { _wanderDistance=value; } public function get wanderDistance():Number { return _wanderDistance; } public function set wanderRadius(value:Number):void { _wanderRadius=value; } public function get wanderRadius():Number { return _wanderRadius; } public function set wanderRange(value:Number):void { _wanderRange=value; } public function get wanderRange():Number { return _wanderRange; } public function set arriveThreshold(value:Number):void { _arrivalThreshold=value; } public function get arriveThreshold():Number { return _arrivalThreshold; } //构造函数 public function SteeredVehicle(color:uint=0xffffff) { _steeringForce=new Vector2D ; super(color); } public function set maxForce(value:Number):void { _maxForce=value; } public function get maxForce():Number { return _maxForce; } //寻找(Seek)行为 public function seek(target:Vector2D):void { var desiredVelocity:Vector2D=target.subtract(_position); desiredVelocity.normalize(); desiredVelocity=desiredVelocity.multiply(_maxSpeed);//注:这里的_maxSpeed是从父类继承得来的 var force:Vector2D=desiredVelocity.subtract(_velocity); _steeringForce=_steeringForce.add(force); } //避开(flee)行为 public function flee(target:Vector2D):void { var desiredVelocity:Vector2D=target.subtract(_position); desiredVelocity.normalize(); desiredVelocity=desiredVelocity.multiply(_maxSpeed); var force:Vector2D=desiredVelocity.subtract(_velocity); _steeringForce=_steeringForce.subtract(force);//这是唯一也seek行为不同的地方,一句话解释:既然发现了目标,那就调头就跑吧! } //到达(arrive)行为 public function arrive(target:Vector2D):void { var desiredVelocity:Vector2D=target.subtract(_position); desiredVelocity.normalize(); var dist:Number=_position.dist(target); if (dist>_arrivalThreshold) { desiredVelocity=desiredVelocity.multiply(_maxSpeed); } else { desiredVelocity=desiredVelocity.multiply(_maxSpeed*dist/_arrivalThreshold); } var force:Vector2D=desiredVelocity.subtract(_velocity); _steeringForce=_steeringForce.add(force); } //追捕(pursue)行为 public function pursue(target:Vehicle):void { var lookAheadTime:Number=position.dist(target.position)/_maxSpeed;//假如目标不动,追捕者开足马力赶过去的话,计算需要多少时间 var predictedTarget:Vector2D=target.position.add(target.velocity.multiply(lookAheadTime)); seek(predictedTarget); } //躲避(evade)行为 public function evade(target:Vehicle):void { var lookAheadTime:Number=position.dist(target.position)/_maxSpeed; var predictedTarget:Vector2D=target.position.add(target.velocity.multiply(lookAheadTime)); flee(predictedTarget); } override public function update():void { _steeringForce.truncate(_maxForce);//限制为最大转向速度,以避免出现突然的大转身 _steeringForce=_steeringForce.divide(_mass);//惯性的体现 _velocity=_velocity.add(_steeringForce); _steeringForce=new Vector2D ; super.update(); } } }