[AI-Steering编程]一.创建机车

来源:互联网 发布:大数据市场格局 编辑:程序博客网 时间:2024/04/30 10:25

家里终于拉上网线了,好久没到论坛里逛了,最近在学习<AdvancED Actionscript3>里的SteeringVehicle编程.写了些笔记,发出来跟大家一起分享
/*----------------------------
文章原创,转载请注明出处:
我的博客:http://blog.sina.com.cn/ladeng6666
------------------------------*/
Steer这个单词在英文中的解释为”驾驶,掌舵”,就像字面上翻译的那样,这次我学习的主要内容是steering机车的编程,steering编程主要是模仿现实中汽车的转向驾驶运动, 关于这类编程的实现,可以在Miniclip公司的canyondefence小游戏上得到完美的体现.
pic.jpg


看过这个游戏你肯定很兴奋,终于找到这种游戏的教程了,别高兴的太早,我没那么厉害,看完了<AdvancED Actionscript3>之后,我还是做不出来这个游戏,书中的知识是有限,真正要做好这个游戏,要学的AI还很多,所以路还是要一步一步走的,我的学习笔记,只是跟大家一起入门游戏AI.好了,闲话少说,开始看书!!
===========================================================
一.创建机车
1.1目的
创建一个机车实例,并且能体现出机车移动和转向
1.2实现
l绘制机车
l机车添加速度属性,使机车能够移动
l机车点击转向力,是机车可以转向
l使机车运动更加贴近实际物理运动
1.3讨论
l绘制机车
对于机车,跟AdvancED中一样,我把他绘制成了三角型,这样更便于1 KB)

209-12-17 23:47


具体代码实现如下:

 
package ladeng6666.steering
{
         import flash.display.Sprite;
        
         public class Vechile extends Sprite
         {
                   private var _color:uint;                    //机车的颜色
                  
                   public function Vechile(color:uint=0)
                   {
                            _color = color;
                            drawVechile();
                   }
                   private function drawVechile():void {
                            graphics.clear();
                            graphics.lineStyle(0);
                            graphics.beginFill(_color, 0.8);
                            graphics.moveTo(10, 0);
                            graphics.lineTo( -10, 5);
                            graphics.lineTo( -10, -5);
                            graphics.lineTo(10, 0);
                            graphics.endFill();
                   }
         }
        
}


l机车添加速度属性,使机车能够移动
对于速度,我没有像以前一样,只是定义个speed:number,然后机车的x或y坐标上叠加speed,而是使用了一个矢量来表示速度velocity:Vector2D.矢量是一个有方向的线段,矢量的长度即速度的大小,矢量的方向即速度的方向,所以用矢量来表示速度再合适不过了,下面我们来讨论机车是如何移动的.
首先,机车要出现在舞台上,就必须有自己的位置position:Vector2D,位置坐标只是一个点,为什么也要定义为矢量类型呢?因为速度是一个矢量,机车的移动实际就是坐标沿着速度的方向,按照速度的大小进行变更,为了能和速度矢量之间进行矢量加减运算,所以我把机车的坐标位置position也定义成了Vector2D类型.代码如下:

 
                   private var _position:Vector2D;                //机车的坐标



机车坐标的变更:
                   public function update():void {
                            this.x = _position.x;
                            this.y = _position.y;
                   }


再下面就是添加速度矢量属性了.
private var _velocity:Vector2D;            //机车的速度向量


机车的方向始终与速度的方向一致:
                   this.rotation = _velocity.angle * 180 / Math.PI;   //添加在update()方法中


由于机车的方向始终与速度一样,所以机车在移动上看不出position和velocity的矢量叠加,而是沿着的速度的x轴和y轴分量之间移动到下一个位置
                   _position = _position.add(_velocity);       //添加在update方法中



velocity.jpg


到这里机车就可以移动了,完整Vehicle类的代码如下:

 
package ladeng6666.steering
{
         import flash.display.Sprite;
        
         public class Vechile extends Sprite
         {
                   private var _color:uint;                    //机车的颜色
                   private var _position:Vector2D;                //机车的坐标
                   private var _velocity:Vector2D;                 //机车的速度向量
                  
                   public function Vechile(color:uint=0xffffff)
                   {
                            _color = color;
                            _position = new Vector2D();
                            _velocity = new Vector2D();
                            _steerForce = new Vector2D();
                           
                            drawVechile();
                   }
                  
                   private function drawVechile():void {
                            graphics.clear();
                            graphics.lineStyle(0);
                            graphics.beginFill(_color, 0.8);
                            graphics.moveTo(10, 0);
                            graphics.lineTo( -10, 5);
                            graphics.lineTo( -10, -5);
                            graphics.lineTo(10, 0);
                            graphics.endFill();
                   }
                   //下面的setter和getter应该就不用解释了吧
                   public function set position(value:Vector2D):void {
                            _position = value;
                   }
                   public function get position():Vector2D {
                            return _position;
                   }
                   public function set velocity(value:Vector2D):void {
                            _velocity = value;
                  }
                   public function get velocity():Vector2D {
                            return _velocity;
                   }
                   public function update():void {
                            _position = _position.add(_velocity);
                           
                            this.x = _position.x;
                            this.y = _position.y;
                           
                            this.rotation = _velocity.angle * 180 / Math.PI;
                   }
         }       
}


文档Main类:

 
package 
{
         import flash.display.Graphics;
         import flash.display.Sprite;
         import flash.events.Event;
         import ladeng6666.steering.Vector2D;
        
         import ladeng6666.steering.Vechile;

         public class Main extends Sprite
         {
                   private var vech:Vechile;                           //机车实例
                  
                   public function Main()
                   {
                            vech = new Vechile();
                            vech.position = new Vector2D(30, 30);     //机车的初始位置矢量
                            vech.velocity = new Vector2D(4, 0);                  //机车的速度矢量
                           
                            addChild(vech);
                           
                            addEventListener(Event.ENTER_FRAME, enterFrameHandler);
                   }
                   //按照帧频随速度更新机车的位置
                   private function enterFrameHandler(e:Event):void {
                            vech.update();
                   }
         }
        
}



机车点击转向力,是机车可以转向
steering.jpg


转向力也被我定义成了一个矢量_steerForce:Vector2D,因为转向力跟速度一样是一个”有方向的线段”,这个外力与速度矢量和便是我们的目标速度矢量.
机车的转向最根本的原因是受到了外力的作用,想象一下,台球桌上的球在没有其他碰撞的情况下,一直往前滚,当与其他球碰撞的时候,由于受到其他球的反作用力,球自然就转弯了(当然用碰撞来解释,有点不太恰当,但原理是一样的),受力的示意图如左图所示.
如图所示,蓝色的velocity速度矢量与红色的steerForce转向力的矢量和是绿色的desiredvelocity目标速度矢量,如上一节所讲,机车的下一步将出现在黑色的圆圈处,且方向与绿色desiredvelocity目标速度矢量是一致的.
具体的代码实现,首先是声明steeringForce变量:

        _steerForce = new Vector2D();


然后是steerForce的setter和getter:

                public function set steerForce(value:Vector2D):void {
                        _steerForce = value;
                }
                public function get steerForce():Vector2D {
                        return _steerForce;
               


update方法中只需要添加速度与转向力的矢量叠加如下:

                public function update():void {
                        _velocity = _velocity.add(_steerForce);                //速度与转向力的矢量叠加
                        _position = _position.add(_velocity);                //位置与速度的矢量叠加
                       
                        this.x = _position.x;
                        this.y = _position.y;
                       
                        this.rotation = _velocity.angle * 180 / Math.PI;
                }


文档Main类为机车vech添加转向力:

package 
{
        import flash.display.Graphics;
        import flash.display.Sprite;
        import flash.events.Event;
        import ladeng6666.steering.Vector2D;
       
        import ladeng6666.steering.Vechile;

        public class Main extends Sprite
        {
                private var vech:Vechile;                        //机车实例
               
                public function Main()
                {
                        vech = new Vechile();
                        vech.position = new Vector2D(30, 30);        //机车的初始位置矢量
                        vech.velocity = new Vector2D(4, 0);                //机车的速度矢量
                        vech.steerForce = new Vector2D(0, 0.2);        //机车的转向力矢量
                        addChild(vech);
                       
                        addEventListener(Event.ENTER_FRAME, enterFrameHandler);
                }
                //按照帧频随速度更新机车的位置
                private function enterFrameHandler(e:Event):void {
                        vech.update();
                }
        }
}


使机车运动更加贴近实际物理运动
到这里,我们的机车已经可以实现运动和转向了,似乎没有什么可以再讲的了,但是在上面的转向例子中,蓝色的速度矢量的长度越来越长,这意味着速度越来越大,因为在转向力的作用下,机车一直作加速运动,这样下去机车的速度迟早会超过光速的(开玩笑的,谁也没那么大的显示器啊O(∩_∩)O).
1.3-实际.jpg

1.3-实际1.jpg


注意:上图的蓝色的速度矢量及红色的转向力矢量的大小都是我放大比例之后的,分解矢量的时候不要参照这个图,如果实际把velocity定到这么大的话,不到一秒中,机车就冲出舞台了.
现实中不管是什么车总有它的最大的速度,当速度达到这个最大值后,将以最大值作匀速运动,所以我们也对velocity作一下限制.
首先声明一个maxSpeed的变量,并定义其setter/getter方法.

                private var _maxSpeed:Number=10;        //机车的最大速度,默认为10;

                public function set maxSpeed(value:Number):void {
                        _maxSpeed = value;
                }
                public function get maxSpeed():Number {
                        return _maxSpeed;
                }


update方法中对velocity进行限制:

                public function update():void {
                        _velocity = _velocity.add(_steerForce);                //速度与转向力的矢量叠加
                        _velocity.truncate(_maxSpeed);                        //限制速度矢量的最大值
                        _position = _position.add(_velocity);                //位置与速度的矢量叠加
                       
                        this.x = _position.x;
                        this.y = _position.y;
                       
                        this.rotation = _velocity.angle * 180 / Math.PI;
                }



这样就实现了对速度矢量大小的限制,这个我们用到了Vector2D类的truncate方法,关于这个方法的原理请参考Vector2D的源码,这里就不解释了.
1.3-实际2.jpg

1.3-实际3.jpg


1.3-actualMove.swf (2.66 KB)


另外还要补充一点,根据物理的能量守恒定理,在受到同样力作用的情况下,质量越大的物体,运动速度越慢(能量守恒定理是这么讲的吗?管他呢,我是这么认为的),自行车推一下可以动,汽车推一下可动不了,就是这个道理.
所以我们给机车定义一个mass变量来表示质量,然后我想用机车的大小来表示它的质量(一般情况下越重的车越大嘛),并定mass的setter和getter方法:

                private var _mass:Number = 1;                //机车的质量,默认为1;

                public function set mass(value:Number):void {
                        if (value< 1) return;
                        _mass = value;
                        this.scaleX = this.scaleY = value;                //这一句实现机车大小的变化
                }
                public function get mass():Number {
                        return _mass;
                }



然后是转向力相对于质量的处理,从相对的角度上来讲,同样大小的力作用到质量大的机车上时,我们把质量缩小比例到小质量,转向力也等比的缩小,这样我们把质量等同后,质量大的机车受到的转向力相对就会小,所以我们将转向力除以质量,来处理质量与转向力之间的关系,steerForce方法变更如下:

                public function set steerForce(value:Vector2D):void {
                        _steerForce = value.divide(_mass);
                }


文档Main类里更改vech的mass属性:

                public function Main()
                {
                        vech = new Vechile();
                        vech.mass = 2;                                        //机车的质量
                        vech.position = new Vector2D(30, 30);                //机车的初始位置矢量
                        vech.velocity = new Vector2D(4, 0);                //机车的速度矢量
                        vech.steerForce = new Vector2D(0, 0.2);        //机车的转向力矢量
                        addChild(vech);
                       
                        addEventListener(Event.ENTER_FRAME, enterFrameHandler);
                }

下面是质量不同的两个机车之间的对比.

通过上面的比较,我们可以看出mass=2时的红色转向力要比mass=1时的红色转向力要小,也就是说质量越大的机车运动的越慢.

1.3-mass.swf (2.69 KB)

mass.jpg(26.62 KB)

 

mass=1

mass.jpg

mass2.jpg(27.71 KB)

 

mass=2

mass2.jpg



本章的目的都实现后,完整vehicle类的代码应该如下:

package ladeng6666.steering
{
        import flash.display.Sprite;
        
        public class Vechile extends Sprite
        {
                private var _color:uint;                        //机车的颜色
                private var _position:Vector2D;                //机车的坐标
                private var _velocity:Vector2D;                //机车的速度向量
                private var _steerForce:Vector2D;        //机车受到的外力
                
                private var _maxSpeed:Number=10;        //机车的最大速度,默认为10;
                private var _mass:Number = 1;                //机车的质量,默认为1;
                
                public function Vechile(color:uint=0xffffff)
                {
                        _color = color;
                        _position = new Vector2D();
                        _velocity = new Vector2D();
                        _steerForce = new Vector2D();
                        
                        drawVechile();
                }
                
                private function drawVechile():void {
                        graphics.clear();
                        graphics.lineStyle(0);
                        graphics.beginFill(_color, 0.8);
                        graphics.moveTo(10, 0);
                        graphics.lineTo( -10, 5);
                        graphics.lineTo( -10, -5);
                        graphics.lineTo(10, 0);
                        graphics.endFill();
                }
                //下面的setter和getter应该就不用解释了吧
                public function set position(value:Vector2D):void {
                        _position = value;
                }
                public function get position():Vector2D {
                        return _position;
                }
                public function set velocity(value:Vector2D):void {
                        _velocity = value;
                }
                public function get velocity():Vector2D {
                        return _velocity;
                }
                public function set steerForce(value:Vector2D):void {
                        _steerForce = value.divide(_mass);
                }
                public function get steerForce():Vector2D {
                        return _steerForce;
                }
                public function set maxSpeed(value:Number):void {
                        _maxSpeed = value;
                }
                public function get maxSpeed():Number {
                        return _maxSpeed;
                }
                public function set mass(value:Number):void {
                        if (value< 1) return;
                        _mass = value;
                        this.scaleX = this.scaleY = value;                //这一句实现机车大小的变化
                }
                public function get mass():Number {
                        return _mass;
                }
                public function update():void {
                        _velocity = _velocity.add(_steerForce);                //速度与转向力的矢量叠加
                        _velocity.truncate(_maxSpeed);                                //限制速度矢量的最大值
                        _position = _position.add(_velocity);                //位置与速度的矢量叠加
                        
                        this.x = _position.x;
                        this.y = _position.y;
                        
                        this.rotation = _velocity.angle * 180 / Math.PI;
                }
        }        
}



文档Main类:

package 
{
        import flash.display.Graphics;
        import flash.display.Sprite;
        import flash.events.Event;
        import ladeng6666.steering.Vector2D;
       
        import ladeng6666.steering.Vechile;

        public class Main extends Sprite
        {
                private var vech:Vechile;                        //机车实例
               
                public function Main()
                {
                        vech = new Vechile();
                        vech.mass = 2;                                        //机车的质量
                        vech.position = new Vector2D(30, 30);                //机车的初始位置矢量
                        vech.velocity = new Vector2D(4, 0);                //机车的速度矢量
                        vech.steerForce = new Vector2D(0, 0.2);        //机车的转向力矢量
                        addChild(vech);
                       
                        addEventListener(Event.ENTER_FRAME, enterFrameHandler);
                }
                //按照帧频更新机车的位置
                private function enterFrameHandler(e:Event):void {
                        vech.update();
                }
        }
}


本章的源码如下:
chapter1.rar (23.01 KB)






原创粉丝点击