转 第二章 ActionScript 3.0 动画基础(1)(as3.0)

来源:互联网 发布:php cdn缓存 编辑:程序博客网 时间:2024/05/22 06:11

类和面向对象编程
      类(Class)和面向对象(Object Oriented),对于有些读者来说可能还没接触过而有些读
者可能已经在 AS (或其它语言) 中使用过很多年了,为了让大家都能学会,我会扼要的介
绍一下这些基础知识。就算是 AS 2 的 OOP 专家也希望能略读下这一段,因为 AS 3.0 的
                                                                  只要你在 Flash 中
工作原理确实发生了很大的变化。  如果你说你从没用过类,那你就错了,
写过代码,那么实际上就已经使用了类。类可以简单理解为一种对象, MovieClip 就是影
片剪辑的类,而文本框、影片剪辑、按钮、字符串和数值等都有它们自己的类。
      一个类最基本的两个部分:属性(数据或信息),行为(动作或它能做的事)。属性
(Property)指用于保存与该类有关的信息变量,   行为(Behavior)就是指函数,如果一个函数
是这个类中的一部分,那么我们就称它为方法(Method)。
一个基本的类:
      常用 Flash 的朋友都知道,我们可以在库中创建一个元件,用这个元件可以在舞台上
创建出很多的实例。与元件和实例的关系相同,类就是一个模板,而对象(如同实例)就是类
的一个特殊表现形式。下面来看一个类的例子:
package {
  public class MyClass {
   public var myProperty:Number = 100;
   public function myMethod() {
     trace("I am here");
   }
  }
}
    先来说明一下这段代码。在这里有些新的知识,对于 AS 2 老手也如此:包的声明。包
            作用就是把相关的类进行分组。知道这一点就够了,我们不再进行深入的讨论,
(Package) ,
本书的示例甚至不会用到包。Package 这个关键字和一对大括号是必需有的,我们理解为默
认包,紧随其后的就是类的定义。
另一个变化是 AS3.0 中的类拥有了访问关键字。访问关键字是指:一个用来指定其它代码
是否可访问该代码的关键字。 public (公有类) 关键字指该类可被外部任何类的代码访问。
本书中所有示例的类都是 public 的。在深入学习了 AS 3.0 后,我们会发现不是所有类都
是公有的,甚至还有多重的类,这些内容超出了本书的谈论范围。
    本例中我们可以看到,这个类的名字为 MyClass,后面跟一对大括号。在这个类中有两
种要素,一个是名为 myProperty 的变量,另一个是名为 myMethod 的函数。
包(Package)
    包主要用于组织管理类。包是根据类所在的目录路径所构成的,并可以嵌套多层。包名
所指的是一个真正存在的文件夹,用 “.”进行分隔。例如,有一个名为 Utils 的类,存
在于文件夹 com/ friendsofed/ makingthingsmove/ 中(使用域名作为包名是一个不成文
的规定,目的是保证包名是唯一的)。这个类就被写成
com.friendsofed.makingthingsmove.Utils。
在 AS 2 中,使用整个包名来创建一个类,例如:
class com.friendsofed.makingthingsmove.Utils {
}
在 AS 3 中,包名写在包的声名处,类名写类的声名处,例如:
package com.friendsofed.makingthingsmove{
  public class Utils {
  }
}
导入(Import)
      想象一下,每次要使用这个类的方法时都要输入
com.friendsofed.makingthingsmove.Utils,是不是太过烦琐太过死板了。别担心,import
语句可以解决这个问题。在这个例子中,可以把下面这句放在 package 中类定义的上面:
import com.friendsofed.makingthingsmove.Utils;。
构造函数(Constructor)
      构造函数是指一个名字与类名相同的方法。当该类被实例化时,该函数会被自动调用,
也可以传入参数,例如:
首先,创建一个类:
package {
  public class MyClass {
   public function MyClass(arg:String) {
     trace("constructed");
     trace("you passed " + arg);
   }
  }
}
然后,假设工作在 Flash CS3 IDE(集成开发环境)中,在时间轴上创建该实例:
var myInstance:MyClass = new MyClass("hello");
结果输出:
constructed
you passed hello
继承(Inheritance)
     一个类可以从另一个类中继承(inherit)和扩展(extend)而来。这就意味着它获得了另
一个类所有的属性和方法(除了那些被 private 掩盖住的属性)。所生成的子类(派生类)还
可以增加更多的属性和方法, 或更改父类(基类)已有的属性或方法。要分别创建两个类来实
现(两个独立的 .as 文件),例如:
package {
  public class MyBaseClass {
   public function sayHello():void {
    trace("Hello from MyBaseClass");
   }
  }
}
package {
  public class MySubClass extends MyBaseClass {
   public function sayGoodbye():void {
     trace("Goodbye from MySubClass");
   }
  }
}
      不要忘记,每个类都必须在其自身的文件中,文件名为该类的类名,扩展名 .as,所以
必须要有 MyBaseClass.as 文件和 MySubClass.as 文件。   因此,在使用 Flash CS3 IDE 时,
保存的 FLA 文件,要与这两个类在同一个文件夹。
下面代码会生产两个实例,把它写入时间轴看看会发生什么:
var base:MyBaseClass = new MyBaseClass();
base.sayHello();
var sub:MySubClass = new MySubClass();
sub.sayHello();
sub.sayGoodbye();
      第一个实例没什么可说的,值得注意的是第二个实例中的 sayHello 方法,虽然在
MySubClass 中没有定义 sayHello,但它却是继承自 MyBaseClass 类的。另一个值得注意
的是,增加了一个新的方法 sayGoodbye,这是父类所没有的。
下面说说,在子类中如何改变一个父类中已存在的方法。在 AS 2 中,我们可以只需要重新
定义这个方法就可以了。而在 AS 3 中,则必需明确地写出 override 关键字,来进行重新
定义。
package {
  public class MySubClass extends MyBaseClass {
   override public function sayHello():void {
     trace("Hola from MySubClass");
   }
   public function sayGoodbye():void {
     trace("Goodbye from MySubClass");
   }
  }
}
      请注意,原来的 sayHello 方法被重写,再调用 MySubClass 后,就有了新的信息。另
外,私有成员也不能被重写,因为它们只能被它们自身的类访问。
MovieClip/Sprite 子类
    我们可以自己写一个类,然后让另一个类去继承它。在 AS 3 中,所有代码都不是写在
时间轴上的,那么它们一开始都要继承自 MovieClip 或 Sprite。 MovieClip 类是影片剪
辑对象属性和方法的 ActionScript 模板。它包括我们所熟悉的属性如:影片的 x,y 坐标,
缩放等,这些在 AS 3 中的变化不大。
    AS 3 还增加了 Sprite 类,通常把它理解为不在时间轴上的影片剪辑。很多情况下,
只使用代码操作对象,并不涉及时间轴和帧,这时就应该使用 Sprite 这个轻型的类。如果
一个类继承自 MovieClip 或 Sprite ,那么它会自动拥有该类所有的属性和方法,我们还
可以为这个类增加特殊的属性和方法。
      例如,游戏设计一个太空船的对象,我们希望它拥有一个图形,并且在屏幕的某个位置
移动,旋转,并为动画添加 enterFrame 侦听器,还有鼠标、键盘的侦听等。这些都可以由
MovieClip 或 Sprite 来完成,所以就要继承自它们。同时,还可以增加一些属性如:速度
(speed)、  油量(fuel)、损坏度(damage),还有像起飞(takeOff)、坠落(crash)、射击(shoot)
或是自毁(selfDestruct)等方法。那么这个类大概是这样的:
package {
  import flash.display.Sprite;
  public class SpaceShip extends Sprite {
   private var speed:Number = 0;
   private var damage:Number = 0;
   private var fuel:Number = 1000;
   public function takeOff():void {
     //...
   }
   public function crash():void {
     //...
   }
   public function shoot():void {
     //...
   }
   public function selfDestruct():void {
     //...
   }
  }
}
     注意,首先要导入 flash.display 包中的 Sprite 类,如果要导入 MovieClip 类,同
样也需要导入这个相同的包 flash.display.MovieClip 类。
创建文档类(Document class)
     现在我们对类已经了一定的了解,接下来,看看如果真正地使用它。有时候我常说基于
AS 3 的 SWF 是多么的重要,这是因为 AS 3 引入了一个全新的概念,文档类(document
class)。
     一个文档类就是一个继承自 Sprite 或 MovieClip 的类,并作为 SWF 的主类。读取
SWF 时,这个文档类的构造函数会被自动调用。它就成为了我们程序的入口,任何想要做的
事都可以写在上面,如:创建影片剪辑,画图,读取资源等等。如果在 Flash CS3 IDE 中
写代码, 可使用文档类,也可以选择继续在时间轴上写代码。但如果使用 Flex Builder 2 或
免费 Flex SDK,那里没有时间轴,唯一的办法就是写在类中。这些工作一切都围绕着强大
的文档类而展开,没有它就没有 SWF。以下是一个文档类的框架:
package {
  import flash.display.Sprite;
  public class Test extends Sprite {
   public function Test() {
    init();
   }
   private function init():void {
     // 写代码处
   }
  }
}
      如果你看过前面的部分,不会认为这是个新知识,只不过是把他们放在了一起而已。使
用默认包,导入并继承 Sprite 类。构造函数只有一句,调用 init 方法。当然,也可以把
所有代码写在构造函数里,但是要养成一个好习惯,就是尽量减少构造函数中的代码,所以
把代码写到了另一个方法中。  本书会给大家很多代码块进行测试,  那时要像上面这个例子一
样把代码块放入 init 方法中,这样在影片编译执行时,就会调用 init 中的代码。下面我
们要开始学习如何连接文档类和 SWF。
使用 Flash CS3 IDE(集成开发环境)
      Flash CS3 IDE 是实现文档类的最方便的工具。把上述的类选择一个文件夹进行保存,
文件名为 Test.as。  打开 Flash CS3 ,创建一个 FLA 文件,保存到一这个类相同的目录下。
确认 FLA 默认发布设置为 Flash Player 9 及 AS 3.0。在属性面板中,我们注意到出现了
一个名为文档类(Document Class)的区域(图 2-1)。只需输入类名:Test。
    请注意,我们输入的是类名,而不是文件名。所以这里不需要输入扩展名 .as。如果这
个类包涵在一个包中,那么就需要输入类的完整路径——例如:
com.friendsofed.chapter2.Test。
程序动画
    下面,再来学习一些 AS 3 编程的基本原理。如果你已经选择好了一个开发环境,那么
就出发吧。让我们进入 ActionScript 动画世界。
动画的执行过程
    几乎所有的程序动画都包括几种不同的执行过程。对于逐帧动画来说,意味着创建和存
储一组连续的位图,每一帧都是一幅图像,只需要进行显示即可。
当我们在 Flash 中使用图形或元件时,事情就发了微妙的变化。这时,Flash 不会为每一帧创建和存储新的位图。对于每一帧而言,Flash 存储的是舞台上每个对象的位置,大
小,颜色等等。比如,一个小球在屏幕上移动,每一帧只存储小球的在该帧上的位置,第 1
帧小球的位置在左边第 10 个像素, 2 帧也许就在第 15 个像素,
                                第                         等等。Flash 播放器(Flash
Player)读取这些信息,再根据这些信息的描述来渲染舞台并显示该帧。根据这些变化扩展
一下流程图。

我是这样描述一个动态程序动画的。

    没有第 1 帧、第 2 帧的概念,脚本动画通常只由一帧完成。下面我们就
来看看动画的执行过程。
    首先,建立初始化。舞台中放入一个影片剪辑,再创建补间动画,或使用代码来描述整
个场景。总之,最后都要对该帧进行渲染及显示。
然后,应用自定义规则。规则可以像“让球向右移动 5 像素”这样简单,也可以是由几十条
复杂的三角函数组成。 使用自定义规则会产生新的描述再根据这些描述进行渲染及显示, 
不断地应用这个规则。
    请注意,同一规则被一遍又一遍地执行,而不是对第 1 帧用一套规则,而对第 2 帧又使
用另一套规则。所以难度就在于,一套规则要处理所有可能出现的情况。要是球向右移动得
过远,超出了舞台怎么办?你的这套规则就要解决这个问题。   是否还希望用户通过鼠标来操
作小球?那么你的规则也要把它考虑进去。
听起来很复杂,其实不然,这里所说的“规则”,实际上就是 ActionScript 代码。每套规
则都可由一行或多行代码组成。下面是小球向右移动 5 像素的例子:
ball.x = ball.x + 5;
这句话是说无论小球 X 坐标(水平轴)在哪里,  都在原来的 X 位置上增加 5 像素,并把该坐
标作为它的新 X 坐标。也可简化为:
ball.x += 5;
    “+=”操作符:把右边的值与左边的变量相加,相加的结果再赋值给该变量。以下是更
多的高级规则,日后会学到:
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);
     这段现在看不懂没关系,大家只要知道 Flash 会在每一帧中生成这段代码,并不断地
执行。
     怎样让它循环执行?看看我第一次的尝试, 这也是很多 AS 初学者都会犯的错误。 这是
在很多程序设计语言中都存在的循环结构, for 和 while。
                                         如              用循环结构使代码重复执行,
这就是我曾写的那段:
for (i = 0; i < 500; i++) {
  ball.x = i;
}
     看起来相当简单。变量 i 从 0 开始,所以小球 X 坐标移动到 0——舞台最左边。i++
让 i 的值每次增长 1,即:0~1~2~3~4…,每次这个值都会做为 ball.x 的值,把小球
从左向右移动。当值为 500 时,表达式 i<500 值为假(false),循环结束。
     如果你也犯过同样的错误, 就会知道,小球没有在舞台上发生移动——只是一下子出现
在了舞台的右边而已。 为什么没有移动到中间的那些点上?其实它移动了,  只是我们没有看
到, 因为我们没有让 Flash 去刷新屏幕。  

     实际上我们使用自定义规则使球移动到指定位置,并创建了 500 次新的场景。但在循环
结束之前没有给出显示, 这是因为 Flash 只在每一帧结束后才进行一次刷新,这点很重要。
以下是 Flash 进入帧的动作顺序:
1.在舞台上放置所有的对象,不论在何级,何层,或是否为加载的影片。
2.执行帧上所有的 Action 脚本(ActionScript),不论在何级,何层,不论处于影片剪辑还
是按钮中,也不论它嵌套在何处。
3.判断是否到了该显示的时候。如果设置帧频为 20 帧/秒,Flash 最少要等上一帧显示后
50 毫秒后再进行下一次显示,显示了该帧后,就要编译和进入下一帧。如果帧频没有到 20
帧/秒,那么要等待到正确的时间再去执行。
定时时间存在着一些问题。首先,众所周知帧频是不精确的(即使在 Flash 9 中),不要依赖
它作为精确的定时器。其次,在大量的编译和 AS 执行花费的时间会超出规定的时间。
     尽管如此,我们也不必担心自己的脚本会被砍掉一部分。在进入第 3 步之前,Flash 会
完成所有可执行代码(第 2 步),即使需要延缓帧频也要完成。Flash 为了能完成脚本,甚至
会等上 15 秒。在上面的例子中,Flash 等待循环结束,然后进入下一帧,只在跳转到下一
帧时进行屏幕的刷新。这就是为什么我们看到的是跳动而不是移动。因此,要想完成移动,
我们所要做的就是打散这个循环。

原创粉丝点击