5.1速度

物体运动的最基本属性就是速度。很多人把速度(velocity)和速率(speed)等同,这是不对的,因为速率仅仅是速度的一部分,速度的概念还包括一个非常重要的因素:方向。速度的简单定义是:某个方向上的速率。

如果我开车从 X 位置出发,以每小时 30公里的速度行驶一个小时,这时如果想找到我可就难了。但是,如果我说以同样的速率向北行驶一个小时,那么大家就可以知道我实际的位置了,这在动画中是非常重要的。这就是引入速度的原因,如果知道物体在某一帧时的位置,那么只要知道它的运动速度和方向,就可以知道物体下一帧所在的位置了。

在介绍速度向量编码之前,先要为大家介绍一些向量的知识,因为它是速度的一般描述方式。

5.1.1向量与速度

向量(vector)由长度和方向组成。在速度中,长度就是速率。向量用带有箭头的线段表示,箭头的长度就是向量的长度,箭头所指的方向就是向量的方向。图 5-1是一些向量。

图5-1

图 5-1 几个向量

需要注意的是,长度总是正数,如果一个长度为负数的向量只表示该向量的反方向,见图5-2。

图5-2

图 5-2 负的速度实际上是反方向的速度

还要注意向量没有起点,向量不能说明哪里是起点哪里是终点,它仅仅表示出了物体移动的速度与方向。因此,如果两个的方向及长度都相同,即使它们位于不同位置,那么它们仍是两个相等的向量,如图5-3 所示。

图5-3

图 5-3 如果向量的方向与长度相同,则它们相等,不考虑位置问题

5.1.2一个轴上的速度

首先,为了简化这个问题,把速度只放在一个轴上:x 轴(水平轴)。让物体向从屏幕的左侧到右侧——这样会比较符合习惯,速率就是物体每一帧移动的像素值。因此,如果说速度在 x 轴上为 5,就意味着物体在每一帧都会右移动 5 个像素。同样,如果速度在x 轴上为 -5,那么物体每一帧就会向左移动 5个像素。

到现在为止大家都能跟上吗?我们刚刚提到了向量长度为负值的情况,科学地讲,速度实际上应该为 5,而方向应为 180度。同理,y 轴正半轴上的速度向量应为 90 度(垂直向下),而负 y 轴负半轴上的速度向量应为 270 或90 度(垂直向上)。实际上,当计算 x,y 速度向量的分量时,通常可以记作正数或负数,比如“ x 速度向量为-5”。在 x 轴上把负号看成“向左”的指示符,在 y 轴上则是“向上”的指示符。

在本书中,将用 vx 表示 x 轴的速度向量,用vy 表示y 轴的速度向量。vx 为正数表示向右移动,为负数表示向左移动,vy 为正数表示向下,vy 为负数表示向上。本章的许多例子都会让物体做出各种移动效果。

为了不让每个例子都花时间绘制物体,我们下面创建一个 Ball 类,这样就可以经常重复使用它了。

package
{
    import flash.display.Sprite;
    
    public class Ball extends Sprite {
        public var radius:Number;
        private var color:uint;
        
        public function Ball(radius:Number=40,color:uint=0xff0000){
            this.radius=radius; 
            this.color=color;
            init();
        }
        
        public function init():void {
            graphics.beginFill(color);
            graphics.drawCircle(0,0,radius);
            graphics.endFill();
        }
    }
}

今后无论何时引用这个类,都要把这个类与工程放在一起,这样只需要使用 new Ball(size, color)就可以了创建一个小球了,或者可以把这个类放在一个特定的位置,并将其所在目录加入类路径中。现在已经有了运动主体,以下是第一个速度向量的示例,文档类Velocity1.as:

package 
{
	import flash.display.Sprite;
	import flash.events.Event;

	public class Velocity1 extends Sprite
	{
		private var ball:Ball;
		private var vx:Number = 5;

		public function Velocity1()
		{
		    init();
		}

		private function init():void
		{
		    ball = new Ball();
		    addChild(ball);
		    ball.x = 50;
		    ball.y = 100;
		    addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function onEnterFrame(event:Event):void
		{
		    ball.x +=  vx;
		}
	}
}

在这个例子中,首先设置一个x 轴速度向量(vx)等于5。记住是指每一帧 5 像素,所以,在每一帧中,vx 都会被加到 ball 的x 属性中。init 方法将小球放到舞台上并为enterFrame 设置事件处理函数。每走一帧,小球都会在前一帧的位置基础上向右移动 5 个像素。怎么样很不错吧,嗯?

给 vx 一个较大或较小的值,或者给个负数来试试,并观察一下物体运动的方向。

5.1.3两个轴上的速度

使用两个轴对物体进行移动也非常简单,只需要定义vx 和vy,并在每一帧将vx 加到x 属性上,vy 加到y 属性上。下面一个示例(Velocity2.as):

package 
{
	import flash.display.Sprite;
	import flash.events.Event;

	public class Velocity2 extends Sprite
	{
		private var ball:Ball;
		private var vx:Number = 5;
		private var vy:Number = 5;

		public function Velocity2()
		{
			init();
		}

		private function init():void
		{
			ball = new Ball();
			addChild(ball);
			ball.x = 50;
			ball.y = 100;
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(event:Event):void
		{
			ball.x +=  vx;
			ball.y +=  vy;
		}
	}
}

大家可以试着改变一下速度变量,别忘了还有负数。

5.1.4角速度

假如想让物体以每帧3像素的速度向45度的位置移动, 在这个描述中我们看不到 vx,vy的影子。但是,大家已经学习了使用 vx 和 vx 移动物体的例子,回忆一下第三章所讲的三角学,然后见图 5-4 所示,每一帧让小球以 3像素的速度向 45 度角的位置移动。在这个图中加入一条边后,是不是与图 5-5 非常相似了?恩,这就是一个由已知角度与斜边构成的直角三角形!

图5-4

图 5-4 长度及方向

图5-5

图 5-5 长度及方向形成一个直角三角形

请注意,这个三角形的两条位于 x,y 轴上的直角边。事实上, x 轴上的直角边长度等于小球所要移动的 x 距离, y 轴上的直角边等于 y 距离。不要忘记,在直角三角形中,只要知道一条边和一个角,就可以求出其它所有边和角的信息。因此,已知角度为 45 度,斜边长为 3像素,就可以使用 Math.cos 和 Math.sin 求出 vx 和 vy 的长度。

角的邻边长度为 vx,因为角的余弦值等于邻边/斜边。也可以说,邻边等于角的余弦值乘以斜边。同样,对边长为 vy 的边,因为角的正弦值等于对边/斜边,或是对边等于正弦乘以斜边。实际使用的代码:

vx = Math.cos(angle) * speed;
vy = Math.sin(angle) * speed; 

在使用 Math 函数之前不要忘记将 45 度角转换为弧度值!一旦获得了 vx 和 vy的值,就可以将它们加到物体的 x,y 坐标上,这样就运动起来了。

下面一个示例(VelocityAngle.as)代码如下:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	public class VelocityAngle extends Sprite
	{
		private var ball:Ball;
		private var angle:Number = 45;
		private var speed:Number = 3;
		
		public function VelocityAngle()
		{
			init();
		}
		
		private function init():void
		{
			ball = new Ball();
			addChild(ball);
			ball.x = 50;
			ball.y = 100;
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(event:Event):void
		{
			var radians:Number = angle * Math.PI / 2;
			var vx:Number = Math.cos(angle) * speed;
			var vy:Number = Math.sin(angle) * speed;
			ball.x += vx;
			ball.y += vy;
		}
	}
}

与前面 vx,vy 主要不同的地方是变成了 angle和speed, 计算出的速度向量作为局部变量被使用。当然,由于是一个简单的示例,角度(angle)和速率(speed)都不变,那么完全可以只计算一次,然后保存在类中作为变量。而对于更高级的运动来说,角度和速度会是应是不断变化的,所以 vx 和 vy 的值也是变化的。

只需要改变角度(angle)与速率(speed),就可以改变物体运动的速度及角度。

下面,让从向量的角度审视一下这个例子。

向量加法

当在一个平面坐标中有两个向量时,使用向量加法可以求出两个向量的合成向量。合成向量是一条从第一个向量的起点连接到最后一个向量终点的向量。图 5-6 中,可以看到三个向量相加及一个合成向量。

图5-6

图 5-6 向量加法

结果与向量所在的位置无关。可以说物体是先沿着这条路前进,再沿那条路前进,然后再到另一条路,顺序可以任意选择,或者可以说物体在这三条路上都走过一次。只要给出速度及方向就可以让物体向终点移动。在上一节例子中,如果将 x 轴的速度向量向右,再将 y 轴的速度向量竖直向下,那么合成向量就是全部向量的总合,见图 5-7。

图5-7

图5-7 速度作为向量

鼠标跟随

让我们使用速度向量的概念解释一下早前的一个问题。回到第三章,我们曾使用一个箭头指向鼠标位置的例子,在这个示例中使用 Math.atan2 计算鼠标与箭头之间的夹角,并使箭头旋转到这个角度上。

根据刚才所学的知识,计算出当前角度的速度向量。这个示例中同样使用 Arrow 类,所以大家要把它找出来,然后与 FollowMouse.as 文档类放在同一目录下:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	public class FollowMouse extends Sprite
	{
		private var arrow:Arrow;
		private var speed:Number = 5;
		
		public function FollowMouse()
		{
			init();
		}
		
		private function init():void
		{
			arrow = new Arrow();
			addChild(arrow);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(event:Event):void
		{
			var dx:Number = mouseX - arrow.x;
			var dy:Number = mouseY - arrow.y;
			var angle:Number = Math.atan2(dy, dx);
			arrow.rotation = angle * 180 / Math.PI;
			var vx:Number = Math.cos(angle) * speed;
			var vy:Number = Math.sin(angle) * speed;
			arrow.x += vx;
			arrow.y += vy;
		}
	}
}

这是一个相当复杂的效果,不过这里大家都能够看懂。先要计算出箭头与鼠标的 x 距离和 y 距离,并使用 Math.atan2 计算出它们的夹角。然后使用这个角度使箭头旋转,再使用 Math.cos 和 Math.sin 与速度相乘计算出 x,y 速度向量,最后将它们加到箭头的坐标上。

5.1.5速度扩展

Sprite,影片剪辑或任何的显示对象都有许多属性可以使用,而这些属性大多数都有比较大的取值范围,可以让我们制作出多种多样的动画。也许,速度一词用于这些属性上并不合适,但是在概念上是相同的,所以我也通常使用 v(velocity) 为变量命名的首字母。

在一个影片旋转的例子中, 通常在每一帧上将物体的 rotation属性增加一些数值,只要加入更大的数值就可以让物体旋转得更快,反之就会更慢。不论正确与否,我通常将旋转速度变量命名为 vr,表示旋转速度。同样使用 Arrow 影片,文档类RotationalVelocity.as:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	
	public class RotationalVelocity extends Sprite
	{
		private var arrow:Arrow;
		private var vr:Number = 5;
		
		public function RotationalVelocity()
		{
			init();
		}
		
		private function init():void
		{
			arrow = new Arrow();
			addChild(arrow);
			arrow.x = stage.stageWidth / 2;
			arrow.y = stage.stageHeight / 2;
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(event:Event):void
		{
			arrow.rotation += vr;
		}
	}
}

速度向量值为 5,方向为顺时针运动。同样的道理,可以将其它属性也加以改变,变量名仍使用 v 字系列,就像这样:

arrow.x += vx;
arrow.y += vy;
arrow.alpha += vAlpha;
arrow.rotation += vr;
arrow.scaleX = arrow.scaleY += vScale;
// etc. 

在本书中看到很多这样的示例,所以希望大家原谅我经常使用 v 字母作开头。

文件下载(已下载 2339 次)

发布时间:2011-3-15 9:50:33  阅读次数:6609

评论

由于网络审查方面的原因,本网站即日起关闭留言功能,若有什么问题可致信站长邮箱:fjphysics@qq.com。