“AS3.0高级动画编程”学习:第二章转向行为(下)

在上一篇里,我们学习了“自主角色”的一些基本行为:寻找(seek)、避开(flee)、到达(arrive)、追捕(pursue)、躲避(evade)、漫游(wander)。这一篇将继续学习其它更复杂,更高级的行为。

一、对象回避(object avoidance)

对象回避的正式解释为:角色预测出对象的行动路径,然后避开他们。

也可以通俗的描述为:假如有一个"灰太狼抓喜羊羊"的游戏场景,“喜羊羊"在草地上四处游荡的时候要尽量避免被随处找羊的"灰太狼"抓住。好象听起来并不复杂,跟前面提到的"避开(flee)"或"躲避(evade)"甚至第一章提到的碰撞检测差不多,只要调头走开、改变路线甚至检测这二者是否发生碰撞即可。

但如果仔细考虑的话,这个并不象上面想的这么简单,如果“羊”跟“狼”的距离足够远(或者“狼”运动得太慢),以至于“狼”在预测的时间范围内根本不可能抓住“羊”,那么“羊”可能根本无需调整方向(或仅需做小的调整);如果“狼”突然出现在离“羊”很近的地方,“羊”做出的反应是急转90度,换个方向跑开。(问:为什么不是转180度反方向跑呢?答:如果大家经常看动物世界里非洲草原上“猎豹追羚羊"的片段,应该就能理解了,大多数情况下,急转弯比反向跑,更能有效避开觅食者)另外,该行为的另一个特征是预测可能要发生的碰撞,而非实际发生的碰撞,所以碰撞检测也不太适合。

ok,直接看算法示意图吧:

2010071014555995.png

首先把目标(障碍)物体认为是一个有半径范围的圆形对象(当然这是一种简化问题的理想模型);

然后得出自己与目标的距离向量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;}//<-----endreturn true;}public function tooClose(vehicle:Vehicle):Boolean {return _position.dist(vehicle.position)<_tooCloseDist;}

重点关注下inSight方法,它直接影响到群落的行为,示意图如下:

2010071111402477.png

先检测二只鸟的距离是否足够近,然后仅关注身后的其它鸟。

测试代码:

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;}//<-----endreturn 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();}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/406049.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ruby scala python_解释一个基准在C,Clojure,Python,Ruby,Scala和其他

免责声明我知道人工基准是邪恶的。它们只能针对非常特定的狭窄情况显示结果。我不认为一种语言比另一种语言更好&#xff0c;因为一些愚蠢的长凳。然而&#xff0c;我不知道为什么结果是如此不同。请在底部查看我的问题。数学基准描述基准是简单的数学计算&#xff0c;找到不同…

[Python]两个list对应元素操作(相减)

两个list的对应元素操作&#xff0c;这里以相减为例&#xff1a; # codinggbkv1 [21, 34, 45] v2 [55, 25, 77]#v v2 - v1 # Error: TypeError: unsupported operand type(s) for -: list and list v list(map(lambda x: x[0]-x[1], zip(v2, v1))) print("%s\n%s\n%…

Win7启动修复MBR(Win7+Linux删除Linux后进入grub rescue的情况)

事因&#xff1a;我的笔记本原先同时安装了Win7Linux&#xff0c;昨天发现硬盘实在不够用&#xff08;才60G&#xff0c;虽然还有个500G的移动硬盘&#xff09;&#xff0c;就想把里面的Ubuntu格了。都是用虚拟机做测试的多。后来就格了Ubuntu所在的分区。重启后出现命令窗口&a…

[书籍推荐]《软件设计精要与模式(第2版)》-张逸——提高设计模式及软件设计的方法...

《软件设计精要与模式&#xff08;第2版&#xff09;》是园子里张逸大大今年4月出版的关于软件设计及设计模式方面的书&#xff0c;我是5月下旬到手&#xff0c;每天晚上看一章&#xff0c;断断续续的到昨天晚上总算把全书28章看完了&#xff0c;感受很深&#xff0c;以往在设计…

solor mysql_solr 同步 mysql

一、首先创建一个数据库和表这里创建了一个表&#xff0c;加上了测试数据&#xff0c;注意这里有一个字段来记录更新时间 update_date二、修改配置文件我们首先介绍全量同步&#xff0c;再介绍增量同步我的 solr 版本是 7.5 的&#xff0c;new_core是我创建的 core&#xff0c…

C++调用Python函数

From: http://www.flatws.cn/article/program/c/2010-08-24/9677.html Python代码在实现某些功能的时候非常方便&#xff0c;如果能够将Python代码与C程序结合起来&#xff0c;那么一定会使Problem Solving方便许多&#xff08;比如&#xff0c;游戏脚本系统&#xff09;~ 从学…

配置ubuntu使用console登录登录欢迎提示

使用ssh或console登陆成功后&#xff0c;ubuntu会顺序执行/etc/update-motd.d中的脚本 有些linux的登陆提示信息放在了/etc/ssh/sshd_config中的Banner配置项中 转载于:https://www.cnblogs.com/chen310/p/4431474.html

网络命令大全(9)--runas

Runas 允许用户用其他权限运行指定的工具和程序&#xff0c;而不是用户当前登录提供的权限。语法runas [{/profile/noprofile}] [/env] [/netonly] [/smartcard] [/showtrustlevels] [/trustlevel] /user:UserAccountName program 参数/profile 加载用户的配置文件。/profile 是…

java canvas 缩放图片_详解如何用HTML5 Canvas API控制图片的缩放变换

摘要&#xff1a;这篇HTML5栏目下的“详解如何用HTML5 Canvas API控制图片的缩放变换”&#xff0c;介绍的技术点是“html5_canvas、canvas、Html5、控制图片、api、图片”&#xff0c;希望对大家开发技术学习和问题解决有帮助。缩放变换scale(sx,sy)传入两个参数&#xff0c;分…

C语言中执行python代码或源程序文件(高级嵌入方式)

环境&#xff1a;Fedora12 Python2.6 C 1. 建立python源代码文件(del.py)&#xff1a; #!/usr/bin/env pythondef calculate(expression):try:result eval(expression)except:print("Eval Error!")return Nonereturn resultif __name__ "__main__":f …

向周鸿祎的360安全浏览器学互联网产品运营和推广

做互联网产品运营就要像周鸿祎一样&#xff0c;老周一直是运营流的大力倡导者&#xff0c;而360安全浏览器则是老周给我们上的产品运营又一课&#xff0c;醍醐灌顶&#xff0c;如梦初醒。 下图是截止到今年6月中国网民的浏览器使用情况图&#xff1a; 根据CNZZ的数据&#xff0…

java中的locksupport_java中线程的停止以及LockSupport工具类

看jstack输出的时候&#xff0c;可以发现很多状态都是TIMED_WAITING(parking)&#xff0c;如下所示&#xff1a;"http-bio-8080-exec-16" #70 daemon prio5 os_prio0 tid0x00007f6088027800 nid0x3a1f waiting on condition [0x00007f60fcd03000]java.lang.Thread.St…

React小结

1. setState setState更新状态的2种写法 (1). setState(stateChange, [callback])------对象式的setState 1.stateChange为状态改变对象(该对象可以体现出状态的更改) 2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用 (2). setState(updat…

4-17学习

//设置自动行数与字符换行 [label setNumberOfLines:0]; label.lineBreakMode UILineBreakModeWordWrap; /** 去除多余分割线 */ self.tableView.tableFooterView [[UIView alloc]init];转载于:https://www.cnblogs.com/pocket-mood/p/4435711.html

PyRun_SimpleFile()崩溃问题

From: http://blog.csdn.net/jq0123/article/details/1504406 PyRun_SimpleFile()造成程序崩溃&#xff0e;例程如下&#xff1a;#include "python.h"int main(){ Py_Initialize(); FILE * fp fopen("test.py", "r"); if …

翻译:Asp.net中多彩下拉框的实现

开发背景&#xff1a; 有人曾经要我开发一个根据不同选择而显示不同颜色的管理工具。我开始考虑利用下拉框来实现条目背景及显示颜色根据条目名称不同而进行变化&#xff0c;根据这个思路我在网上搜了半天也没有找到任何相关的解决方案&#xff0c;最后我想到了一个比当初需…

深入react技术栈(8):事件系统

我是歌谣 放弃很容易 但是坚持一定很酷 微信公众号搜索前端小歌谣获取前端知识 1合成事件的绑定方式 2合成事件的实现机制 3在React中使用原生事件 4合成事件和原生事件混用 5对比react与原生事件 文章参考深入React技术栈