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

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

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

一、对象回避(object avoidance)

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

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

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

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

img_f685ce535f3683773ae887ec7a898c8f.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;
			}
			//<-----end
			
			
			return true;
		}

		public function tooClose(vehicle:Vehicle):Boolean {
			return _position.dist(vehicle.position)<_tooCloseDist;
		}

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

img_572bfd0076b2898d03b22f78f369e1e1.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;
			}
			//<-----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();
		}
	}
}
目录
相关文章
|
2月前
|
关系型数据库 MySQL PHP
PHP编程:从基础到高级的旅程
PHP,一种流行的服务器端脚本语言,因其在Web开发中的广泛应用而受到许多开发者的青睐。本文将从PHP的基本概念和语法入手,逐步深入到面向对象编程、数据库操作以及框架使用等高级主题。无论你是PHP新手还是有一定经验的开发者,这篇文章都将为你提供有价值的参考和学习路径。
38 2
|
3月前
|
C# Windows 监控
WPF应用跨界成长秘籍:深度揭秘如何与Windows服务完美交互,扩展功能无界限!
【8月更文挑战第31天】WPF(Windows Presentation Foundation)是 .NET 框架下的图形界面技术,具有丰富的界面设计和灵活的客户端功能。在某些场景下,WPF 应用需与 Windows 服务交互以实现后台任务处理、系统监控等功能。本文探讨了两者交互的方法,并通过示例代码展示了如何扩展 WPF 应用的功能。首先介绍了 Windows 服务的基础知识,然后阐述了创建 Windows 服务、设计通信接口及 WPF 客户端调用服务的具体步骤。通过合理的交互设计,WPF 应用可获得更强的后台处理能力和系统级操作权限,提升应用的整体性能。
98 0
|
3月前
|
自然语言处理 前端开发 JavaScript
前端进阶必读:JS闭包深度解析,掌握这一特性,你的代码将焕然一新!
【8月更文挑战第23天】闭包是JavaScript的一项高级功能,让函数能够访问和操作外部函数作用域中的变量。本文深入解析闭包概念、组成及应用场景。闭包由函数及其词法环境构成,通过在一个函数内定义另一个函数来创建。它有助于封装私有变量、维持状态和动态生成函数。然而,不当使用闭包可能导致内存泄漏或性能问题。掌握闭包对于实现模块化代码和成为优秀前端开发者至关重要。
40 0
|
设计模式 分布式计算 算法
一些令人惊叹的编程方式:
一些令人惊叹的编程方式:
111 1
|
安全
游戏开发零基础入门教程(12):从想法到设计的过程
一个游戏通常开始于一个想法,这个想法可以是千奇百怪的,可以是五花八门的,甚至可以是可笑的。不论如何有一个想法,是一个游戏的开端。当你有了一个想法了以后,如果你希望它最终能够变成一个真正的游戏,那么你就必须要继续往前走,反复的思考,雕琢你的粗糙的想法,将它细化,形成一份具体的可行的游戏设计方案。
122 0
|
XML 移动开发 前端开发
这16种原生函数和属性的区别,你真的知道吗? 精心收集,高级前端必备知识,快快打包带走
原生内置了很多API, 作用类似,却也有差千差万别,了解其区别,掌握前端基础,是修炼上层,成为前端高级工程师的必备知识,让我们一起来分类归纳,一起成长吧。
199 0
这16种原生函数和属性的区别,你真的知道吗? 精心收集,高级前端必备知识,快快打包带走
|
自然语言处理 搜索推荐
革命性的基于知识编程语言Wolfram发布第一个演示
我们曾在去年年底时介绍过 Stephen Wolfram 这位传奇人物雄心勃勃的新计划,一种将颠覆编程甚至颠覆世界的全新计算模式—Wolfram 语言。2 个月过去之后,这门语言推出了第一个演示视频。虽然视频只有 10 几分钟,但看到那简洁明了的代码以及丰富绚丽的结果之后,你一定会感到极大的震撼。
199 0
革命性的基于知识编程语言Wolfram发布第一个演示
|
人工智能
带你读《少儿人工智能趣味入门动画与游戏编程一本通》之二:角色的基础:“运动”“外观”“声音”模块
Scratch是图形化的编程语言,它具有学习环境趣味性强、操作简单且直观等特点,很好适合6-12岁的孩子学习。本书是立足于Scratch 3.0版本的少儿编程入门书,能让孩子轻松愉快地掌握编程技能,锻炼和提高思维能力和创造力,为迎接人工智能时代的到来做好准备。本书以对Scratch中积木块的分类讲解作为主线,并将编程的核心思想融入大量精心设计的案例,让孩子在实际动手操作中更直观、更深刻地理解不同积木块的运用。本书对积木块的功能和用法解释详尽,语言通俗易懂,能够减少孩子对编程的畏惧心理,没有编程基础的家长也能陪伴孩子一起阅读,在融洽的亲子互动氛围中,自信、愉快地完成学习。
下一篇
无影云桌面