返回“Flash基础理论课 - 目录”
弹性运动
一直以来,我都认为弹性运动将是ActionScript. 动画中最强大和最有用的物理学概念。几乎所有的物体都可以使用弹性运动。下面就来看看什么是弹性运动以及在Flash 编程中的应用。
如同本章开始时所提到的,弹性的加速度与物体到目标点的距离成正比例。想象一下现实中弹簧的性质。把一个小球拴在橡皮圈一头,再将另一头系在一个固定的地方。当小球悬在半空时,在没有施加外力的情况下,小球就处在目标点的位置上。再将小球微微拉动,松手后橡皮圈会对其施加一些外力,又将小球拽回了目标点。如果用最大力量将小球挪远,那么橡皮圈就会对小球施加很大的力。小球急速向目标点飞去,并朝着另一面运动。这时,小球的速度非常快。当它越过了目标点时,橡皮圈开始把它向回拉——改变其速度向量。这时,小球会继续运动,但是它运动得越远,所受到的拉力就越大。然后,速度向量为零,方向相反,一切再重新开始。最终,在反弹了几次后,速度逐渐慢下来直到停止——停在目标点上。
下面我们将这个过程翻译成 ActionScript。为了简化这个过程,我们先在一维坐标上进行实验。
一维坐标上的弹性运动
这里我们仍然使用可以拖拽的小球作为主体。默认位置还是x轴的0 点,使它具有运动到中心点的弹性。像使用缓动一样,需要一个变量保存弹性的数值。可以认为这个数同距离的比例,较大的弹性值会使弹性运动显得十分僵硬。较小的弹性值会使弹性运动像一条松紧带。我们选用 0.1 作为弹性 (spring) :
private var spring:Number = 0.1;
private var targetX:Number = stage.stageWidth / 2;
private var vx:Number = 0;
不要担心物体当前的位置,只需要知道如何确定这些变量与表达式就可以了。
然后加入运动代码并且找出与目标点的距离:
var dx:Number = targetX - ball.x;
下面计算加速度。加速度与距离成正比,也就是距离乘以 spring的值:
var ax:Number = dx * spring;
得到了加速度以后,我们就回到了熟悉的地方,把加速度加到速度向量中。
vx += ax;
ball.x += vx;
在写代码前,先来模拟一下运行时的数据。假设物体的x坐标为0,vx为0,目标x为100,spring值为0.1。执行过程如下:
1. 距离(100) 乘以 spring,得到 10。将它加入 vx 中,此时 vx变为10。再将vx 加入到速度向量上使得 x 位置变为10。
2. 下一次,距离(100-10) 等于90。加速度为90 乘以 0.1,结果为9。将结果加入 vx ,使 vx变为19。x坐标变为29。
3. 下一次,距离为71,加速度为7.1,将结果加到 vx 中,使 vx变成 26.1。x坐标变为55.1。
4. 下一次,距离为44.9,加速度为4.49,vx变成 30.59。x坐标变为85.69。
注意,每次的加速度随着物体越接近目标,变得越来越小,但是速度向量仍在不断增涨。虽然涨幅不像启初那样快,但是速度却越来越快。
几次过后,物体就超过了目标点,到达了117的位置。现在的距离是100 – 117,等于-17。将这一部分加入到速度向量中,会使物体的移动稍稍慢下来。
现在大家知道弹性是如何工作了吧,让我们来实践一下。与往常一样,要保证 Ball 类是有效的,使用下面这个文档类(Spring.as):
package {
import flash.display.Sprite;
import flash.events.Event;
public class Spring1 extends Sprite {
private var ball:Ball;
private var spring:Number = 0.1;
private var targetX:Number = stage.stageWidth / 2;
private var vx:Number = 0;
public function Spring1() {
init();
}
private function init():void {
ball = new Ball();
addChild(ball);
ball.y = stage.stageHeight / 2;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
var dx:Number = targetX - ball.x;
var ax:Number = dx * spring;
vx += ax;
ball.x += vx;
}
}
}
试验一下,可以看出一个类似于弹簧的效果,唯一的问题是它永远不会停止。前面在描述弹性运动时说过“速度逐渐慢下来直到停止”。由于小球每次摇摆时的距离相同,所以速度向量也相同,这样它就会以同样的速度来回摆动。这时,我们需要引入摩擦力。非常简单——只需要一个 friction变量,值为0.95。放在类开始的地方作为成员变量:
private var friction:Number = 0.95;
在enterFrame. 函数中,每次将vx 乘以 friction,就可以了。
文档类 Spring2.as的onEnterFrame. 方法如下:
private function onEnterFrame(event:Event):void {
var dx:Number = targetX - ball.x;
var ax:Number = dx * spring;
vx += ax;
vx *= friction;
ball.x += vx;
}
这样一来,程序就完整了,虽说只是个一维的弹性运动。试改变friction的值观察运动效果。认真理解这个程序会对大家非常有帮助。掌握这个程序后,就来看看二维的弹性运动吧。
二维弹性运动
好了,不用麻烦了,直接看程序(Spring3.as):
package {
import flash.display.Sprite;
import flash.events.Event;
public class Spring3 extends Sprite {
private var ball:Ball;
private var spring:Number = 0.1;
private var targetX:Number = stage.stageWidth / 2;
private var targetY:Number = stage.stageHeight / 2;
private var vx:Number = 0;
private var vy:Number = 0;
private var friction:Number = 0.95;
public function Spring3() {
init();
}
private function init():void {
ball = new Ball();
addChild(ball);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
var dx:Number = targetX - ball.x;
var dy:Number = targetY - ball.y;
var ax:Number = dx * spring;
var ay:Number = dy * spring;
vx += ax;
vy += ay;
vx *= friction;
vy *= friction;
ball.x += vx;
ball.y += vy;
}
}
}
我们看到,唯一不同之处就是加入了y轴的运动。但问题是,这个效果看上去还是很像一维的弹性运动。是的,虽然小球是沿着x,y轴运动,但它仍是直线运动。这是因为加速度的起点均为零,并且两种力的大小相同,因此是直线运动到目标点。
为了让效果更好,将初始 vx的值设置得大一些,比如 50。现在,看起来更加形象、真实了。但这只是个开始,更厉害的还在后面呢。
向移动目标运动
实现弹性运动并不需要目标点是固定。前面在介绍缓动运动时,我们就介绍了一个非常简便快捷的方法——小球跟随鼠标。要让小球跟随鼠标位置非常简单,只要将原来的targetX和targetY变成鼠标位置即可。在弹性运动中也是如此,每一帧都要计算物体与目标点的距离,然后再确定加速度。
效果非常 Cool,我认为值得大家多写几遍。事实上,代码的变化并不大,只需将前面的程序做如下修改:
var dx:Number = targetX - ball.x;
var dy:Number = targetY - ball.y;
改为:
var dx:Number = mouseX - ball.x;
var dy:Number = mouseY - ball.y;
设置完成后,就可以将targetX和targetY 这两个变量声明删除。如果不删,也不会有什么问题。
弹簧在哪?
下面我们将看到小球在橡皮圈上运动的情况。但是似乎还需要一个可见的橡皮圈,只需要用绘图 API 绘制一条线即可!绘图指令可以直接写在Sprite的子类中。在很多复杂的程序中,我们可能需要单独创建一个空影片当作绘图层来用。
方法很简单。在确定了小球的位置后,调用 clear() 将之前的线条擦除。然后重新设置 lineStyle. 并绘制出小球与鼠标的连线。我们只需要在enterFrame. 函数中执行如下操作:
graphics.clear();
graphics.lineStyle(1);
graphics.moveTo(ball.x, ball.y);
graphics.lineTo(mouseX, mouseY);
非常有趣!我们还能做什么?再加上重力怎么样?这样小球看上去更像是悬在空中。很简单,只需要在每帧的vy 中加入重力(gravity)。以下代码结合了重力与画线指令(Spring5.as):
package {
import flash.display.Sprite;
import flash.events.Event;
public class Spring5 extends Sprite {
private var ball:Ball;
private var spring:Number = 0.1;
private var vx:Number = 0;
private var vy:Number = 0;
private var friction:Number = 0.95;
private var gravity:Number = 5;
public function Spring5() {
init();
}
private function init():void {
ball = new Ball();
addChild(ball);
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
var dx:Number = mouseX - ball.x;
var dy:Number = mouseY - ball.y;
var ax:Number = dx * spring;
var ay:Number = dy * spring;
vx += ax;
vy += ay;
vy += gravity;
vx *= friction;
vy *= friction;
ball.x += vx;
ball.y += vy;
graphics.clear();
graphics.lineStyle(1);
graphics.moveTo(ball.x, ball.y);
graphics.lineTo(mouseX, mouseY);
}
}
}
执行后的结果如图 8-2 所示。请注意,我们加入的重力 (gravity) 只有5,只是为了让小球下坠。如果重力太小的话,就看不到效果了。
图8-2 可见的鼠标弹簧
我们刚刚又违背了真实的物理学。当然,不能在物体上施加“累积重力”!重力是一个常数,取决于物体所在星球的大小和质量。我们所能做的就是累加物体的质量,以便在物体上产生多个重力。因此,严格来讲,重力还应该是0.5,而质量应该在10左右。接下来,用质量乘以重力等于5。或者可将gravity变量名改为forceThatGravityIsExertingOnThisObjectBasedOnItsMass。好了,大家明白这个意思后我就可以节省一下空间使用变量名 gravity。
同样,试验一下这个程序。试减少gravity与spring的值,试改变friction的值。我们可以有无限多种数字的组合,可以建立出任何类型的重力系统。
联系客服