“AS3.0高级动画编程”学习:第二章转向行为(上)
因為這一章的內容基本上都是涉及向量的,先來一個2D向量類:Vector2D.as (再次強烈建議不熟悉向量運算的童鞋,先回去惡補一下高等數學-07章空間解釋幾何與向量代數.pdf)
package {import flash.display.Graphics;public class Vector2D {private var _x:Number;private var _y:Number;//構造函數public function Vector2D(x:Number=0,y:Number=0) {_x=x;_y=y;}//繪制向量(以便于顯示)public function draw(graphics:Graphics,color:uint=0):void {graphics.lineStyle(0,color);graphics.moveTo(0,0);graphics.lineTo(_x,_y);}//克隆對象public function clone():Vector2D {return new Vector2D(x,y);}//位置歸零public function zero():Vector2D {_x=0;_y=0;return this;}//是否在零位置public function isZero():Boolean {return _x==0&&_y==0;}//獲得向量的角度public function get angle():Number {return Math.atan2(_y,_x);}//設置向量的模(即大小)public function set length(value:Number):void {var a:Number=angle;_x=Math.cos(a)*value;_y=Math.sin(a)*value;}//獲取向量大小的平方public function get lengthSQ():Number {return _x*_x+_y*_y;}//獲取向量的模(即大小)public function get length():Number {return Math.sqrt(lengthSQ);}//設置向量的角度public function set angle(value:Number):void {var len:Number=length;_x=Math.cos(value)*len;_y=Math.sin(value)*len;} //截斷向量(設置向量模最大值)public function truncate(max:Number):Vector2D {length=Math.min(max,length);return this;}//交換x,y坐標public function reverse():Vector2D {_x=- _x;_y=- _y;return this;}//定義二個向量的加法運算public function add(v2:Vector2D):Vector2D {return new Vector2D(_x+v2.x,_y+v2.y);}//定義二個向量的減法運算public function subtract(v2:Vector2D):Vector2D {return new Vector2D(_x-v2.x,_y-v2.y);}//向量模的乘法運算public function multiply(value:Number):Vector2D {return new Vector2D(_x*value,_y*value);}//向量模的除法運算public function divide(value:Number):Vector2D {return new Vector2D(_x/value,_y/value);}//判定二個向量(坐標)是否相等public function equals(v2:Vector2D):Boolean {return _x==v2.x&&_y==v2.y;}//設置x軸坐標public function set x(value:Number):void {_x=value;}//返回x軸坐標public function get x():Number {return _x;}//設置y軸坐標public function set y(value:Number):void {_y=value;}//返回y軸坐標public function get y():Number {return _y;}//單位化向量(即設置向量的模為1,不過這里用了一種更有效率的除法運算,從而避免了lengh=1帶來的三角函數運算)public function normalize():Vector2D {if (length==0) {_x=1;return this;}//建議大家畫一個基本的3,4,5勾股定理的直角三角形即可明白下面的代碼var len:Number=length;_x/=len;_y/=len;return this;} //判定向量是否為單位向量public function isNormalized():Boolean {return length==1.0;}//點乘(即向量的點積)public function dotProd(v2:Vector2D):Number {return _x*v2.x+_y*v2.y;}//叉乘(即向量的矢量積)public function crossProd(v2:Vector2D):Number {return _x*v2.y-_y*v2.x;}//返回二個向量之間的夾角public static function angleBetween(v1:Vector2D,v2:Vector2D):Number {if (! v1.isNormalized()) {v1=v1.clone().normalize();}if (! v2.isNormalized()) {v2=v2.clone().normalize();}return Math.acos(v1.dotProd(v2));//建議先回顧一下http://www.cnblogs.com/yjmyzz/archive/2010/06/06/1752674.html中提到的到夾角公式}//判定給定的向量是否在本向量的左側或右側,左側返回-1,右側返回1public function sign(v2:Vector2D):int {return perp.dotProd(v2)<0?-1:1;}//返回與本向量垂直的向量(即自身順時針旋轉90度,得到一個新向量)public function get perp():Vector2D {return new Vector2D(- y,x);//建議回顧一下"坐標旋轉"}//返回二個矢量末端頂點之間的距離平方public function distSQ(v2:Vector2D):Number {var dx:Number=v2.x-x;var dy:Number=v2.y-y;return dx*dx+dy*dy;}//返回二個矢量末端頂點之間的距離public function dist(v2:Vector2D):Number {return Math.sqrt(distSQ(v2));}//toString方法public function toString():String {return "[Vector2D (x:"+_x+", y:"+_y+")]";}}
}
有幾個地方稍加解釋:
1、向量夾角的計算
上圖為向量的夾角公式,再來對照一下代碼部分:
public static function angleBetween(v1:Vector2D,v2:Vector2D):Number {if (! v1.isNormalized()) {v1=v1.clone().normalize();}if (! v2.isNormalized()) {v2=v2.clone().normalize();}return Math.acos(v1.dotProd(v2));
}
首先對向量v1,v2做了單位化處理,使其變成(模為1的)單位向量,這樣夾角公式中的|a|×|b|(即分母)自然也就是1,公式演變成cos(θ)=a.b(即夾角余弦 等于 向量a與b的點乘),然后再對其取反余弦Math.acos,最終得到夾角
2、垂直向量的取得
上圖是坐標(順時針)旋轉的標準公式,如果把α設置為90度,則
,即:
public function get perp():Vector2D {return new Vector2D(- y,x);
}
3、判定其它向量是在自身的左側還是右側
如上圖,先取得A的垂直向量,然后計算其它向量跟垂直向量的點積(點乘的公式,在物理上的表現之一為 W = |F|*|S|Cos(θ) ),如果其它向量與該垂直向量的夾角小于90度,點乘的值必為正,反之為負,所以也就能判定左右了(注意:這里的左右是指人站在坐標原點,順著向量A的方向來看的)
再來定義一個機車類Vehicle.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() {_position=new Vector2D ;_velocity=new Vector2D ;draw();}protected function draw():void {graphics.clear();graphics.lineStyle(0);graphics.moveTo(10,0);graphics.lineTo(-10,5);graphics.lineTo(-10,-5);graphics.lineTo(10,0);}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;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;public class VehicleTest extends Sprite {private var _vehicle:Vehicle;public function VehicleTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_vehicle=new Vehicle ;addChild(_vehicle);_vehicle.position=new Vector2D(100,100);_vehicle.velocity.length=5;_vehicle.velocity.angle=Math.PI/4;//45度addEventListener(Event.ENTER_FRAME,onEnterFrame);}private function onEnterFrame(event:Event):void {_vehicle.update();}}
}
OK,現在可以進入正題了:(下面是從原書上直接抄過來的)
轉向行為(steering behaviors)這一術語,指的是一系列使對象行動起來像似長有智商的算法。這些行為都歸于人工智能或人工生命一類,是讓對象呈現出擁有生命一般,對如何移動到目的地、捕捉或逃避其它對象、避開障礙物、尋求路徑等做出因地適宜的決定。??
一、尋找行為(Seek)
簡單點講,就是角色本身試圖移動(包括轉向)到目標位置(這個位置可能是固定的,也可能是移動的)。
先定義一個從Vehicle繼承的子類:具有轉向能力的機車SteeredVehicle.as
package {import flash.display.Sprite;//(具有)轉向(行為的)機車public class SteeredVehicle extends Vehicle {private var _maxForce:Number=1;//最大轉向力private var _steeringForce:Vector2D;//轉向速度public function SteeredVehicle() {_steeringForce = new Vector2D();super();}public function set maxForce(value:Number):void {_maxForce=value;}public function get maxForce():Number {return _maxForce;}override public function update():void {_steeringForce.truncate(_maxForce);//限制為最大轉向速度,以避免出現突然的大轉身_steeringForce=_steeringForce.divide(_mass);//慣性的體現_velocity=_velocity.add(_steeringForce);_steeringForce = new Vector2D();super.update();}}
}
代碼不難理解:僅增加了最大轉向力maxForce(主要是為了防止機車一瞬間就突然移動到目標位置,會引起視覺上的動畫不連貫);另外對update做了重載處理,在更新機車x,y坐標及朝向(即rotation)之前,累加了轉向速度并考慮到了物體的慣性。
再來考慮“尋找(seek)”行為,先看下面這張圖:
根據向量運算,可以先得到機車期望的理想速度(desireVolocity)--注:如果用這個速度行駛,物體立馬就能到達目標點。當然我們要體現物體是逐漸靠近目標點的,所以顯然不可能用理想速度前行,而是要計算出轉向速度force,最終再把轉向速度force疊加到自身的速度_velocity上,這樣機車就能不斷向目標點移動了。
//尋找(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);
}
把這段代碼加入到SteeredVehicle.as中就能讓SteeredVehicle類具有seek行為,下面是測試代碼:
package {import SteeredVehicle;import Vector2D;import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;public class SeekTest extends Sprite {private var _vehicle:SteeredVehicle;public function SeekTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_vehicle = new SteeredVehicle();addChild(_vehicle);addEventListener(Event.ENTER_FRAME, onEnterFrame);}private function onEnterFrame(event:Event):void {_vehicle.seek(new Vector2D(mouseX, mouseY));//以當前鼠標位置為目標點_vehicle.update();}}
}
二、避開(flee)行為
它跟尋找(seek)行為正好是相反的,可以通俗的理解為:“既然發現了目標,那么就調頭逃跑吧”,所以代碼上只要改一行即可
//避開(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行為不同的地方,一句話解釋:既然發現了目標,那就調頭就跑吧!
}
同樣,把上述代碼加入到SteeredVehicle.as中就能讓SteeredVehicle類具有flee行為,測試代碼:
package {import SteeredVehicle;import Vector2D;import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;public class FleeTest extends Sprite {private var _vehicle:SteeredVehicle;public function FleeTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_vehicle = new SteeredVehicle();_vehicle.position = new Vector2D(stage.stageWidth/2,stage.stageHeight/2);_vehicle.edgeBehavior = Vehicle.BOUNCE;addChild(_vehicle);addEventListener(Event.ENTER_FRAME, onEnterFrame);}private function onEnterFrame(event:Event):void {_vehicle.flee(new Vector2D(mouseX, mouseY));//避開鼠標當前位置_vehicle.update();}}
}
seek行為與flee行為組合起來,可以完成類似“警察抓小偷”的效果
package {import SteeredVehicle;import Vector2D;import Vehicle;import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;import flash.text.TextField;import flash.text.TextFormat;public class SeekFleeTest1 extends Sprite {private var _seeker:SteeredVehicle;//尋找者(可理解為:警察)private var _fleer:SteeredVehicle;//躲避者(事理解為:小偷)private var _seekerSpeedSlider:SimpleSlider ;//警察的最大速度控制滑塊private var _txtSeekerMaxSpeed:TextField;private var _fleerSpeedSlider:SimpleSlider ;//小偷的最大速度控制滑塊private var _txtFleerMaxSpeed:TextField;public function SeekFleeTest1() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_seeker = new SteeredVehicle(0xff0000);_seeker.position=new Vector2D();_seeker.edgeBehavior=Vehicle.BOUNCE;addChild(_seeker);_seeker.maxSpeed = 5;_fleer = new SteeredVehicle(0x0000ff);_fleer.position=new Vector2D(stage.stageWidth*Math.random(),stage.stageHeight*Math.random());_fleer.edgeBehavior=Vehicle.BOUNCE;addChild(_fleer);addEventListener(Event.ENTER_FRAME, onEnterFrame);addSpeedControl();}//添加速度控制組件private function addSpeedControl():void{_seekerSpeedSlider = new SimpleSlider(5,25,10);_seekerSpeedSlider.rotation = 90;_seekerSpeedSlider.x = 150;_seekerSpeedSlider.y = 20;_seekerSpeedSlider.backColor = _seekerSpeedSlider.backBorderColor = _seekerSpeedSlider.handleColor = _seekerSpeedSlider.handleBorderColor = 0xff0000;addChild(_seekerSpeedSlider);_seekerSpeedSlider.addEventListener(Event.CHANGE,onSeekerSpeedChange); _txtSeekerMaxSpeed = new TextField();var _tfseeker:TextFormat = new TextFormat();_tfseeker.color = 0xff0000;_txtSeekerMaxSpeed.defaultTextFormat = _tfseeker;_txtSeekerMaxSpeed.text = "10";addChild(_txtSeekerMaxSpeed);_txtSeekerMaxSpeed.y = _seekerSpeedSlider.y -6;_txtSeekerMaxSpeed.x = _seekerSpeedSlider.x +3;_fleerSpeedSlider = new SimpleSlider(5,25,10);_fleerSpeedSlider.rotation = 90;_fleerSpeedSlider.x = 480;_fleerSpeedSlider.y = 20;_fleerSpeedSlider.backColor = _fleerSpeedSlider.backBorderColor = _fleerSpeedSlider.handleColor = _fleerSpeedSlider.handleBorderColor = 0x0000ff;addChild(_fleerSpeedSlider);_fleerSpeedSlider.addEventListener(Event.CHANGE,onFleerSpeedChange); _txtFleerMaxSpeed = new TextField(); var _tffleer:TextFormat = new TextFormat();_tffleer.color = 0x0000ff; _txtFleerMaxSpeed.defaultTextFormat = _tffleer;_txtFleerMaxSpeed.text = "10";addChild(_txtFleerMaxSpeed);_txtFleerMaxSpeed.y = _fleerSpeedSlider.y -6;_txtFleerMaxSpeed.x = _fleerSpeedSlider.x +3;}function onSeekerSpeedChange(e:Event):void{_seeker.maxSpeed = _seekerSpeedSlider.value;_txtSeekerMaxSpeed.text = _seekerSpeedSlider.value.toString();}function onFleerSpeedChange(e:Event):void{_fleer.maxSpeed = _fleerSpeedSlider.value;_txtFleerMaxSpeed.text = _fleerSpeedSlider.value.toString();}private function onEnterFrame(event:Event):void {_seeker.seek(_fleer.position);//警察 抓 小偷_fleer.flee(_seeker.position);//小偷 躲 警察_seeker.update();_fleer.update();} }
}
調整紅色滑塊和藍色滑塊,可改變seeker與fleer的最大速度。(注:代碼中的SimpleSlider在Flash/Flex學習筆記(46):正向運動學中能找到) 如果愿意,您還可以加入碰撞檢測,比如當“警察”抓住“小偷”時,顯示一個提示:“小樣,我抓住你了!”
如果加入更多的物體,比如A,B,C三個,讓A追逐B同時躲避C,B追逐C同時躲避A,C追逐A同時躲避B,將是下面這副模樣:
package {import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;public class SeekFleeTest2 extends Sprite {private var _vehicleA:SteeredVehicle;private var _vehicleB:SteeredVehicle;private var _vehicleC:SteeredVehicle;public function SeekFleeTest2() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_vehicleA=new SteeredVehicle(0xff0000) ;_vehicleA.position=new Vector2D(stage.stageWidth*Math.random(),stage.stageHeight*Math.random());_vehicleA.edgeBehavior=Vehicle.BOUNCE;addChild(_vehicleA);_vehicleB=new SteeredVehicle(0x0000ff) ;_vehicleB.position=new Vector2D(stage.stageWidth*Math.random(),stage.stageHeight*Math.random());_vehicleB.edgeBehavior=Vehicle.BOUNCE;addChild(_vehicleB);_vehicleC=new SteeredVehicle(0x00ff00) ;_vehicleC.position=new Vector2D(stage.stageWidth*Math.random(),stage.stageHeight*Math.random());_vehicleC.edgeBehavior=Vehicle.BOUNCE;addChild(_vehicleC);addEventListener(Event.ENTER_FRAME,onEnterFrame);}private function onEnterFrame(event:Event):void {//A追求B,躲避C_vehicleA.seek(_vehicleB.position);_vehicleA.flee(_vehicleC.position); //B追求C,躲避A_vehicleB.seek(_vehicleC.position);_vehicleB.flee(_vehicleA.position);//C追求A,躲避B_vehicleC.seek(_vehicleA.position);_vehicleC.flee(_vehicleB.position);_vehicleA.update();_vehicleB.update();_vehicleC.update();}}
}
Flash動畫的邊界,猶如人世間的一張網,將你我他都罩住,我們都在追尋一些東西,同時也在逃避一些東西,于是乎:愛我的人我不愛,我愛的人愛別人······ 現實如此,程序亦如此。
三、到達(arrive)行為
到達行為其實跟尋找行為很相似,區別在于:尋找行為發現目標后,不斷移動靠近目標,但速度不減,所以會出現最終一直在目標附近二頭來回跑,停不下來。而到達行為在靠近目標時會慢慢停下來,最終停在目標點。(這個咋這么熟悉?對了,這就是以前學習過來的緩動動畫,詳見Flash/Flex學習筆記(38):緩動動畫)
//到達(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);
}
當然這里的比例因子:_arrivalThreshold需要先定義,同時為了方便動態控制,還要對外以屬性的形式暴露出來
private var _arrivalThreshold:Number=100;//到達行為的距離閾值(小于這個距離將減速)public function set arriveThreshold(value: Number):void {_arrivalThreshold=value;
}public function get arriveThreshold():Number {return _arrivalThreshold;
}
把上面這二段代碼加入SteeredVehicle.as中,然后測試一把:
package {import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;public class ArriveTest extends Sprite {private var _vehicle:SteeredVehicle;public function ArriveTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_vehicle=new SteeredVehicle ;addChild(_vehicle);addEventListener(Event.ENTER_FRAME,onEnterFrame);}private function onEnterFrame(event:Event):void {_vehicle.arrive(new Vector2D(mouseX,mouseY));_vehicle.update();}}
}
四、追捕(pursue)行為
追捕跟尋找很類似,不過區別在于:尋找(seek)是發現目標后,以預定的速度向目標靠攏,不管目標跑得多快還是多慢,所以如果目標比尋找者(seeker)要移動得快,seeker永遠是追不上的;而追捕行為是要在目標前進的路上,提前把目標攔截到,也可以理解為先預定一個(target前進路線上的)目標位置,然后再以尋找行為接近該位置,所以只要預定目標位置計算得合理,就算追捕者的速度要慢一點兒,也是完全有可能把目標給抓住的。
代碼:
//追捕(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);
}
解釋:假如目標不動的話,我們先計算二者之間的距離,然后以最大速度狂奔過去,大概需要lookAheadTime這么長時間,然后根據這個時間,得到預定的目標位置,再以該位置為目標,尋找(seek)過去。(當然這種算法并不精確,但是處理起來比較簡單,重要的是:配合Enter_Frame事件后,它確實管用!)
測試代碼:
package {import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;import flash.events.MouseEvent;import flash.text.TextField;public class PursueTest extends Sprite {private var _seeker:SteeredVehicle;private var _pursuer:SteeredVehicle;private var _target:Vehicle;private var _isRun:Boolean = false;private var _text:TextField;public function PursueTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_seeker = new SteeredVehicle(0x0000ff); addChild(_seeker);_pursuer = new SteeredVehicle(0xff0000); addChild(_pursuer);_target = new Vehicle(0x000000); _target.velocity.length=15;//目標對象跑得快一點,這樣才能看出區別addChild(_target); _seeker.edgeBehavior = _target.edgeBehavior = _pursuer.edgeBehavior = Vehicle.BOUNCE;stage.addEventListener(MouseEvent.CLICK,stageClick);_text = new TextField();_text.text = "點擊鼠標開始演示";_text.height = 20;_text.width = 100; _text.x = stage.stageWidth/2 - _text.width/2;_text.y = stage.stageHeight/2 - _text.height/2;addChild(_text);}private function onEnterFrame(event:Event):void {_seeker.seek(_target.position);_seeker.update();_pursuer.pursue(_target);_pursuer.update();_target.update();}private function stageClick(e:MouseEvent):void{ if (!_isRun){_target.position=new Vector2D(stage.stageWidth/2,stage.stageHeight/2);addEventListener(Event.ENTER_FRAME, onEnterFrame); _isRun = true;removeChild(_text); }else{removeEventListener(Event.ENTER_FRAME, onEnterFrame);_isRun = false;_target.position = _seeker.position = _pursuer.position = new Vector2D(0,0);addChild(_text);_text.text = "點擊鼠標重新開始"; } }}
}
這里為了區別“追捕行為”與"尋找行為",我們同時加入了追捕者(_pursuer-紅色)與尋找者(_seeker-藍色),通過下面的演示可以看出,(紅色)追捕者憑借算法上的優勢,始終能更接近目標。
五、躲避(evade)行為
躲避跟追捕正好相反,可以理解為:如果我有可能擋在目標前進的路線上了,我就提前回避,讓開這條道。(俗話:好狗不擋道)
//躲避(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);//僅僅只是這里改變了而已
}
把前面學到的這些個行為放在一起亂測一通吧:
package {import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;import flash.events.MouseEvent;import flash.text.TextField;public class PursueEvadeTest extends Sprite {private var _pursuer:SteeredVehicle;private var _evader:SteeredVehicle;private var _target:SteeredVehicle;private var _seeker:SteeredVehicle;private var _fleer:SteeredVehicle;private var _pursuer2:SteeredVehicle;private var _evader2:SteeredVehicle;private var _text:TextField;private var _isRun:Boolean = false;public function PursueEvadeTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_pursuer=new SteeredVehicle(0xff0000);addChild(_pursuer);_evader=new SteeredVehicle(0x00ff00); addChild(_evader);_target=new SteeredVehicle(0x000000);_target.velocity.length=15; addChild(_target);_seeker=new SteeredVehicle(0xff00ff);addChild(_seeker);_fleer=new SteeredVehicle(0xffff00); addChild(_fleer);_pursuer2 = new SteeredVehicle();addChild(_pursuer2);_evader2 = new SteeredVehicle(); addChild(_evader2);_evader2.edgeBehavior = _pursuer2.edgeBehavior = _target.edgeBehavior = _evader.edgeBehavior = _pursuer.edgeBehavior = _fleer.edgeBehavior = _seeker.edgeBehavior = Vehicle.BOUNCE;_text = new TextField();_text.text="點擊鼠標開始演示";_text.height=20;_text.width=100;_text.x=stage.stageWidth/2-_text.width/2;_text.y=stage.stageHeight/2-_text.height/2;addChild(_text);stage.addEventListener(MouseEvent.CLICK,stageClick);}private function stageClick(e:MouseEvent):void {if (! _isRun) {_target.position=new Vector2D(stage.stageWidth/2,stage.stageHeight/2);_fleer.position=new Vector2D(400,300);_evader2.position=new Vector2D(400,200);_evader.position=new Vector2D(400,100);addEventListener(Event.ENTER_FRAME, onEnterFrame);_isRun=true;removeChild(_text);} else {_pursuer2.position =_evader2.position = _evader.position = _pursuer.position = _target.position=_seeker.position=_pursuer.position= new Vector2D(0,0);removeEventListener(Event.ENTER_FRAME, onEnterFrame);_isRun=false; addChild(_text);_text.text="點擊鼠標重新開始";}}private function onEnterFrame(event:Event):void {_seeker.seek(_target.position);_fleer.flee(_target.position);_pursuer.pursue(_target);_evader.evade(_target);_pursuer2.pursue(_evader2);_evader2.evade(_pursuer2);_target.update();_seeker.update();_pursuer.update();_fleer.update();_evader.update();_pursuer2.update();_evader2.update();}}
}
對于這個示例,也許看不出”避開(flee)“與“躲避(evade)”的區別,反正都是不擋道嘛,沒關系,下面會有機會看到區別的
六、漫游(wander)行為
顧名思義,就是要讓物體在屏幕上漫不經心的閑逛。可能大家首先想到的是讓速度每次隨機改變一些(類似布朗運動),但很快您就會發現這樣做的結果:物體象抽風一樣在屏幕上亂動,一點都不連續,體現不出“漫不經心”閑逛的特征。所以我們需要一種更為平滑的處理算法:
如上圖,先在物體運動的正前方,畫一個指定半徑的圈,然后讓向量offset每次隨機旋轉一個小小的角度,這樣最終能得到轉向力向量force=center+offset,最終把向量force疊加到物體的速度上即可.
private var _wanderAngle:Number=0;
private var _wanderDistance:Number=10;
private var _wanderRadius:Number=5;
private var _wanderRange:Number=1;//漫游
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;
}
雖然這次增加的代碼看上去比較多,但是大部分是用于封裝屬性的,關鍵的代碼并不難理解。好了,做下基本測試:
package { import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;public class WanderTest extends Sprite {private var _vehicle:SteeredVehicle;public function WanderTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE;_vehicle = new SteeredVehicle();_vehicle.maxSpeed = 3;_vehicle.wanderDistance = 50;_vehicle.position=new Vector2D(200,200);//_vehicle.edgeBehavior = Vehicle.BOUNCE;addChild(_vehicle);addEventListener(Event.ENTER_FRAME, onEnterFrame);}private function onEnterFrame(event:Event):void {_vehicle.wander();_vehicle.update();}}
}
如果讓漫游行為跟前面提到的行為組合,效果會更好一些:
package {import flash.display.Sprite;import flash.display.StageAlign;import flash.display.StageScaleMode;import flash.events.Event;import flash.events.MouseEvent;import flash.text.TextField;public class FleeEvadeWanderTest extends Sprite {private var _pursuer:SteeredVehicle;private var _evader:SteeredVehicle;private var _target:SteeredVehicle;private var _seeker:SteeredVehicle;private var _fleer:SteeredVehicle; private var _text:TextField;private var _isRun:Boolean = false;public function FleeEvadeWanderTest() {stage.align=StageAlign.TOP_LEFT;stage.scaleMode=StageScaleMode.NO_SCALE; _evader=new SteeredVehicle(0x00ff00);//躲避者(綠色) addChild(_evader);_target=new SteeredVehicle(0x000000);//目標(黑色) _target.velocity.length = 20;addChild(_target); _fleer=new SteeredVehicle(0xffff00);//避開者(黃色) addChild(_fleer);_target.edgeBehavior = _evader.edgeBehavior = _fleer.edgeBehavior = Vehicle.BOUNCE;_text = new TextField();_text.text="點擊鼠標開始演示";_text.height=20;_text.width=100;_text.x=stage.stageWidth/2-_text.width/2;_text.y=stage.stageHeight/2-_text.height/2;addChild(_text);stage.addEventListener(MouseEvent.CLICK,stageClick);}private function stageClick(e:MouseEvent):void {if (! _isRun) {_target.position=new Vector2D(50,50);_evader.position = _fleer.position=new Vector2D(stage.stageWidth/2,stage.stageHeight/2); addEventListener(Event.ENTER_FRAME, onEnterFrame);_isRun=true;removeChild(_text);} else {_evader.position = _target.position=_fleer.position=new Vector2D(0,0);removeEventListener(Event.ENTER_FRAME, onEnterFrame);_isRun=false; addChild(_text);_text.text="點擊鼠標重新開始";}}private function onEnterFrame(event:Event):void {_target.wander(); _fleer.flee(_target.position); _evader.evade(_target);_target.update(); _fleer.update();_evader.update();}}
}
前面提到了flee(避開)與evade(躲避)很難看出區別,但在這個示例里,大概能看出一些細節上的些許不同:flee算法是以目標當前的位置為做基點避開的,而evade是以目標前進方向上未來某個時時間點的位置做為基點避開的,所以相對而言,(綠色的)evader更有前瞻性--即所謂的先知先覺,而(黃色的)fleer只是見知見覺,最終在視覺效果上,evader總是希望跟目標以反方向逃開(這樣能躲得更遠,更安全一點)。
注:博客園的nasa(微軟MVP),對于本章內容也有相應的Sliverlight實現,推薦大家對照閱讀。
?
轉載于:https://www.cnblogs.com/yjmyzz/archive/2010/07/07/1773140.html
總結
以上是生活随笔為你收集整理的“AS3.0高级动画编程”学习:第二章转向行为(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 萌二小浣熊是谁画的啊?
- 下一篇: 上海欢乐谷大洋历险身高限制