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

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

原作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com

一、对象回避(object avoidance)

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

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

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

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

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

然后得出自己与目标的距离向量difference;

接下来将自身的速度向量单位化,得到单位向量header(即长度为1,且与速度同方向的向量);

计算header与difference的点积(也叫点乘、内积、标量积),得到一个值dotProd = |header| * |difference| * cos(θ) = |difference| * cos(θ) (注:header为单位向量,大小为1,所以可省去),很明显如果该值小于0,则表示障碍物就在前方,准备做调整;

将header放大一个系数,模拟出自身的触角feeler(相当于物体自身向前方伸出去一个触须试探试探);

将difference投影在feeler上,得到向量projection,并进一步得到距离dist(即projection末端与difference末端的距离);

如果dist小于圆半径,且projection长度小于feeler的长度(即触角碰到了目标了),则转90度逃开;

 1 private var _avoidDistance:Number=300;//发现障碍物的有效视野
 2 private var _avoidBuffer:Number=20;//机车在准备避开时,自身和障碍物间的预留距离。
 3
 4 public function set avoidDistance(value:Number):void {
 5     _avoidDistance=value;
 6 }
 7 public function get avoidDistance():Number {
 8     return _avoidDistance;
 9 }
10
11 public function set avoidBuffer(value:Number):void {
12     _avoidBuffer=value;
13 }
14 public function get avoidBuffer():Number {
15     return _avoidBuffer;
16 }
17
18 //对象回避
19 public function avoid(circles: Array):void {
20     for (var i: int=0; i < circles.length; i++) {
21         var circle:Circle=circles[i] as Circle;
22         var heading:Vector2D=_velocity.clone().normalize();
23         // 障碍物和机车间的位移向量
24         var difference:Vector2D=circle.position.subtract(_position);
25         var dotProd:Number=difference.dotProd(heading);
26         // 如果障碍物在机车前方
27         if (dotProd>0) {
28             // 机车的“触角”
29             var feeler:Vector2D=heading.multiply(_avoidDistance);
30             // 位移在触角上的映射
31             var projection:Vector2D=heading.multiply(dotProd);
32             // 障碍物离触角的距离
33             var dist:Number=projection.subtract(difference).length;
34             // 如果触角(在算上缓冲后)和障碍物相交
35             // 并且位移的映射的长度小于触角的长度
36             // 我们就说碰撞将要发生,需改变转向
37             if (dist < circle.radius + _avoidBuffer && projection.length < feeler.length) {
38                 // 计算出一个转90度的力
39                 var force:Vector2D=heading.multiply(_maxSpeed);
40                 force.angle+=difference.sign(_velocity)*Math.PI/2;
41                 // 通过离障碍物的距离,调整力度大小,使之足够小但又能避开
42                 force=force.multiply(1.0-projection.length/feeler.length);
43                 // 叠加于转向力上
44                 _steeringForce=_steeringForce.add(force);
45                 // 刹车——转弯的时候要放慢机车速度,离障碍物越接近,刹车越狠。
46                 _velocity=_velocity.multiply(projection.length/feeler.length);
47             }
48         }
49     }
50 }

将以上代码加入SteeredVehicle.as,当然,在测试前还要有一个Circle类来模拟障碍物

 1 package {
 2     import flash.display.Sprite;
 3     public class Circle extends Sprite {
 4         private var _radius:Number;
 5         private var _color:uint;
 6         private var _vx:Number;
 7         private var _vy:Number;
 8
 9         public function Circle(radius:Number, color:uint = 0x000000) {
10             _radius=radius;
11             _color=color;
12             graphics.lineStyle(0, _color);
13             graphics.beginFill(_color);
14             graphics.drawCircle(0, 0, _radius);
15             graphics.endFill();
16         }
17
18         public function get radius():Number {
19             return _radius;
20         }
21
22         public function get position():Vector2D {
23             return new Vector2D(x, y);
24         }
25
26         public function get vx():Number {
27             return _vx;
28         }
29
30         public function set vx(value:Number):void {
31             _vx = value;
32         }
33
34         public function get vy():Number {
35             return _vy;
36         }
37
38         public function set vy(value:Number):void {
39             _vy = value;
40         }
41     }
42 }

测试:

 1 package {
 2     import flash.display.Sprite;
 3     import flash.display.StageAlign;
 4     import flash.display.StageScaleMode;
 5     import flash.events.Event;
 6
 7     public class AvoidTest extends Sprite {
 8         private var _vehicle:SteeredVehicle;
 9         private var _circles:Array;
10         private var _numCircles:int=5;
11
12         public function AvoidTest():void {
13             stage.align=StageAlign.TOP_LEFT;
14             stage.scaleMode=StageScaleMode.NO_SCALE;
15             _vehicle = new SteeredVehicle(0xff0000);
16
17             _vehicle.edgeBehavior=Vehicle.BOUNCE;
18             addChild(_vehicle);
19
20             //初始化障碍物
21             _circles = new Array();
22             for (var i:int = 0; i < _numCircles; i++) {
23                 var c:Circle=new Circle(Math.random()*30+15,0x0000ff);
24                 c.x=Math.random()*stage.stageWidth;
25                 c.y=Math.random()*stage.stageHeight;
26                 c.vx=Math.random()-0.5;
27                 c.vy=Math.random()-0.5;
28
29                 if (c.x<c.radius) {
30                     c.x=c.radius;
31                 } else if (c.x>stage.stageWidth-c.radius) {
32                     c.x=stage.stageWidth-c.radius;
33                 }
34
35                 if (c.y<c.radius) {
36                     c.y=c.radius;
37                 } else if (c.y>stage.stageHeight-c.radius) {
38                     c.y=stage.stageHeight-c.radius;
39                 }
40
41                 addChild(c);
42                 _circles.push(c);
43             }
44             addEventListener(Event.ENTER_FRAME, onEnterFrame);
45         }
46
47         private function onEnterFrame(e:Event):void {
48
49             _vehicle.wander();
50
51             //处理障碍物的运动以及边界反弹
52             for (var i:int = 0; i < _numCircles; i++) {
53                 var c:Circle=_circles[i] as Circle;
54                 c.x+=c.vx;
55                 c.y+=c.vy;
56                 if (c.x<c.radius) {
57                     c.x=c.radius;
58                     c.vx*=-1;
59                 } else if (c.x>stage.stageWidth-c.radius) {
60                     c.x=stage.stageWidth-c.radius;
61                     c.vx*=-1;
62                 }
63                 if (c.y<c.radius) {
64                     c.y=c.radius;
65                     c.vy*=-1;
66                 } else if (c.y>stage.stageHeight-c.radius) {
67                     c.y=stage.stageHeight-c.radius;
68                     c.vy*=-1;
69                 }
70             }
71             _vehicle.avoid(_circles);
72             _vehicle.update();
73         }
74     }
75 }

二、路径跟随(path following)

对于玩过星际之类游戏的朋友们,这种行为应该最熟悉了。随便选一个神族的狂热者(俗称叉叉兵),然后在几个指定的位置点击一下,它们就会沿着指定的位置来回巡逻。这就是路径跟随:角色尽可能的沿着指定的路径移动。

在算法上的处理很简单:用数组保存一组位置(每个位置其实就是一个Vector2D对象),然后加一个索引变量(指针),用于指示当前移动到了哪一个位置,最终将机车以seek行为移动以下一个位置。

但有一个要注意的细节:seek行为会让机车最终在目标位置来回反复运动停不下来,为了让代码能知道机车已经经过了当前位置,接下来应该去下一个位置,需要引入一个距离阈值,用于判断机车是否已经接近目标点。

 1 private var _pathIndex:int=0;//路径索引
 2         private var _pathThreshold:Number=20;//路径跟随中的距离阈值
 3
 4         public function set pathIndex(value:int):void {
 5             _pathIndex=value;
 6         }
 7         public function get pathIndex():int {
 8             return _pathIndex;
 9         }
10         public function set pathThreshold(value:Number):void {
11             _pathThreshold=value;
12         }
13         public function get pathThreshold():Number {
14             return _pathThreshold;
15         }
16
17         public function set avoidDistance(value:Number):void {
18             _avoidDistance=value;
19         }
20         public function get avoidDistance():Number {
21             return _avoidDistance;
22         }
23
24         public function set avoidBuffer(value:Number):void {
25             _avoidBuffer=value;
26         }
27         public function get avoidBuffer():Number {
28             return _avoidBuffer;
29         }
30
31         //路径跟随
32         public function followPath(path:Array,loop:Boolean=false):void {
33             var wayPoint:Vector2D=path[_pathIndex];
34             if (wayPoint==null) {
35                 return;
36             }
37             if (_position.dist(wayPoint)<_pathThreshold) {
38                 if (_pathIndex>=path.length-1) {
39                     if (loop) {
40                         _pathIndex=0;
41                     }
42                 } else {
43                     _pathIndex++;
44                 }
45             }
46             if (_pathIndex>=path.length-1&&! loop) {
47                 arrive(wayPoint);
48             } else {
49                 seek(wayPoint);
50             }
51         }

测试:

 1 package {
 2
 3     import flash.display.Sprite;
 4     import flash.display.StageAlign;
 5     import flash.display.StageScaleMode;
 6     import flash.events.Event;
 7     import flash.events.MouseEvent;
 8     public class PathTest extends Sprite {
 9         private var _vehicle:SteeredVehicle;
10         private var _path:Array;
11         public function PathTest() {
12             stage.align=StageAlign.TOP_LEFT;
13             stage.scaleMode=StageScaleMode.NO_SCALE;
14             _vehicle=new SteeredVehicle  ;
15             addChild(_vehicle);
16             _path=new Array  ;
17             stage.addEventListener(MouseEvent.CLICK,onClick);
18             addEventListener(Event.ENTER_FRAME,onEnterFrame);
19         }
20         private function onEnterFrame(event:Event):void {
21             _vehicle.followPath(_path,true);
22             _vehicle.update();
23         }
24         private function onClick(event:MouseEvent):void {
25             graphics.lineStyle(0,0,.25);
26             if (_path.length==0) {
27                 graphics.moveTo(mouseX,mouseY);
28             }
29             graphics.lineTo(mouseX,mouseY);
30             graphics.drawCircle(mouseX,mouseY,10);
31             graphics.moveTo(mouseX,mouseY);
32             _path.push(new Vector2D(mouseX,mouseY));
33         }
34     }
35 }

拿鼠标在上面随便点一下,就能看到效果了

三、群落(flock)行为

群落行为是指类似鸟群这样的复合行为。它由三个子行为组成:

分离(separation):鸟群中每个角色都试着和相邻角色保持一定的距离(即:一只鸟与其它鸟太靠近时,主动退让一定的距离,以避免碰到)
凝聚(cohesion):每个角色尽量不掉队,不落下太远(即:尽量向鸟群靠拢)
队列(alignment):每个角色尽可能与相邻角色行动于同一方向(即:每只鸟的速度方向可能不完全相同,但大致跟队伍的总体方向一致)

借用一句李建忠老师名言:原代码就是最好的设计! 直接上代码吧:

 1 private var _inSightDist:Number=200;//视野距离
 2 private var _tooCloseDist:Number=60;//防止群落靠得太近的安全距离
 3
 4 //群落行为
 5 public function flock(vehicles:Array):void {
 6     var averageVelocity:Vector2D=_velocity.clone();//平均速度变量
 7     var averagePosition:Vector2D=new Vector2D  ;//平均位置变量
 8     var inSightCount:int=0;//在视野中的机车数量
 9
10     for (var i:int=0; i<vehicles.length; i++) {
11         var vehicle:Vehicle=vehicles[i] as Vehicle;
12         if (vehicle!=this&&inSight(vehicle)) { //如果其它机车在视野中
13             //累加速度与位置
14             averageVelocity=averageVelocity.add(vehicle.velocity);
15             averagePosition=averagePosition.add(vehicle.position);
16             //如果其它机车太靠近,则避开(即分离行为[separation]的体现)
17             if (tooClose(vehicle)) {
18                 flee(vehicle.position);
19             }
20             inSightCount++; //累加在视野中的机车数
21         }
22     }
23     if (inSightCount>0) {
24         //计算平均位置
25         averageVelocity=averageVelocity.divide(inSightCount);
26         averagePosition=averagePosition.divide(inSightCount);
27         seek(averagePosition);//向中心位置靠拢(即凝聚行为[cohesion]的体现)
28         _steeringForce.add(averageVelocity.subtract(_velocity));//根据平均速度校准自身速度(即队列[alignment]行为的体现)
29     }
30 }
31
32 public function set inSightDist(vaule:Number):void {
33     _inSightDist=vaule;
34 }
35 public function get inSightDist():Number {
36     return _inSightDist;
37 }
38 public function set tooCloseDist(value:Number):void {
39     _tooCloseDist=value;
40 }
41 public function get tooCloseDist():Number {
42     return _tooCloseDist;
43 }
44
45 //判断(身后的其它)机车是否在视野范围内
46 public function inSight(vehicle:Vehicle):Boolean {
47     if (_position.dist(vehicle.position)>_inSightDist) {
48         return false;
49     }
50
51     //---->start 下面这一段代码甚至去掉也行,不过去掉后,群落的行为将有所不同
52     var heading:Vector2D=_velocity.clone().normalize();
53     var difference:Vector2D=vehicle.position.subtract(_position);
54     var dotProd:Number=difference.dotProd(heading);
55     if (dotProd<0) {
56         return false;
57     }
58     //<-----end
59
60
61     return true;
62 }
63
64 public function tooClose(vehicle:Vehicle):Boolean {
65     return _position.dist(vehicle.position)<_tooCloseDist;
66 }

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

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

测试代码:

 1 package {
 2     import flash.display.Sprite;
 3     import flash.display.StageAlign;
 4     import flash.display.StageScaleMode;
 5     import flash.events.Event;
 6     public class FlockTest extends Sprite {
 7         private var _vehicles:Array;
 8         private var _numVehicles:int=20;
 9         public function FlockTest() {
10             stage.align=StageAlign.TOP_LEFT;
11
12             stage.scaleMode=StageScaleMode.NO_SCALE;
13             _vehicles=new Array  ;
14             for (var i:int=0; i<_numVehicles; i++) {
15                 var vehicle:SteeredVehicle=new SteeredVehicle(Math.random()*0xffffff);
16                 vehicle.position=new Vector2D(Math.random()*stage.stageWidth,Math.random()*stage.stageHeight);
17                 vehicle.velocity=new Vector2D(Math.random()*20-10,Math.random()*20-10);
18                 vehicle.edgeBehavior=Vehicle.BOUNCE;
19                 _vehicles.push(vehicle);
20                 addChild(vehicle);
21             }
22             addEventListener(Event.ENTER_FRAME,onEnterFrame);
23         }
24         private function onEnterFrame(event:Event):void {
25             for (var i:int=0; i<_numVehicles; i++) {
26                 _vehicles[i].flock(_vehicles);
27                 _vehicles[i].update();
28             }
29         }
30     }
31 }

如果把inSight中检测其它鸟是否在身后的代码去掉,即简化成:

1 public function inSight(vehicle:Vehicle):Boolean {
2             if (_position.dist(vehicle.position)>_inSightDist) {
3                 return false;
4             }
5             return true;
6         }

预测一下最终的效果:这样相当于只要距离小于阈值的其它鸟,其速度和位置都会被计算在内,最终整个群落将始终聚集在一定的范围内,不会发生分离,从而体现出了另外一种群落效果。

最后,给出Vehicle.as及SteeredVehicle.as的完整代码

  1 package {
  2     import flash.display.Sprite;
  3
  4     public class Vehicle extends Sprite {
  5         //边界行为:是屏幕环绕(wrap),还是反弹{bounce}
  6         protected var _edgeBehavior:String=WRAP;
  7         //质量
  8         protected var _mass:Number=1.0;
  9         //最大速度
 10         protected var _maxSpeed:Number=10;
 11         //坐标
 12         protected var _position:Vector2D;
 13         //速度
 14         protected var _velocity:Vector2D;
 15
 16         //边界行为常量
 17         public static const WRAP:String="wrap";
 18         public static const BOUNCE:String="bounce";
 19
 20         public function Vehicle(color:uint=0xffffff) {
 21             _position=new Vector2D  ;
 22             _velocity=new Vector2D  ;
 23             draw(color);
 24         }
 25
 26
 27         protected function draw(color:uint=0xffffff):void {
 28             graphics.clear();
 29             graphics.lineStyle(0);
 30             graphics.beginFill(color);
 31             graphics.moveTo(10,0);
 32             graphics.lineTo(-10,5);
 33             graphics.lineTo(-10,-5);
 34             graphics.lineTo(10,0);
 35             graphics.endFill();
 36         }
 37
 38
 39         public function update():void {
 40
 41             //设置最大速度
 42             _velocity.truncate(_maxSpeed);
 43
 44             //根据速度更新坐标向量
 45             _position=_position.add(_velocity);
 46
 47             //处理边界行为
 48             if (_edgeBehavior==WRAP) {
 49                 wrap();
 50             } else if (_edgeBehavior==BOUNCE) {
 51                 bounce();
 52             }
 53
 54             //更新x,y坐标值
 55             x=position.x;
 56             y=position.y;
 57
 58             //处理旋转角度
 59             rotation=_velocity.angle*180/Math.PI;
 60         }
 61
 62         //反弹
 63         private function bounce():void {
 64             if (stage!=null) {
 65                 if (position.x>stage.stageWidth) {
 66                     position.x=stage.stageWidth;
 67                     velocity.x*=-1;
 68                 } else if (position.x<0) {
 69                     position.x=0;
 70                     velocity.x*=-1;
 71                 }
 72                 if (position.y>stage.stageHeight) {
 73                     position.y=stage.stageHeight;
 74                     velocity.y*=-1;
 75                 } else if (position.y<0) {
 76                     position.y=0;
 77                     velocity.y*=-1;
 78                 }
 79             }
 80         }
 81
 82         //屏幕环绕
 83         private function wrap():void {
 84             if (stage!=null) {
 85                 if (position.x>stage.stageWidth) {
 86                     position.x=0;
 87                 }
 88                 if (position.x<0) {
 89                     position.x=stage.stageWidth;
 90                 }
 91                 if (position.y>stage.stageHeight) {
 92                     position.y=0;
 93                 }
 94                 if (position.y<0) {
 95                     position.y=stage.stageHeight;
 96                 }
 97             }
 98         }
 99
100         //下面的都是属性定义
101
102
103         public function set edgeBehavior(value:String):void {
104             _edgeBehavior=value;
105         }
106
107         public function get edgeBehavior():String {
108             return _edgeBehavior;
109         }
110
111
112         public function set mass(value:Number):void {
113             _mass=value;
114         }
115
116         public function get mass():Number {
117             return _mass;
118         }
119
120         public function set maxSpeed(value:Number):void {
121             _maxSpeed=value;
122         }
123
124         public function get maxSpeed():Number {
125             return _maxSpeed;
126         }
127
128         public function set position(value:Vector2D):void {
129             _position=value;
130             x=_position.x;
131             y=_position.y;
132         }
133
134         public function get position():Vector2D {
135             return _position;
136         }
137
138         public function set velocity(value:Vector2D):void {
139             _velocity=value;
140         }
141
142         public function get velocity():Vector2D {
143             return _velocity;
144         }
145
146         override public function set x(value:Number):void {
147             super.x=value;
148             _position.x=x;
149         }
150
151         override public function set y(value:Number):void {
152             super.y=value;
153             _position.y=y;
154         }
155     }
156 }

  1 package {
  2     import flash.display.Sprite;
  3
  4     //(具有)转向(行为的)机车
  5     public class SteeredVehicle extends Vehicle {
  6         private var _maxForce:Number=1;//最大转向力
  7         private var _steeringForce:Vector2D;//转向速度
  8         private var _arrivalThreshold:Number=100;//到达行为的距离阈值(小于这个距离将减速)
  9         private var _wanderAngle:Number=0;
 10         private var _wanderDistance:Number=10;
 11         private var _wanderRadius:Number=5;
 12         private var _wanderRange:Number=1;
 13
 14         private var _avoidDistance:Number=300;//发现障碍物的有效视野
 15         private var _avoidBuffer:Number=20;//机车在准备避开时,自身和障碍物间的预留距离。
 16
 17         private var _pathIndex:int=0;//路径索引
 18         private var _pathThreshold:Number=20;//路径跟随中的距离阈值
 19
 20         private var _inSightDist:Number=200;//视野距离
 21         private var _tooCloseDist:Number=60;//防止群落靠得太近的安全距离
 22
 23         //群落行为
 24         public function flock(vehicles:Array):void {
 25             var averageVelocity:Vector2D=_velocity.clone();//平均速度变量
 26             var averagePosition:Vector2D=new Vector2D  ;//平均位置变量
 27             var inSightCount:int=0;//在视野中的机车数量
 28
 29             for (var i:int=0; i<vehicles.length; i++) {
 30                 var vehicle:Vehicle=vehicles[i] as Vehicle;
 31                 if (vehicle!=this&&inSight(vehicle)) { //如果其它机车在视野中
 32                     //累加速度与位置
 33                     averageVelocity=averageVelocity.add(vehicle.velocity);
 34                     averagePosition=averagePosition.add(vehicle.position);
 35                     //如果其它机车太靠近,则避开(即分离行为[separation]的体现)
 36                     if (tooClose(vehicle)) {
 37                         flee(vehicle.position);
 38                     }
 39                     inSightCount++; //累加在视野中的机车数
 40                 }
 41             }
 42             if (inSightCount>0) {
 43                 //计算平均位置
 44                 averageVelocity=averageVelocity.divide(inSightCount);
 45                 averagePosition=averagePosition.divide(inSightCount);
 46                 seek(averagePosition);//向中心位置靠拢(即凝聚行为[cohesion]的体现)
 47                 _steeringForce.add(averageVelocity.subtract(_velocity));//根据平均速度校准自身速度(即队列[alignment]行为的体现)
 48             }
 49         }
 50
 51         public function set inSightDist(vaule:Number):void {
 52             _inSightDist=vaule;
 53         }
 54         public function get inSightDist():Number {
 55             return _inSightDist;
 56         }
 57         public function set tooCloseDist(value:Number):void {
 58             _tooCloseDist=value;
 59         }
 60         public function get tooCloseDist():Number {
 61             return _tooCloseDist;
 62         }
 63
 64         //判断(身后的其它)机车是否在视野范围内
 65         public function inSight(vehicle:Vehicle):Boolean {
 66             if (_position.dist(vehicle.position)>_inSightDist) {
 67                 return false;
 68             }
 69
 70             //---->start 下面这一段代码甚至去掉也行,不过去掉后,群落的行为将有所不同
 71             var heading:Vector2D=_velocity.clone().normalize();
 72             var difference:Vector2D=vehicle.position.subtract(_position);
 73             var dotProd:Number=difference.dotProd(heading);
 74             if (dotProd<0) {
 75                 return false;
 76             }
 77             //<-----end
 78
 79
 80             return true;
 81         }
 82
 83         public function tooClose(vehicle:Vehicle):Boolean {
 84             return _position.dist(vehicle.position)<_tooCloseDist;
 85         }
 86
 87
 88         public function set pathIndex(value:int):void {
 89             _pathIndex=value;
 90         }
 91         public function get pathIndex():int {
 92             return _pathIndex;
 93         }
 94         public function set pathThreshold(value:Number):void {
 95             _pathThreshold=value;
 96         }
 97         public function get pathThreshold():Number {
 98             return _pathThreshold;
 99         }
100
101         public function set avoidDistance(value:Number):void {
102             _avoidDistance=value;
103         }
104         public function get avoidDistance():Number {
105             return _avoidDistance;
106         }
107
108         public function set avoidBuffer(value:Number):void {
109             _avoidBuffer=value;
110         }
111         public function get avoidBuffer():Number {
112             return _avoidBuffer;
113         }
114
115         //路径跟随
116         public function followPath(path:Array,loop:Boolean=false):void {
117             var wayPoint:Vector2D=path[_pathIndex];
118             if (wayPoint==null) {
119                 return;
120             }
121             if (_position.dist(wayPoint)<_pathThreshold) {
122                 if (_pathIndex>=path.length-1) {
123                     if (loop) {
124                         _pathIndex=0;
125                     }
126                 } else {
127                     _pathIndex++;
128                 }
129             }
130             if (_pathIndex>=path.length-1&&! loop) {
131                 arrive(wayPoint);
132             } else {
133                 seek(wayPoint);
134             }
135         }
136
137         //对象回避
138         public function avoid(circles:Array):void {
139             for (var i:int=0; i<circles.length; i++) {
140                 var circle:Circle=circles[i] as Circle;
141                 var heading:Vector2D=_velocity.clone().normalize();
142                 // 障碍物和机车间的位移向量
143                 var difference:Vector2D=circle.position.subtract(_position);
144                 var dotProd:Number=difference.dotProd(heading);
145                 // 如果障碍物在机车前方
146                 if (dotProd>0) {
147                     // 机车的“触角”
148                     var feeler:Vector2D=heading.multiply(_avoidDistance);
149                     // 位移在触角上的映射
150                     var projection:Vector2D=heading.multiply(dotProd);
151                     // 障碍物离触角的距离
152                     var dist:Number=projection.subtract(difference).length;
153                     // 如果触角(在算上缓冲后)和障碍物相交
154                     // 并且位移的映射的长度小于触角的长度
155                     // 我们就说碰撞将要发生,需改变转向
156                     if (dist<circle.radius+_avoidBuffer&&projection.length<feeler.length) {
157                         // 计算出一个转90度的力
158                         var force:Vector2D=heading.multiply(_maxSpeed);
159                         force.angle+=difference.sign(_velocity)*Math.PI/2;
160                         // 通过离障碍物的距离,调整力度大小,使之足够小但又能避开
161                         force=force.multiply(1.0-projection.length/feeler.length);
162                         // 叠加于转向力上
163                         _steeringForce=_steeringForce.add(force);
164                         // 刹车——转弯的时候要放慢机车速度,离障碍物越接近,刹车越狠。
165                         _velocity=_velocity.multiply(projection.length/feeler.length);
166                     }
167                 }
168             }
169         }
170
171         //漫游
172         public function wander():void {
173             var center:Vector2D=velocity.clone().normalize().multiply(_wanderDistance);
174             var offset:Vector2D=new Vector2D(0);
175             offset.length=_wanderRadius;
176             offset.angle=_wanderAngle;
177             _wanderAngle+=Math.random()-0.5*_wanderRange;
178             var force:Vector2D=center.add(offset);
179             _steeringForce=_steeringForce.add(force);
180         }
181
182         public function set wanderDistance(value:Number):void {
183             _wanderDistance=value;
184         }
185         public function get wanderDistance():Number {
186             return _wanderDistance;
187         }
188
189         public function set wanderRadius(value:Number):void {
190             _wanderRadius=value;
191         }
192         public function get wanderRadius():Number {
193             return _wanderRadius;
194         }
195         public function set wanderRange(value:Number):void {
196             _wanderRange=value;
197         }
198         public function get wanderRange():Number {
199             return _wanderRange;
200         }
201
202         public function set arriveThreshold(value:Number):void {
203             _arrivalThreshold=value;
204         }
205         public function get arriveThreshold():Number {
206             return _arrivalThreshold;
207         }
208
209         //构造函数
210         public function SteeredVehicle(color:uint=0xffffff) {
211             _steeringForce=new Vector2D  ;
212             super(color);
213         }
214         public function set maxForce(value:Number):void {
215             _maxForce=value;
216         }
217         public function get maxForce():Number {
218             return _maxForce;
219         }
220
221         //寻找(Seek)行为
222         public function seek(target:Vector2D):void {
223             var desiredVelocity:Vector2D=target.subtract(_position);
224             desiredVelocity.normalize();
225             desiredVelocity=desiredVelocity.multiply(_maxSpeed);//注:这里的_maxSpeed是从父类继承得来的
226             var force:Vector2D=desiredVelocity.subtract(_velocity);
227             _steeringForce=_steeringForce.add(force);
228         }
229
230         //避开(flee)行为
231         public function flee(target:Vector2D):void {
232             var desiredVelocity:Vector2D=target.subtract(_position);
233             desiredVelocity.normalize();
234             desiredVelocity=desiredVelocity.multiply(_maxSpeed);
235             var force:Vector2D=desiredVelocity.subtract(_velocity);
236             _steeringForce=_steeringForce.subtract(force);//这是唯一也seek行为不同的地方,一句话解释:既然发现了目标,那就调头就跑吧!
237         }
238
239         //到达(arrive)行为
240         public function arrive(target:Vector2D):void {
241             var desiredVelocity:Vector2D=target.subtract(_position);
242             desiredVelocity.normalize();
243             var dist:Number=_position.dist(target);
244             if (dist>_arrivalThreshold) {
245                 desiredVelocity=desiredVelocity.multiply(_maxSpeed);
246             } else {
247                 desiredVelocity=desiredVelocity.multiply(_maxSpeed*dist/_arrivalThreshold);
248             }
249             var force:Vector2D=desiredVelocity.subtract(_velocity);
250             _steeringForce=_steeringForce.add(force);
251         }
252
253         //追捕(pursue)行为
254         public function pursue(target:Vehicle):void {
255             var lookAheadTime:Number=position.dist(target.position)/_maxSpeed;//假如目标不动,追捕者开足马力赶过去的话,计算需要多少时间
256             var predictedTarget:Vector2D=target.position.add(target.velocity.multiply(lookAheadTime));
257             seek(predictedTarget);
258         }
259
260         //躲避(evade)行为
261         public function evade(target:Vehicle):void {
262             var lookAheadTime:Number=position.dist(target.position)/_maxSpeed;
263             var predictedTarget:Vector2D=target.position.add(target.velocity.multiply(lookAheadTime));
264             flee(predictedTarget);
265         }
266
267
268         override public function update():void {
269             _steeringForce.truncate(_maxForce);//限制为最大转向速度,以避免出现突然的大转身
270             _steeringForce=_steeringForce.divide(_mass);//惯性的体现
271             _velocity=_velocity.add(_steeringForce);
272             _steeringForce=new Vector2D  ;
273             super.update();
274         }
275     }
276 }

时间: 2024-10-12 00:00:13

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

“AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (上)

“AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (上) 原作者:菩提树下的杨过出处:http://yjmyzz.cnblogs.com 一提到“A*算法”,可能很多人都有"如雷贯耳"的感觉.用最白话的语言来讲:把游戏中的某个角色放在一个网格环境中,并给定一个目标点和一些障碍物,如何让角色快速“绕过障碍物”找出通往目标点的路径.(如下图) 在寻路过程中,角色总是不停从一个格子移动到另一个相邻的格子,如果单纯从距离上讲,移动到与自身斜对角的格子走的距离要长一些,

“AS3.0高级动画编程”学习:第三章等角投影(上)

什么是等角投影(isometric)? 原作者:菩提树下的杨过出处:http://yjmyzz.cnblogs.com 刚接触这个概念时,我也很茫然,百度+google了N天后,找到了一些文章: [转载]等角(斜45度)游戏与数学  ( 原文链接:http://www.javaeye.com/articles/1225) [转载]使用illustrator和正交投影原理以及基本三视图制图   (http://www.vanqy.cn/index.php/2009/03/working-with-

Elasticsearch学习-----第二章 windows环境下Elasticsearch同步mysql数据库

在上一章中,我们已经能够通过spring boot来使用Elasticsearch,但是由于我们习惯性的将数据写入mysql,所以为了解决这个问题,Elasticsearch为我们提供了一个插件logstash来同步我们的数据库.本文所有的安装环境和使用环境都是在windows系统下进行的. 一.logstash的安装 首先在官网上下载logstash: logstash下载地址:https://www.elastic.co/downloads/logstash 需要注意的是logstash的版

一维向量旋转算法 编程珠玑 第二章

看了编程珠玑第二章,这里面讲了三道题目,这里说一下第二题,一维向量旋转算法. 题目:将一个n元一维向量(例数组)向左旋转i个位置. 解决方法:书上讲解了5种方法,自己只想起来2种最简单方法(下面讲的前两种). 1.原始方法. 从左向右依次移动一位,对所有数据平移:这样循环i次,算法最坏时间复杂度达n^2.耗时不推荐. 2.空间换时间. 顾名思义,申请一个i长度的空间,把前i半部分放到申请空间中,再把后面的所有数据向左移动i个位置,最后把申请的空间中的数据放到后半部分.浪费空间,不推荐. 3.杂技

编程珠玑第二章

编程珠玑第二章 A题 给定一个最多包含40亿个随机排列的32位整数的顺序文件,找出一个不在文件中一32位整数. 1.在文件中至少存在这样一个数? 2.如果有足够的内存,如何处理? 3.如果内存不足,仅可以用文件来进行处理,如何处理? 答案: 1.32位整数,包括-2146473648~~2146473647,约42亿个整数,而文件中只有40亿个,必然有整数少了. 2.如果采用位数思想来存放,则32位整数最多需要占用43亿个位.约512MB的内存空间. 可以采用前一章的位处理方法.然后判断每个in

java编程思想 第二章

这篇时间较之前篇章时间靠后,是由于,某一天晚上看完Java编程思想文献之后来不及做笔记了. 以下笔记基本为转载,不是原创 第二章   一切都是对象 目录: 2.1 用引用操纵对象 2.2 必须由你创建所有对象 2.3 永远不需要销毁对象 2.4 创建新的数据类型:类 2.5 方法.参数和返回值 2.6 构建一个Java程序 2.7 你的第一个Java程序 2.8 注释和嵌入式文档 2.9 编码风格 2.1 用引用操纵对象 一切都看作对象,操纵的标识符实际上是对象的一个“引用”,遥控器(引用)操纵

[书籍翻译] 《JavaScript并发编程》 第二章 JavaScript运行模型

本文是我翻译<JavaScript Concurrency>书籍的第二章 JavaScript运行模型,该书主要以Promises.Generator.Web workers等技术来讲解JavaScript并发编程方面的实践. 完整书籍翻译地址:https://github.com/yzsunlei/javascript_concurrency_translation .由于能力有限,肯定存在翻译不清楚甚至翻译错误的地方,欢迎朋友们提issue指出,感谢. 本书第一章我们探讨了JavaScri

Java基础知识二次学习-- 第二章 基础语法与递归补充

第二章 基础语法与递归补充   时间:2017年4月24日10:39:18 章节:02章_01节,02章_02节 视频长度:49:21 + 15:45 内容:标识符,关键字与数据类型 心得:由字母,下划线,$,数字组成,应该由字母,下划线$开头,同时应该避开java保留字符 变量是内存中的一小块区域,使用变量名来访问这块区域 执行过程中的内存管理(疑问:这里的内存和Jvm的一样吗?) code segment 存放代码 data segment 静态变量 字符串常量 stack 栈 局部变量 h

集体智慧编程_第二章(提供推荐)_1

前言:最近正在拜读Toby Segaran先生写的集体智慧编程,首先感谢Toby Segaran先生将知识以书本的方式传播给大家,同时也感谢莫映和王开福先生对此书的翻译,谢谢各位的不辞辛苦.首先在写随笔之前,跟各位分享一下我的编程环境:win7系统,python版本是2.7.10,开发环境我选择的是pycharm程序.本书的第一章为集体智慧导言,主要介绍的何为集体智慧和机器学习的相关概念和其局限性,以及与机器学习相关的例子和应用场景.下面开始机器学习第二章--提供推荐的相关内容. 本章主要内容: