ActionScript 3.0动画基础-4

本文作者:egoldy
文章出处:http://www.webstudio.com.cn
文章性质:翻译
阅读次数:5953
发布时间:2007-07-08
声明: 此文章为未出版的keith peters的ActionScript 3.0 making things move中文版样章。为书中的第二章。webstudio会在中文版出版之际,友情提供论坛forum支持。转载请注明出处,谢谢!

接上一篇.....



2.6显示列表display list

在AS3之前,你可以在一个Flash影片中创建不同数量类型的可视对象,包括影片剪辑,图形元件,按钮,文本域,位图,组件,和基本的图形。这些对象实际上并没有被组织成一定的层次结构,并且他们都有各自不同的创建,销毁,和操作的方法。举例来说,影片剪辑可以通过贴加(attach),复制,或通过动态创建放置在舞台上。文本域可以动态的创建或是在Flash IDE中创建。当你在创建位图,视频,组件和其它的内容时,他们看起来象是在不同的平台上创建的,然后被堆放在Flash中,并强行让他们在一直工作。
在AS3中,这些不同的对象现在都被统一规化在一个地方。你所看到的舞台上的任何对象都由DisplayObject类的扩展所创建的。换种角度讲,所有的对象都是一个大家庭的一部分,他们都有着相同的基本行为,根据这点你可以知道怎样创建他们,把他们放在舞台上,销毁他们,并在舞台上操作他们。
现在创建一个精灵(sprite),一个影片剪辑,或一个文本域的方法非常的相似。实际上,所有的显示(display)对象现在都使用相同的方法创建,这种方法也是你创建其它任何类型对象的方法,那就是使用new操作符。为了验证这一点,下面给出如何创建这几个对象:
代码:

var  myTextfield:TextField  =  new  TextField();
var  myMovieClip:MovieClip  =  new  MovieClip();
var  mySprite:Sprite  =  new  Sprite();


这种方式是不是更简单呢?
你已看过前面的范例了,如果你已经创建一个影片剪辑或是精灵(sprite),现在你可在那些对象里绘制内容,如下:
代码:

mySprite.graphics.beginFill(0xff0000);
mySprite.graphics.drawCircle(0,  0,  
40); mySprite.graphics.endFill();


但是如果只是象上面那样做了,你可能看不到任何内容。这就是我们将要讨论的显示列表(display list)。“display list”术语是一个新增的,但是如果你使用Flash已经有一段时间了,它不应当是一个完全陌生的概念。把你影片中的所有可视对象想为一个树形。树的最底部是舞台,它默认状态就是可视的。在舞台上,你可能还有几个影片剪辑或其它类型的可视对象(文本域,图形等等)。当你将他们加入到舞台上时,他们也会变成可视的。在那些影片剪辑内部可能还会有其它的影片剪辑或其它的可视对象,并且在其它的内部也可能会有。这就是最基本的显示列表(display list)。

显示列表(display list)工作方式是AS2与AS3中最大的不同之处。在AS2中,当你通过attach命令或是使用createEmptyMovieClip创建一个影片剪辑, 它必须要在创建时指明它将放在哪里。另外一点,影片剪辑被创建后,被放置在一个指定位置的列表中,那就是它所要留的地方。移除影片剪辑时,没有办法在位置列表中改变它的位置或是让它从列表中彻底销毁。
在AS3中,当你创建一个新的影片剪辑,精灵,或其它的显示(display)对象时,它不会自动的加入到显示列表(display list)中。注意在刚刚给出的sprite范例中,当创建精灵(sprite)时没有提到过父级(parent)或是深度(depth)。它们是可以存在的但并不是在显示列表(display list)中被操作的。
如果从字面意义上理解“stage”(舞台),你可以想象那些对象就是幕后的演员,收起翅膀,悬在舞台的上空,或是想象为趴在地底下。你不能看见他们,但是他们都在那里,繁忙的准备着他们的入场,到那时他们将跳上舞台并且真实的显示出来。
现在为止,我们已经使用了树的比喻和一个剧院的比喻---还有其它的比喻吗?我们也可以将显示列表(display list)想象为一个大家庭,一个这样的parent/child/sibling家族关系。当你加入某些东西到显示列表(display list)中,对象将被加入到父级(parent)中,那么新加入对象就是父级的儿子。如此这样,当你增加一个对象到列表中时它是有理性的。你需要使用addChild方法来完成。
你的文档类(document class)就象是一个树的根(或是所有可视对象的伟大的父亲,如果你愿意)。它是自动可视的,然后你开始为它加入孩子。因此,如你看到的之前的范例,要使精灵(sprite)或其它显示对象(display object)可视,传递给它一个新创建的精灵(sprite):
代码:

var  mySprite:Sprite  =  new  Sprite();
mySprite.graphics.beginFill(0xff0000);
mySprite.graphics.drawCircle(0,  0,  40);
mySprite.graphics.endFill();
addChild(mySprite);


如果你试一试这些,将这些代码放在之前类结构中的init函数中。注意它将放置在原点0,0上。你可以通过调整x,y属性来改变它的位置。还要注意的是这里没有提到深度(depth),深度如同当我们在AS2中创建影片剪辑或使用attach贴加他们的过程。虽然深度管理大部分情况下是自动的,但仍有加入子对象的方法可以加入到指定的深度或改变它的深度,这些在我们需要的时候我们会讲到。
要从一个显示列表(display list)中移除掉一个对象,调用removeChild方法,传递一个子对象的引用。第一个关键的地方是要意识到移除一个子对象并不是销毁它!它会保持与你移除它之前一样的状态而存在的。并且在将它加入回显示列表(display list)时仍保持原有的状态。换种角度讲,在AS2中,如果你已经在子对象中绘制了一些图形,或载入了些外部内容到子对象中,当你要将它加回到显示列表(display list)中时,将不得不重新绘制或重新载入那些内容。
第二个关键的地方要意识到当你将一个子对象加回到列表中时,你可以自由的将它放在任何地方。这被称为重设父级(reparenting)并且是令人兴备的(至少对我是这样)。你可以从一个影片剪辑移除一个对象并将它贴加到另一个对象中,当你移除它时它会保持原有的状态,这在之前版本中是不可能的。实际上,你不需要移除它,因为一个子对象只能有一个父级,将它加入另一个父级会自动先从原来的父级移除。
The following class demonstrates reparenting:
下面的类演示重设父级
代码:

package  {
import  flash.display.Sprite;
import  flash.events.MouseEvent;

public  class  Reparenting  extends  Sprite  {
private  var  parent1:Sprite;
private  var  parent2:Sprite;
private  var  ball:Sprite;
public  function  Reparenting()  {
init();
}
private  function  init():void  {
parent1  =  new  Sprite();
addChild(parent1);
parent1.graphics.lineStyle(1,  0);
parent1.graphics.drawRect(-50,  -50,  
100,  100); parent1.x  =  60;
parent1.y  =  60;

parent2  =  new  Sprite();
addChild(parent2);
parent2.graphics.lineStyle(1,  0);
parent2.graphics.drawRect(-50,  -50,  100,  100);
parent2.x  =  170;
parent2.y  =  60;

ball  =  new  Sprite();
parent1.addChild(ball);
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(0,  0,  40);
ball.graphics.endFill();
ball.addEventListener(MouseEvent.CLICK,  onBallClick);
}

public  function  onBallClick(event:MouseEvent):void
{
parent2.addChild(ball);
}
}
}



这个类创建了三个精灵(sprite):parent1,parent2,和ball。Parent精灵直接加到显示列表(display list)中,并且方块被绘制在它们的内部。Ball精灵(sprite)被加入到parent1中,因此现在它存在于显示列表(display list)中并是可视的。
当小球被点击时,它会加入到parent2中。注意这里没有改变它的x,y位置的代码。它会发生移动是因为它在位置不同的精灵(sprite)中。还有要注意的当精灵(sprite)在移除和加入的过程中,动态绘制的圆形会始终保持着。

2.6.1子类中的显示对象(display object)

在前面,你已经学习了在创建文档类(document class)时精灵(sprite)和影片剪辑的子类。这些类的子类还有许多的其它用途。
首先,你可能在想,因为在AS3中attachMovieClip不在可用了,那如何在影片运行时将Flash CS3 IDE库中的影片剪辑元件放入到影片中呢?答案是使用一个扩展了精灵(sprite)或影片剪辑的子类。虽然我努力的在创建SWFs影片时不去使用任何的工具,但我们还是要回到Flash IDE中来解释它。
通过下面的例子是最好的说明:
1.  创建一个新的FLA,然后在舞台上绘制一些内容。
2.  选中内容按下F8将其转换为元件。
3.  在转换元件的对话框中,输入元件的名称,并选择元件类型为影片剪辑(MovieClip)。
4.  选择为ActionScript导出。
在这一点上,在之前版本的Flash中,你将获得一个自动生成的链接ID,你也可以选择输入一个类。
在Flash CS3中,链接ID文本域被禁止了,类文本域强制要求输入并且添充了一个默认值。它也是一个新的文本域,基于类,它预置的是flash.display.MovieClip。你可以将它改为扩展影片剪辑或精灵(sprite)的其它类。
输入你想要的类的名称。不用担心你还没有创建这个类。然后点击“OK”。这里会比较有趣。因为Flash不能找到你指定名称的类,它将在编译时自动生成一个类。这并不意味着你可以找到Flash自动创建的一个ActionScirpt类,但是它会在你的SWF中以字节代码(bytecode)的形式创建一个基于精灵(sprite)或影片剪辑的新类。这个类实际上不能象扩展其它基础类的子类一样,但是它将会链接到你库中的元件。

现在,假设你在刚才说到的对话中输入的类名为Ball。在你的文档类(document Class),或在时间线上,你可以书写如下代码:
代码:

var  ball:Ball  =  new  Ball();
addChild(ball);


这样库中的元件应会在舞台上被创建,就是象以前AS2中的attachMovie方法。
如果你想将库中的影片剪辑内容贴加到影片中,这是一个非常好的方法。但是比只是简单的接受自动生成类的更好方法是,你可以指定一个自已编写的真实的类路径,可以为你的元件加入一些函数定义。现在让我们跳出Flash IDE,回到下一个范例的类中来。
现在,我们将重新来看前面刚刚给出的重设父级(reparenting)的范例,那里的一些复制可以被简化为另一个类。在那个范例中,我们创建了一个名为parent1的精灵并在它的内部绘制了一个盒子如下代码:
代码:

parent1.graphics.lineStyle(1,  0);
parent1.graphics.drawRect(-50,  -50,  100,  100);

我们接着创建了另外一个精灵(sprite),parent2,在它的内部也用同样的代码绘制了同样的盒子。尽管这是一个很简单的例子,但是它可以清晰的显示出精灵的子类是怎样应用的。首先,我们创建一个扩展精灵(sprite)名为ParentBox 的类。在这个类中,我们编写编制盒子子代码。
代码:

package  {
import  flash.display.Sprite;

public  class  ParentBox  extends  Sprite  {
public  function  ParentBox()
{
init();
}
private  function  init():void
{
graphics.lineStyle(1,  0);
graphics.drawRect(-50,  -50,  100,  100);
}
}
}

然后我们将告诉类创建两个ParentBox即可,而不再是两个精灵(spirte)了。

代码:

package  {
import  flash.display.Sprite;
import  flash.events.MouseEvent;

public  class  Reparenting2  extends  Sprite  {
private  var  parent1:ParentBox;
private  var  parent2:ParentBox;
private  var  ball:Sprite;

public  function  Reparenting2()  {
init();
}
private  function  init():void  {
parent1  =  new  ParentBox();
addChild(parent1);
parent1.x  =  60;
parent1.y  =  60;

parent2  =  new  ParentBox();
addChild(parent2);
parent2.x  =  170;
parent2.y  =  60;

ball  =  new  Sprite();
parent1.addChild(ball);
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(0,  0,  40);
ball.graphics.endFill();
ball.addEventListener(MouseEvent.CLICK,  onBallClick);
}
public  function  onBallClick(event:MouseEvent):void
{
parent2.addChild(ball);
}
}
}


作为parentBox的实例,他们仍是精灵(Sprite),因此可以在它们中加入子对象。它们也可以使用新的绘制图形的init方法。这是一个很小的例子,但是希望你能够理解它,当你继续向下看这本书时,你会看到许多更为复杂的范例。
你也许想要针对此例加入一个Ball类,这样做不能够在减少你的复制操作了,但是会使你的类变得更为复杂,一个比较好的主意是将某些可分离的功能定义成不同的类,而不应是把任何不论有多小的东西都放入到一个单独的类中。这种方式也是最值得的推荐的。实际上,在接下本书的内容中,你很快就可以创建一个你需要用到的Ball类了。

2.7用户交互操作

最后,我们来看用户交互,可能这就是你要阅读本书的原因之一。除此之外,如果本书没有使用交互或在影片中的一些动态输入,你也许要使用的就是补间(tween)了。
实际上,我们在前面的章节上并不是一点也没有讨论这部分的内容。用户交互是基于用户事件,它们产生于鼠标事件和键盘事件。让我们快速的过一遍各种用户事件和它们的处理者(handler)。

2.7.1鼠标事件

鼠标事件在AS3中已经发生了激动人心的改变。在AS2中,影片剪辑自动的侦听所有的鼠标事件。现在任何对象都明确的加入自身做为一个侦听者。另外,在AS3中要响应鼠标事件,需要鼠标的光标停放在可视的显示对象(display object)内容上。这一点不同于AS2,在AS2中的mouseUp,mouseDown和mouseMove对于任何影片剪辑都会响应,而不管鼠标的光标是否已经定好位置。AS3中的mouseUp和mouseDown事件现在等同于AS2中的onPress和onRelease。
鼠标事件的名称被定成字符串,这在前面的内容中我们已经提到过,最好的方法是使用MouseEvent的属性以保证不会出错。下面是MouseEvent类中可用的鼠标事件:
CLICK
DOUBLE_CLICK
MOUSE_DOWN
MOUSE_MOVE
MOUSE_OUT
MOUSE_OVER
MOUSE_UP
MOUSE_WHEEL
ROLL_OUT
ROLL_OVER
这些属性自身名称就是很好的解释。如果要感觉一下他们,你可以创建和编译下面的类,它将trace出精灵(sprite)上发生的每一个鼠标事件。

代码:

package  {
import  flash.display.Sprite;
import  flash.events.MouseEvent;

public  class  MouseEvents  extends  Sprite  {
public  function  MouseEvents()  {
init();
}
private  function  init():void
{
var  sprite:Sprite  =  new  Sprite(); addChild(sprite);
sprite.graphics.beginFill(0xff0000); sprite.graphics.drawCircle(0,  0,  
50); sprite.graphics.endFill();
sprite.x  =  stage.stageWidth  /  2;
sprite.y  =  stage.stageHeight  /  2;

sprite.addEventListener(MouseEvent.CLICK,  onMouseEvent);
sprite.addEventListener(MouseEvent.DOUBLE_CLICK, onMouseEvent);
sprite.addEventListener(MouseEvent.MOUSE_DOWN, onMouseEvent);
sprite.addEventListener(MouseEvent.MOUSE_MOVE, onMouseEvent);
sprite.addEventListener(MouseEvent.MOUSE_OUT, onMouseEvent);
sprite.addEventListener(MouseEvent.MOUSE_OVER, onMouseEvent);
sprite.addEventListener(MouseEvent.MOUSE_UP, onMouseEvent);
sprite.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseEvent);
sprite.addEventListener(MouseEvent.ROLL_OUT, onMouseEvent);
sprite.addEventListener(MouseEvent.ROLL_OVER, onMouseEvent);
}
public  function  onMouseEvent(event:MouseEvent):void
{
trace(event.type);
}
}
}


注意,这个类对每一种事类型使用了同一个处理者(handler),只是简单的trace出被发送出来的事件类型。

2.7.2鼠标位置

除了鼠标事件,还有两个重要的属性是可能的,它们是来自文档类(document class)或其它显示对象(display object)用来检测当前鼠标光标的位置:mouseX和mouseY。注意他们是影片剪辑的属性,返回是鼠标相对于剪辑注册点的位置。例如,如果你有一个名为sprite的精灵在舞台上的位置为100,100,鼠标光标的位置在150,250,你会得到下面的结果:
mouseX is 150.
mouseY is 250.
sprite.mouseX is 50.
sprite.mouseY is 150.
注意影片剪辑是如何得出鼠标光标在它自身上的位置的。

2.7.3键盘事件

键盘事件是另一部分在AS3中被整理的部分。例如,在AS2中,影片剪辑自动的侦听键盘事件,但是它们只在特点的环境下才会响应事件。因些最好的办法是加入一个影片剪辑做为侦听者,但是当影片接受多个事件而不只是单单接收键盘事件时,这种方法会失效。接着,AS2组件架构的一大部分是用来处理键盘的交互---关系到Flash播放器底层的架构:tab管理,焦点管理,文本域中的回车和tab键,等等。
现在我们说情况要好的很多
键盘事件的名称,就象鼠标事件一样,被定义成字符串,但他们仍是KeyboardEvent类的属性。他们只有两个:
   KEY_DOWN
   KEY_UP
你可以在一个指定的对象上侦听键盘事件,就象我们前面讲到的侦听鼠标事件一样。为了做到这一点,然而,你需要先使对象获得焦点以使它可以捕获那些事件。你可以象下面这样书写:
stage.focus  =  sprite;
而在许多案例中,只记得侦听键盘事件却忽视的它的焦点。要做到这一点,你可以直接在舞台上侦听键盘事件。下面是一个范例示范:
代码:

package  {
import  flash.display.Sprite;
import  flash.events.KeyboardEvent;

public  class  KeyboardEvents  extends  Sprite  {
public  function  KeyboardEvents()  {
init();
}
private  function  init():void  {
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyboardEvent);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyboardEvent);
}
public  function  onKeyboardEvent(event:KeyboardEvent):void  {
trace(event.type);
}
}
}


2.7.4键盘代码

通常,你想知道的并不只是一个键子被按下去或放出,而是想知道它是哪一个键子。有两种方法可以读取键盘事件函数中的信息。
在之前我们讨论事件的过程中,我说过一个事件处理者(handler)接收传过来的一个事件对象,这个事件对象中包含着刚刚发生的事件的数据信息。在一个键盘事件中,有两个属性与事件中的按键有关:charCode和keyCode。
charCode属性给出的是真实的描述键子的字符。例如,如果用户在键盘上按下“a”键,charCode将包含这个字符串“a”。如果用户同时也按下了Shift键,charCode将包含的是“A”。
KeyCode包含的是描述按下键子的物理键值。如果一个用户按下“a”键,keyCode将包含数值65,会忽视其它同时按下的按键。如果用户同时按下Shift和“a”,你需要两个键盘事件:一个是Shift(keyCode 16)和另一个是“a”(65)。
flash.ui.Keyboard类也有一些属性可用于一些数字键,因此你不需要记住它们,例如,Keyboard.SHIFT 等于数值16,因些你可检测keyCode是否等于Keyboard.SHIFT来发现Shift键是否被按下。这一章中的最后一个例子示范如下:

代码:

package  {
import  flash.display.Sprite;
import  flash.events.KeyboardEvent;
import  flash.ui.Keyboard;

public  class  KeyCodes  extends  Sprite  {
private  var  ball:Sprite;

public  function  KeyCodes()  {
init();
}
private  function  init():void  {
ball  =  new  Sprite();
addChild(ball);
ball.graphics.beginFill(0xff0000);
ball.graphics.drawCircle(0,  0,  40);
ball.graphics.endFill();
ball.x  =  stage.stageWidth  /  2;
ball.y  =  stage.stageHeight  /  2;
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyboardEvent);
}
public  function  onKeyboardEvent(event:KeyboardEvent):void  {
switch(event.keyCode)  {
case  Keyboard.UP  :
ball.y  -=  10;
break;

case  Keyboard.DOWN  :
ball.y  +=  10;
break;

case  Keyboard.LEFT  :
ball.x  -=  10;
break;

case  Keyboard.RIGHT  :
ball.x  +=  10;
break;

default:
break;
}
}
}
}



还一点需要知道的是当你在Flash创作环境中测试你的影片时,Flash IDE会侦听一些键子来操作IDE自身。如Tab键,所有的功能键,和一些指定了快捷键的菜单,这些键子不会在你的测试影片中被接收。你可以通过选择控制菜单(control>Disable Keyboard Shortcuts)然后选择禁止快捷键来解决。测试时就象真正工作在浏览器中一样。

总结

这一章覆盖了所有关于ActionScript动画所需要基础。你现已经知道了帧循环,事件,侦听者,处理者,和显示列表。我也介绍了类,对象和基本的用户交互。这是许多有用的资料!如果有些地方还有点模糊不用担心。当进入到指定的技术内容时,我会尽量详细讲解它们,并且你可以随时回到这里,巩固任何概念。至少,现在你已经熟悉了这些术语和概念,你可以准备继续向前了。

你可能感兴趣的:(actionscript)