类和面向对象编程
者可能已经在 AS (或其它语言) 中使用过很多年了,为了让大家都能学会,我会扼要的介
绍一下这些基础知识。就算是 AS 2 的 OOP 专家也希望能略读下这一段,因为 AS 3.0 的
工作原理确实发生了很大的变化。
写过代码,那么实际上就已经使用了类。类可以简单理解为一种对象, MovieClip 就是影
片剪辑的类,而文本框、影片剪辑、按钮、字符串和数值等都有它们自己的类。
(Property)指用于保存与该类有关的信息变量,
是这个类中的一部分,那么我们就称它为方法(Method)。
一个基本的类:
创建出很多的实例。与元件和实例的关系相同,类就是一个模板,而对象(如同实例)就是类
的一个特殊表现形式。下面来看一个类的例子:
package {
}
(Package) ,
本书的示例甚至不会用到包。Package 这个关键字和一对大括号是必需有的,我们理解为默
认包,紧随其后的就是类的定义。
另一个变化是 AS3.0 中的类拥有了访问关键字。访问关键字是指:一个用来指定其它代码
是否可访问该代码的关键字。 public (公有类) 关键字指该类可被外部任何类的代码访问。
本书中所有示例的类都是 public 的。在深入学习了 AS 3.0 后,我们会发现不是所有类都
是公有的,甚至还有多重的类,这些内容超出了本书的谈论范围。
种要素,一个是名为 myProperty 的变量,另一个是名为 myMethod 的函数。
包(Package)
所指的是一个真正存在的文件夹,用 “.”进行分隔。例如,有一个名为 Utils 的类,存
在于文件夹 com/ friendsofed/ makingthingsmove/ 中(使用域名作为包名是一个不成文
的规定,目的是保证包名是唯一的)。这个类就被写成
com.friendsofed.makingthingsmove.Utils。
在 AS 2 中,使用整个包名来创建一个类,例如:
class com.friendsofed.makingthingsmove.Utils {
}
在 AS 3 中,包名写在包的声名处,类名写类的声名处,例如:
package com.friendsofed.makingthingsmove{
}
导入(Import)
com.friendsofed.makingthingsmove.Utils,是不是太过烦琐太过死板了。别担心,import
语句可以解决这个问题。在这个例子中,可以把下面这句放在 package 中类定义的上面:
import com.friendsofed.makingthingsmove.Utils;。
构造函数(Constructor)
也可以传入参数,例如:
首先,创建一个类:
package {
}
然后,假设工作在 Flash CS3 IDE(集成开发环境)中,在时间轴上创建该实例:
var myInstance:MyClass = new MyClass("hello");
结果输出:
constructed
you passed hello
继承(Inheritance)
一个类所有的属性和方法(除了那些被 private 掩盖住的属性)。所生成的子类(派生类)还
可以增加更多的属性和方法, 或更改父类(基类)已有的属性或方法。要分别创建两个类来实
现(两个独立的 .as 文件),例如:
package {
}
package {
}
必须要有 MyBaseClass.as 文件和 MySubClass.as 文件。
保存的 FLA 文件,要与这两个类在同一个文件夹。
下面代码会生产两个实例,把它写入时间轴看看会发生什么:
var base:MyBaseClass = new MyBaseClass();
base.sayHello();
var sub:MySubClass = new MySubClass();
sub.sayHello();
sub.sayGoodbye();
MySubClass 中没有定义 sayHello,但它却是继承自 MyBaseClass 类的。另一个值得注意
的是,增加了一个新的方法 sayGoodbye,这是父类所没有的。
下面说说,在子类中如何改变一个父类中已存在的方法。在 AS 2 中,我们可以只需要重新
定义这个方法就可以了。而在 AS 3 中,则必需明确地写出 override 关键字,来进行重新
定义。
package {
}
外,私有成员也不能被重写,因为它们只能被它们自身的类访问。
MovieClip/Sprite 子类
时间轴上的,那么它们一开始都要继承自 MovieClip 或 Sprite。 MovieClip 类是影片剪
辑对象属性和方法的 ActionScript 模板。它包括我们所熟悉的属性如:影片的 x,y 坐标,
缩放等,这些在 AS 3 中的变化不大。
只使用代码操作对象,并不涉及时间轴和帧,这时就应该使用 Sprite 这个轻型的类。如果
一个类继承自 MovieClip 或 Sprite ,那么它会自动拥有该类所有的属性和方法,我们还
可以为这个类增加特殊的属性和方法。
移动,旋转,并为动画添加 enterFrame 侦听器,还有鼠标、键盘的侦听等。这些都可以由
MovieClip 或 Sprite 来完成,所以就要继承自它们。同时,还可以增加一些属性如:速度
(speed)、
或是自毁(selfDestruct)等方法。那么这个类大概是这样的:
package {
}
样也需要导入这个相同的包 flash.display.MovieClip 类。
创建文档类(Document class)
AS 3 的 SWF 是多么的重要,这是因为 AS 3 引入了一个全新的概念,文档类(document
class)。
SWF 时,这个文档类的构造函数会被自动调用。它就成为了我们程序的入口,任何想要做的
事都可以写在上面,如:创建影片剪辑,画图,读取资源等等。如果在 Flash CS3 IDE 中
写代码, 可使用文档类,也可以选择继续在时间轴上写代码。但如果使用 Flex Builder 2 或
免费 Flex SDK,那里没有时间轴,唯一的办法就是写在类中。这些工作一切都围绕着强大
的文档类而展开,没有它就没有 SWF。以下是一个文档类的框架:
package {
}
用默认包,导入并继承 Sprite 类。构造函数只有一句,调用 init 方法。当然,也可以把
所有代码写在构造函数里,但是要养成一个好习惯,就是尽量减少构造函数中的代码,所以
把代码写到了另一个方法中。
样把代码块放入 init 方法中,这样在影片编译执行时,就会调用 init 中的代码。下面我
们要开始学习如何连接文档类和 SWF。
使用 Flash CS3 IDE(集成开发环境)
文件名为 Test.as。
确认 FLA 默认发布设置为 Flash Player 9 及 AS 3.0。在属性面板中,我们注意到出现了
一个名为文档类(Document Class)的区域(图 2-1)。只需输入类名:Test。
个类包涵在一个包中,那么就需要输入类的完整路径——例如:
com.friendsofed.chapter2.Test。
程序动画
就出发吧。让我们进入 ActionScript 动画世界。
动画的执行过程
储一组连续的位图,每一帧都是一幅图像,只需要进行显示即可。
当我们在 Flash 中使用图形或元件时,事情就发了微妙的变化。这时,Flash 不会为每一帧创建和存储新的位图。对于每一帧而言,Flash 存储的是舞台上每个对象的位置,大
小,颜色等等。比如,一个小球在屏幕上移动,每一帧只存储小球的在该帧上的位置,第 1
帧小球的位置在左边第 10 个像素, 2 帧也许就在第 15 个像素,
Player)读取这些信息,再根据这些信息的描述来渲染舞台并显示该帧。根据这些变化扩展
一下流程图。
我是这样描述一个动态程序动画的。
来看看动画的执行过程。
个场景。总之,最后都要对该帧进行渲染及显示。
然后,应用自定义规则。规则可以像“让球向右移动 5 像素”这样简单,也可以是由几十条
复杂的三角函数组成。 使用自定义规则会产生新的描述再根据这些描述进行渲染及显示,
不断地应用这个规则。
用另一套规则。所以难度就在于,一套规则要处理所有可能出现的情况。要是球向右移动得
过远,超出了舞台怎么办?你的这套规则就要解决这个问题。
作小球?那么你的规则也要把它考虑进去。
听起来很复杂,其实不然,这里所说的“规则”,实际上就是 ActionScript 代码。每套规
则都可由一行或多行代码组成。下面是小球向右移动 5 像素的例子:
ball.x = ball.x + 5;
这句话是说无论小球 X 坐标(水平轴)在哪里,
标作为它的新 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);
执行。
在很多程序设计语言中都存在的循环结构, for 和 while。
这就是我曾写的那段:
for (i = 0; i < 500; i++) {
}
让 i 的值每次增长 1,即:0~1~2~3~4…,每次这个值都会做为 ball.x 的值,把小球
从左向右移动。当值为 500 时,表达式 i<500 值为假(false),循环结束。
在了舞台的右边而已。 为什么没有移动到中间的那些点上?其实它移动了,
到, 因为我们没有让 Flash 去刷新屏幕。
结束之前没有给出显示, 这是因为 Flash 只在每一帧结束后才进行一次刷新,这点很重要。
以下是 Flash 进入帧的动作顺序:
1.在舞台上放置所有的对象,不论在何级,何层,或是否为加载的影片。
2.执行帧上所有的 Action 脚本(ActionScript),不论在何级,何层,不论处于影片剪辑还
是按钮中,也不论它嵌套在何处。
3.判断是否到了该显示的时候。如果设置帧频为 20 帧/秒,Flash 最少要等上一帧显示后
50 毫秒后再进行下一次显示,显示了该帧后,就要编译和进入下一帧。如果帧频没有到 20
帧/秒,那么要等待到正确的时间再去执行。
定时时间存在着一些问题。首先,众所周知帧频是不精确的(即使在 Flash 9 中),不要依赖
它作为精确的定时器。其次,在大量的编译和 AS 执行花费的时间会超出规定的时间。
完成所有可执行代码(第 2 步),即使需要延缓帧频也要完成。Flash 为了能完成脚本,甚至
会等上 15 秒。在上面的例子中,Flash 等待循环结束,然后进入下一帧,只在跳转到下一
帧时进行屏幕的刷新。这就是为什么我们看到的是跳动而不是移动。因此,要想完成移动,
我们所要做的就是打散这个循环。