转 第八章 缓动与弹性运动(1)(as3.0)

  很难相信我们居然用了七章才把基础的内容介绍完,现在进入第八章,这里是高级内容
的起点。从这里开始内容也开始变得越来越有趣了,前面的章节都是些常用的概念与技术。
从今天开始,每章只着重介绍一两种特殊的运动。
     本章将介绍缓动运动(成比例速度)与弹性运动(成比例加速度),不用担心它们只是
两个名词术语,这章可以快速地略读。我会给出很多例子程序,可以使大家充分了解这项技
术的强大。
成比例运动
    缓动(ease)与弹性(spring)联系紧密。这两种方法都是将对象(通常指 Sprite 或
MovieClip)从某一点移动到目标点。使用缓动运动(Easing),如同让影片滑动到目标并停
止。使用弹性运动(Springing),会产生向前或向后的反弹,最终停止在目标点位。两种方
法具有一些共同点:
■ 需要一个目标点;
■ 确定到目标点的距离;
■ 成比例地将影片移动到目标点——距离越远,移动速度越快。
     缓动运动(easing)与弹性运动(springing)的不同之处在于移动的比例。缓动运动时,
速度与距离成正比,离目标越远,物体运动速度越快。当物体与目标点非常非常接近时,就
几乎不动了。
     弹性运动时,加速度与距离成正比。如果物体与目标离得很远,再用上加速度,会使移
动速度非常快。当物体接近目标时,加速度会减小,但依然存在!物体会飞过目标点,随后
再由反向加速度将它拉回来。最终,用摩擦力使其静止。
     下面,我们分别看一下这两种方法,先从缓动(easing)开始。

 

缓动(Easing)
     首先说明缓动的种类不只有一种。在 Flash IDE 中,制作补间动画时,我们就可以看
到 “缓动输入”(ease in) 和 “缓动输出”(ease out)。下面所讨论的缓动类型与运
动补间的 “缓动输出”相似。在本章后面的 “高级缓动”一节,将会给大家一个网站连接,
在那里可以学习制作所有缓动的效果。
简单缓动
    简单缓动是个非常基础概念,就是将一个物体移到别处去。创建这个“运动效果”时,
希望物体能在几帧内慢慢移动到某一点。我们可以求出两点之间的夹角,然后设置速度,再
使用三角学计算出 vx 和 vy,然后让物体运动。每一帧都判断一下物体与目标点的距离,
如果到达了目标则停止。 这种运动还需要一定条件的约束才能实现, 但如果要让物体运动得
很自然,显然这种方法是行不通的。
    问题在于物体沿着固定的速度和方向运动,到达目标点后,立即停止。这种方法,用于
表现物体撞墙的情景,也许比较合适。但是物体移动到目标点的过程,就像是某个人明确地
知道他的目的地,然后向着目标有计划地前进,起初运动的速度很快,而临近目标点时,速
度就开始慢下来了。换句话讲,它的速度向量与目标点的距离是成比例的。
    先来举个例子。比如说我们开车回家,当离家还有几千米的距离时,要全速前进,当离
开马路开进小区时速度就要稍微慢一点儿。 当还差两座楼时就要更慢一点儿。在进入车库时,
速度也许只有几迈。 当进入停车位时速度还要更慢些,在还有几英尺的时候, 速度几乎为零。
     如果大家注意观察就会发现,这种行为就像关门、推抽屉一样。开始的速度很快,然后
逐渐慢下来。
     在我们使用缓动使物体归位时,运动显得很自然。简单的缓动运动实现起来也非常简单,
比求出夹角,计算 vx,vy 还要简单。下面是缓动的实现策略:
1. 确定一个数字作为运动比例系数,这是个小于 1 的分数;
2. 确定目标点;
3. 计算物体与目标点的距离;
4. 用距离乘以比例系数,得出速度向量;
5. 将速度向量加到当前物体坐标上;
6. 重复 3 到 5 步。

    我们先来解释一下这个过程,看看在 ActionScript 中是怎样实现的。
    首先,确定一个分数作为比例系数。我们说过,速度与距离是成比例的。也就是说速度
是距离的一部分。比例系数在 0 和 1 之间,系数越接近 1,运动速度就会越快;系数越接
近 0,运动速度就会越慢。但是要小心,系数过小会使物体无法到达目标。开始我们以 0.2
作为系数,这个变量名就叫 easing。初始代码如下:
var easing:Number = 0.2;
接下来,确定目标。只需要一个简单的 x,y 坐标,选择舞台中心坐标再合适不过了。
var targetX:Number = stage.stageWidth / 2;
var targetY:Number = stage.stageHeight / 2;
下面,确定物体到达目标的距离。假设已经有一个名为 ball 影片,只需要从 ball 的 x,y
坐标中减去目标的 x,y。
var dx:Number = targetX - ball.x;
var dy:Number = targetY - ball.y;
速度等于距离乘以比例系数:
vx = dx * easing;
vy = dy * easing;
下面,大家知道该怎么做了吧:
ball.x += vx;
ball.y += vy;
最后重复步骤 3 到步骤 5,因此只需加入 enterFrame 处理函数。
让我们再看一下这三个步骤,以便将它们最大程度地简化:
var dx:Number = targetX - ball.x;
var dy:Number = targetY - ball.y;
vx = dx * easing;
vy = dy * easing;
ball.x += vx;
ball.y += vy;
把前面四句简化为两句:
vx = (targetX - ball.x) * easing;
vy = (targetY - ball.y) * easing;
ball.x += vx;
ball.y += vy;
如果大家觉得还不够精简,还可以进一步缩短:
ball.x += (targetX - ball.x) * easing;
ball.y += (targetY - ball.y) * easing;
    在开始学习使用缓动时,也许大家会比较喜欢用详细的句型,让程序看上去更加清晰。
但是当你使过几百次后,就会更习惯用第三种写法。下面,我们选用第二种句型,以加强对
速度的理解。

     现在就来看一下脚本动作,依然延用 Ball 类。以下是文档类 Easing1.as:
package {
 import flash.display.Sprite;
 import flash.events.Event;
 public class Easing1 extends Sprite {
  private var ball:Ball;
  private var easing:Number=0.2;
  private var targetX:Number=stage.stageWidth / 2;
  private var targetY:Number=stage.stageHeight / 2;
  public function Easing1() {
    trace(targetX,targetY);
    init();
  }
  private function init():void {
    ball=new Ball ;
    addChild(ball);
    addEventListener(Event.ENTER_FRAME,onEnterFrame);
  }
  private function onEnterFrame(event:Event):void {
    var vx:Number=(targetX - ball.x) * easing;
    var vy:Number=(targetY - ball.y) * easing;
     ball.x+= vx;
     ball.y+= vy;
   }
  }
}
      试改变 easing 的值,观察运动效果。
      下面,大家可以让小球变成可以拖拽的,与第七章所做的拖拽与抛落效果很像。在点击
小球时开始拖拽,同时,删除 enterFrame 处理函数并且用 stage 侦听 mouseUp。在
mouseUp 函数中,停止拖拽,删除 mouseUp 方法,并重新开始 enterFrame。下面是文档类
Easin2.as :
package {
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.events.MouseEvent;
  public class Easing2 extends Sprite {
   private var ball:Ball;
   private var easing:Number=0.2;
   private var targetX:Number=stage.stageWidth / 2;
   private var targetY:Number=stage.stageHeight / 2;
   public function Easing2() {
     init();
   }
   private function init():void {
     ball=new Ball ;
     addChild(ball);
     ball.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown);
     addEventListener(Event.ENTER_FRAME,onEnterFrame);
   }
    private function onMouseDown(event:MouseEvent):void {
      ball.startDrag();
      removeEventListener(Event.ENTER_FRAME,onEnterFrame);
      stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUp);
    }
    private function onMouseUp(event:MouseEvent):void {
      ball.stopDrag();
      addEventListener(Event.ENTER_FRAME,onEnterFrame);
      stage.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp);
    }
   private function onEnterFrame(event:Event):void {
     var vx:Number=(targetX - ball.x) * easing;
     var vy:Number=(targetY - ball.y) * easing;
     ball.x+= vx;
     ball.y+= vy;
   }
  }
}

 

缓动何时停止
     在物体缓动运动到目标点时,物体最终会到达目标点并且完成缓动效果。但是,即使不
显示该对象,缓动代码仍在执行中,这一来浪费了 CPU 资源。当物体到达目标时,应该停
止执行代码。判断物体是否到达目标的方法非常简单,就像这样:
if(ball.x == targetX && ball.y == targetY) {
     // code to stop the easing
}
     但是这里要注意一些技巧。
     我们所讨论的缓动类型涉及到了著名的 Xeno 悖论。Xeno 也是位希腊人,爱好测量实
验。Xeno 将运动分解为下面几个步骤:物体要从 A 点到达 B 点,它首先要移动到两点间
一半的距离。然后物体再从该点出发,到达与 B 点距离一半的距离。然后再折半。每次只
移动离目标一半的距离,但永远无法准确地达到目标。
     这个悖论听起来是非常符合逻辑的。但是很明显,我们确实将物体从一点移动到了另一
点,这样看来他的说法有些问题。到 Flash 中看看,影片在 x 轴上的位置为 0,假设要将
它移动到 x 轴为 100 的位置。按照悖论所说,设缓动系数为 0.5,这样每次运动到离目标
一半的距离。过程如下:
■ 从 0 点开始,经过 1 帧,到达 50。
■ 第 2 帧,到达 75。
■ 剩下的距离是 25。它的一半是 12.5 ,所以新的距离就是 87.5。
■ 按照这种顺序,位置将变化为 93.75, 96.875, 98.4375 等等。20 帧以后,将到达
99.999809265。
     从理论上讲位置越来越接近目标,但是永远无法准确地到达目标点。然而,在代码中进
行试验时,结果就发生了一些微妙的变化。归根结底问题就在于“一次最少能移动多少个像
素”,答案是 1/20。事实上,二十分之一像素有个特有的名字:twip (缇)。在 Flash 内
部计算单位都采用 twip 像素,包括所有 Sprite 影片,影片剪辑和其它舞台对象。因此,
在显示影片位置时,这个数值永远是 0.05 的倍数。
     下面举个例子,一个影片要到达 100 的位置,而它所到达的最接近的位置事实上是
99.5。再分隔的距离,就是加上 (100 – 99.95) /2。相当于加上了 0.025,四十分之一像
    超出了 twip 是能够移动的最小值, 因此无法加上“半个 twip” 结果是只增加了 0 像
                                                             
素。
素。如果大家不信的话,可以亲自试一下(提示:将代码放入框架类中的 init 方法):
var sprite:Sprite;
sprite = new Sprite();
addChild(sprite);
sprite.x = 0;
var targ:Number = 100;
for(var i:Number = 0; i < 20; i++) {
     trace(i + ": " + sprite.x);
     sprite.x += (targ - sprite.x) * .5;
}
     循环 20 次,将影片移动离目标一半的距离,这是基本缓动应用。将代码放入 for 循
环,只是为了测试其位置,并不在于观察物体运动。循环到第 11 次时,影片已经到达了
99.95,这已经是它能够到达的最远的地方了。
     长话短说,影片并非无限地接近目标,但是它确实永远无法准确地到达目标点。这样一
来,缓动代码就永远不会停止。我们要回答的问题是 “哪里才是物体最接近的目标位置?” 
这需要确定到目标点的距离是否小于某个范围。 我发现在很多软件中,  如果物体与目标点的
距离相差在一个像素以内,就可以说它已经到达了目标点,即可停止缓动了。
在处理二维坐标时,可以使用第三章所介绍的公式来计算点间距离:
distance = Math.sqrt(dx * dx + dy * dy)
      如果只处理一维坐标点,如只移动一个轴的位置,就需要使用距离的绝对值,因为它有
可能是个负数,使用 Math.abs 方法。
      OK,说得很多了,来写代码吧。这个简单的文档类,演示了如何关闭缓动运动
(EasingOff.as):
package {
  import flash.display.Sprite;
  import flash.events.Event;
  public class EasingOff extends Sprite {
   private var ball:Ball;
   private var easing:Number = 0.2;
   private var targetX:Number = stage.stageWidth / 2;
   public function EasingOff() {
     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;
     if (Math.abs(dx) < 1) {
      ball.x = targetX;
      removeEventListener(Event.ENTER_FRAME, onEnterFrame);
      trace("done");
     } else {
      var vx:Number = dx * easing;
      ball.x += vx;
     }
   }
  }
}
      此例中,将缓动公式分解使用,首先计算出距离,因为我们需要知道是否该停止缓动。
大家应该知道为什么要使用 dx 的绝对值了吧。如果小球在目标点的右边,dx 的值总是负
的,if (dx < 1) 的结果永远为真,这就会使缓动停止。而使用 Math.abs,就可以判断实
际距离是否小于 1。
      记住,如果将拖拽与缓动相结合,要在放开小球时,将运动代码重新启用。

移动的目标
    上面例子中的目标点都是单一且固定的,这些似乎还不能满足我们的要求。事实上,
Flash 并不关心物体是否到达目标,或目标是否还在移动。它只会问 “我的目标在哪里?
距离有多远?速度是多少?”,每帧都如此。因此,我们可以很容易将目标点改为鼠标所在
的位置,只需将原来 targetX 和 targetY 的地方,改成鼠标的坐标 (mouseX 和 mouseY)。
以下是一个比较简单的版本(EaseToMouse.as):
package {
  import flash.display.Sprite;
  import flash.events.Event;
  public class EaseToMouse extends Sprite {
   private var ball:Ball;
   private var easing:Number = 0.2;
   public function EaseToMouse() {
     init();
   }
   private function init():void {
     ball = new Ball();
     addChild(ball);
     addEventListener(Event.ENTER_FRAME, onEnterFrame);
   }
   private function onEnterFrame(event:Event):void {
     var vx:Number = (mouseX - ball.x) * easing;
     var vy:Number = (mouseY - ball.y) * easing;
     ball.x += vx;
     ball.y += vy;
   }
  }
}

    移动鼠标观察小球跟随情况,是不是距离越远速度越快。
    试想还有没有其它可移动的目标。当然还可以是一个影片向着另一个影片缓动。在早先
的 Flash 时代,鼠标追踪者(mouse trailers)——即一串影片跟踪者鼠标的效果——曾
经风靡一时。缓动就是制作这种效果的方法之一。第一个影片缓动到鼠标上,第二个影片缓
动到第一个影片上,第三个再缓动到第二个上,依此类推。大家不妨一试。

缓动不仅限于运动
    本书中,有很多简单的例子程序。在这些例子中,我们主要是计算影片所用变量的值。
通常,使用 x,y 属性控制物体的位置。不过别忘了 Sprite 影片,影片剪辑以及各种显示
对象还有很多其它可以操作的属性,而且基本都是用数字表示的。所以在读例子程序时,也
应该试用其它属性代替这个例子中的属性。下面给大家一些启发。

 


透明度
将缓动用在 alpha 属性上。开始设置为 0 ,目标设置为 1 :
ball.alpha = 0;
var targetAlpha:Number = 1;
在 enterFrame 函数中,使用缓动可以实现影片淡入效果:
ball.alpha += (targetAlpha - ball.alpha) * easing;
若将 0 和 1 颠倒过来就可以实现影片的淡出效果。

旋转
    设置一个 rotation 属性和一个目标 rotation。当然,还需要一个能够表现旋转对象,
比如一个箭头(arrow):
arrow.rotation = 90;
var targetRotation:Number = 270;
arrow.rotation += (targetRotation - arrow.rotation) * easing;

颜色
    如果大家想挑战一下,可以在 24 位色彩中使用缓动。设置好 red,green,blue 的初始
值,使用缓动单独表现每一色彩元素的值,然后将它们再组合成 24 位色彩值。例如,我们
可以从 red 缓动到 blue。初始颜色如下:
red = 255;
green = 0;
blue = 0;
redTarget = 0;
greenTarget = 0;
blueTarget = 255;
在 enterFrame 处理函数中的每一帧执行缓动。这里只表现一个 red 值:
red += (redTarget - red) * easing;
再将这三个数值组合为一个数(如第四章介绍的):
col = red << 16 | green << 8 | blue;
最后可以在 ColorTransform (见第四章),线条颜色或填充色中使用该颜色值。

高级缓动
    现在我们已经看到简单的缓动效果是如何实现的了,大家也许正在考虑如何使用更复杂
的缓动公式制作一些效果。例如,在开始时比较缓慢,然后渐渐开始加速,最后在接近目标
时再将速度慢下来。或者希望在一段时间或若干帧内完成缓动效果。
    Robert Penner 以收集、编制和实现缓动公式而出名。我们可以在
www.robertpenner.com 中找到他的缓动公式。在他写这些内容时 AS 3 版本还没有出现,
但是用我们前面几章所学知识,将它们转化为 AS 3 版本的也是件非常容易的事。
    OK,下面进入 Flash 中我最喜欢的一个主题:弹性运动(Springing)。

弹性运动
    一直以来,我都认为弹性运动将是 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,只是为了
让小球下坠。如果重力太小的话,就看不到效果了。
    我们刚刚又违背了真实的物理学。当然,不能在物体上施加“累积重力”!重力是一个
常数,取决于物体所在星球的大小和质量。我们所能做的就是累加物体的质量,以便在物体
上产生多个重力。因此,严格来讲,重力还应该是 0.5,而质量应该在 10 左右。接下来,
用质量乘以重力等于 5。或者可将 gravity 变量名改为
forceThatGravityIsExertingOnThisObjectBasedOnItsMass。好了,大家明白这个意思后我
就可以节省一下空间使用变量名 gravity。
    同样,试验一下这个程序。试减少 gravity 与 spring 的值,试改变 friction 的值。
我们可以有无限多种数字的组合,可以建立出任何类型的重力系统。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值