zoukankan      html  css  js  c++  java
  • “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 }
    View Code

    将以上代码加入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 }
    View Code

    测试:

     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 }
    View Code

    二、路径跟随(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         }
    View Code

    测试:

     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 }
    View Code

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

    三、群落(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 }
    View Code

    重点关注下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 }
    View Code

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

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

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

     最后,给出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 }
    View Code
      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 }
    View Code
     
    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com 
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
     
  • 相关阅读:
    软件工程
    ROR
    全息技术(Holographic technique)
    VR技术、AR技术、MR技术
    人工智能(AI)
    机器学习(Machine Learning)
    hdoj Scaena Felix
    周赛题解
    Good Luck in CET-4 Everybody!(博弈)
    Paths on a Grid(规律)
  • 原文地址:https://www.cnblogs.com/buerjiongjiong/p/4731073.html
Copyright © 2011-2022 走看看