roadProgram的专栏

一个程序员的路

转 第十九章 flash实用技巧 (2)(as3.0)

基于时间的动画
      如果要让动画中物体的速度是一致的,    那么基于时间的动画就是我们要使用的方法。  这
种方法在一些游戏中比较常用。我们知道,帧和计时器动画都不能以特定的速率播放。一个
复杂的动画在一台又老又慢的电脑上运行可能要比最初设计的速度慢上许多。       我们马上会看
到,使用基于时间的动画无论最终动画运行的帧频如何,都将获得可靠的速度。
      首先要改变考虑速度的方法。到目前为止,在我说 vx = 5 时,我们使用的单位是像素
每帧(pixels per frame)  。换句话讲,每进入新的一帧物体都将在 x 轴上运动 5 像素。在计
时器动画中,当然,就应该是每次定时间隔移动 5 像素。
      对于时间动画,我们将使用真正的时间单位,如秒。由于我们是处理完整的一秒,而非
其中的一部分,因此这个速度值就要更大一些。如果某个物体的速度是每帧 10 像素,每秒
30 帧的速度运动,     大约每秒 300 像素。 比如下面这个例子,我从第六章的 Bouncing2.as 文
档类中截取了一部分并进行了一些变化,见下面粗体部分(也可在 TimeBased.as 中找到)      :
package {
  import flash.display.Sprite;
  import flash.events.Event;
  import flash.utils.getTimer;
  public class TimeBased extends Sprite {
   private var ball:Ball;
   private var vx:Number;
   private var vy:Number;
   private var bounce:Number = -0.7;
   private var time:Number;
   public function TimeBased() {
    init();
}
private function init():void {
  stage.frameRate = 10;
  ball = new Ball();
  ball.x = stage.stageWidth / 2;
  ball.y = stage.stageHeight / 2;
  vx = 300;
  vy = -300;
  addChild(ball);
  time = getTimer();
  addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(event:Event):void {
  var elapsed:Number = getTimer() - time;
  time = getTimer();
  ball.x += vx * elapsed / 1000;
  ball.y += vy * elapsed / 1000;
  var left:Number = 0;
var right:Number = stage.stageWidth;
var top:Number = 0;
var bottom:Number = stage.stageHeight;
if (ball.x + ball.radius > right) {
  ball.x = right - ball.radius;
  vx *= bounce;
      } else if (ball.x - ball.radius < left) {
        ball.x = left + ball.radius;
        vx *= bounce;
      }
      if (ball.y + ball.radius > bottom) {
        ball.y = bottom - ball.radius;
        vy *= bounce;
      } else if (ball.y - ball.radius < top) {
        ball.y = top + ball.radius;
        vy *= bounce;
      }
    }
  }
}
如上所描述,我改变了对速度的计算,让它们使用固定的值,而非随机值。然后我创建了一
个名为 time 的变量,让它等于 flash.utils.getTimer 函数的结果。getTimer 函数非常简单。
它返回影片已经运行了的毫秒数 —— 这就是它全部的工作。我们没有办法清除它,重启它,
改变它,等等。它只是一个计数器。
        看起来它似乎用处不大,但是如果先调用一次 getTimer 将值保存起来,稍后再调用它
一次,将两个结果相减,我们就能知道确切的—— 毫秒 ——这两次调用之间经过了多少时
间。
        这就是策略:在每帧的开始时调用 getTimer 计算与上一帧间隔了多长时间。如果将它
除以 1,000,我们将得到以秒为单位的间隔时间,这是个以秒为单位的分数。由于我们的 vx
和 vy 现在是以像素每秒来计算的,因此可以让它们去乘以这个分数,这样就知道要对物体
移动多少了。同样,不要忘记重新设置 time 变量的值,以便让下一帧进行计算。
     测试一下,  我们将看到小球移动的速度几乎与最初的速度相同!但是真正令人激动的是
我们可以以任何帧频来发布这个影片,它仍然可以以同样的速度运动!通过修改
stage.frameRate 的值,试验一下高到 1,000 fps,低到 10 fps,你会看到小球的速度是相同
的。当然,较高的频率会让动画更加流畅,而较低的频率则会十分不连贯,但是速度是一致
的。
     大家可以把这个技术应用在本书任何一个包含速度的例子中。  如果这样的话, 还需要将
相似的技术应用在加速度或外力上,如重力,因为它们也是基于时间的。加速度肯定要比转
前要大很多,因为加速度被定义为速度对时间的变化率。例如,重力大约为 32 英尺/秒/
秒。
     如果在一个 30 fps 帧的动画中,重力为 0.5 的话,那么现在就应该是 450。计算方法
0.5 * 30 * 30。然后像这样将它加入:
       vy += gravity * elapsed / 1000;
在最后一个例子中加入 450 重力后测试一下。我们会看到它与帧动画中加入重力 0.5 的效
果是相同的。使用这种技术的一个技巧是将帧频设置得非常高,如 100。虽然没有机器能够
与真正的帧频相吻合,但是基于时间的动画将保证每个人看到的影片运行得都是最流畅的。
同质量物体的碰撞
    回忆一下第十一章的动量守恒。那里有非常严谨的代码。不过,当两个相同质量的物体
发生碰撞时,我们实现起来可以更简单一些。基本原理是,两个物体沿着碰撞的线路交换它
们的速度。同时还要用坐标旋转来决定碰撞的线路,以及物体的速度,这样就摆脱了复杂的
动量守恒公式。来看看它是如何工作的,让我们回到文件 MultiBilliard2.as 中,将用它作为
下一个例子 SameMass.as 的基础。我就不再列出原先所有的代码了,因为它实在是一个很
大的文件。但是,我们要来看一下创建所有小球的 for 循环:
for(var i:uint = 0; i < numBalls; i++) {
var radius:Number = Math.random() * 50 + 20;
  var ball:Ball = new Ball(radius);
  ball.mass = radius;
  ball.x = Math.random() * stage.stageWidth;
  ball.y = Math.random() * stage.stageHeight;
  ball.vx = Math.random() * 10 - 5;
  ball.vy = Math.random() * 10 - 5;
  addChild(ball);
  balls.push(ball);
}
对于新的文件来说,要把粗体字的部分改为这一行:
      var radius:Number = 30;
让所有小球大小都相同,消除了质量的概念,相当于给它们相同的质量。
接下来,看一下 checkCollision 函数。请找到这一部分:
// 旋转 ball0 的速度
var vel0:Point = rotate(ball0.vx,
ball0.vy,
sin,
cos,
true);
// 旋转 ball1 的速度
var vel1:Point = rotate(ball1.vx,
ball1.vy,
sin,
cos,
true);
// 碰撞反应
var vxTotal:Number = vel0.x - vel1.x;
vel0.x = ((ball0.mass - ball1.mass) * vel0.x +
2 * ball1.mass * vel1.x) /
(ball0.mass + ball1.mass);
vel1.x = vxTotal + vel0.x;
这一部分找出了碰撞线路上的速度,根据物体的质量求出碰撞的结果。“碰撞反应”部分是
动量守恒的要素,这就是我们可以消除的部分。我们可以让 vel0 和 vel1 进行交换,就可
以很容易地替换它了。整个代码段如下:
// 旋转 ball0 的速度
var vel0:Point = rotate(ball0.vx,
ball0.vy,
sin,
cos,
true);
// 旋转 ball1 的速度
var vel1:Point = rotate(ball1.vx,
ball1.vy,
sin,
cos,
true);
// 交换两个速度
var temp:Point = vel0;
vel0 = vel1;
vel1 = temp;
这里甚至还可以再优化, 但是为了代码的清晰我就不做改变了。现在我们已经摆脱了一小块
儿数学问题,测试一下修改前与修改后的文件,所得的结果是相同的。

声音整合
      本书一直没有提到声音的使用。  因为声音并不是动画的直接组成部分,    优质的声音效果
可以让 Flash 影片更加真实、引人入胜。
      我们可以使用不同的方法来加入声音。回溯到 Flash IDE 最早的程序版本,我们有一
种使用声音的特殊方式 —— 将声音导入到库,      再把它加入到帧里面。  这不是我要介绍的方
法。我将介绍在 AS 3 中使用声音的一些基础。
     AS 3 的 Sound 类有了很大的变化,而且还有几个额外的类可以帮助我们对其进行修
饰。这里没有太多的空间进行深入的讨论,     但是有一个方面我想应该对于我们这本书来说会
很有用。这就是当动画中发生某种事件时,应该播放声音。最明显的就应该是碰撞了。一个
小球碰到墙上或其它小球上,我们会听到“砰”“啵嘤”“啪”或其它什么声音。因此,我
                                                、      、
们需要掌握通过 ActionScript 来启动声音的能力。
  这个例子中,我们还要回到 Bouncing2.as,小球会与墙壁产生碰撞。每次碰撞到墙壁时,
我想让它发出声音。新的类请见 SoundEvents.as。
      首先,需要有声音效果。在网上有许多免费的声音效果资源。其中最火的 Flash 声音
网 站 是 FlashKit 。 他 们 的 音 乐 文 件 除 了 有 loop 以 外 , 还 有 一 个 声 音 效果库
www.flashkit.com/soundfx/。这些效果被分类为 Cartoon,Interfaces,Mechanical 等等,而且
这个网站有超过 6,000 多个声音效果文件,所以您应该能够找到适合自己的音效。我们可
                                    ,找到自己喜欢的文件后,以 MP3 格式进行下载。将
以在页面上直接进行预览(preview)
它保存到硬盘上与最终发部的影片放在同一目录下。
      有时我们要重新给文件一个更为简单的名字。例如,我下载了一个“boing”声音,我
就将它重命名为 boing.mp3。
      在 AS 3 中使用声音的基础实际上要比 AS 2 中简单一些。
首先,我们需要创建声音对象。假设在类中已经声明了一个名为 boing 的变量:
     private var boing:Sound;
创建一个声音对象就这么简单:
     boing = new Sound();
当然,如同大多数 AS 3 的类一样,Sound 类也在包中,flash.media 包,因此要确保先导
入 flash.media.Sound。
     连接一个外部声音对象最简单的方法是在构造函数中传入一个请求(request) 。
     就像读取外部图像(第四章)一样,我们不能直接传入外部声音文件的 URL。而是要
将它包装到 URLRequest 中(flash.net.URLRequest,需要导入它) 。应该像这样:
     boing = new Sound(new URLRequest("boing.mp3"));
全部内容就是这样。现在声音已经准备好。我们要做的就是:
     mySound.play();
无论在哪儿都会播放出这个音效。在 play 中有一些可选参数,如偏移的毫秒数,以及播放
的次数, 但是默认情况下是从声音的起始位置播放一次声音,这已经满足了我们通常的需求。
以下是 SoundEvents.as 的全部代码,展示了 Sound 对象的创建,无论何时小球碰撞到墙上,
都会播放声音。
package {
import flash.display.Sprite;
import flash.events.Event;
import flash.media.Sound;
import flash.net.URLRequest;
public class SoundEvents extends Sprite {
 private var ball:Ball;
 private var vx:Number;
 private var vy:Number;
 private var bounce:Number = -0.7;
 private var boing:Sound;
 public function SoundEvents() {
   init();
 }
 private function init():void {
   boing = new Sound(new URLRequest("boing.mp3"));
   ball = new Ball();
   ball.x = stage.stageWidth / 2;
   ball.y = stage.stageHeight / 2;
   vx = Math.random() * 10 - 5;
   vy = -10;
   addChild(ball);
   addEventListener(Event.ENTER_FRAME, onEnterFrame);
 }
 private function onEnterFrame(event:Event):void {
   ball.x += vx;
   ball.y += vy;
   var left:Number = 0;
  var right:Number = stage.stageWidth;
  var top:Number = 0;
  var bottom:Number = stage.stageHeight;
  if (ball.x + ball.radius > right) {
    boing.play();
    ball.x = right - ball.radius;
    vx *= bounce;
  } else if (ball.x - ball.radius < left) {
    boing.play();
    ball.x = left + ball.radius;
    vx *= bounce;
  }
  if (ball.y + ball.radius > bottom) {
    boing.play();
    ball.y = bottom - ball.radius;
    vy *= bounce;
  } else if (ball.y - ball.radius < top) {
    boing.play();
    ball.y = top + ball.radius;
    vy *= bounce;
  }
}
  }
}
    测试一下影片看一看 … … 听一听拥有声音以后带来的不同感受。当然,要找到正确
的声音用在正确的环境下,也不要加得太多,因为这本身也是一门艺术

(如果要转载请注明出处http://blog.sina.com.cn/jooi,谢谢)

阅读更多
个人分类: Flash Flex
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭