[ADC文章]使用ActionScript 3制作以3D方式移动的星星[转载自InfoQ]

本文转载自InfoQ,原文地址是: http://www.infoq.com/cn/vendorcontent/show.action?vcr=838

本教程演示了如何基于Adobe Flash Player 10及Adobe Flash CS4专业版的三维功能,并使用ActionScript3构建用户可控的3D动画。

Flash Player 10中新增的3D支持为你完成了许多3D相关的工作,并且它能和2D特性等功能集成使用。这些3D特性和2D中的对应物比较类似,因此在为游戏和动画添加3D功能的时候,你可能会有似曾相识的感觉——即便某些金属和可视化模型你是第一次遇见。

在这个3D教程中,我会向你展示如何来创建一系列以3D方式移动的星星,以及如何让它们响应用户输入。如图1(点击播放这个动画,调整滑动条以观察效果)。

图1. 3D场景中的星星(点击播放这个动画,调整滚动条以观察效果)

要求

你需要以下软件和文件来完成这个向导:

Flash Player 10或更新版本

Flash CS4 专业版

示例文件

必要知识

你需要对Adobe Flash的制作界面,使用符号及鼠标交互方式有基本的了解。对3D功能的作用有所了解也是有帮助的。你应该对Mariko Ogawa的文章《探索Flash新增的3D功能》中讨论的话题较为熟悉。本教程结尾处有关于Flash中3D功能的更多信息。

创建场景及星星

Flash Player 10的3D功能可以让你设置3D的属性,并让播放器来做其余的计算。这让你可以为游戏或SWF内容添加3D效果,而不需要事先进行数学计算。本教程会使用 ActionScript来修改星星,但是在此之前你需要创建一个场景,并向库中添加图像元素。

  1. 创建一个新的动画,并提供一个600 × 320的场景,并将背景设置成黑色(如图2)。

    Set the Stage size and background color

    图2. 设置场景的尺寸和背景色

  2. 在场景的左上角创建三个滑动条:打开Components窗口(选择Window > Components)并将Slider组件拖至场景中。创建三个滑动条之后,将它们命名为angleSpeedSliderzSpeedSliderradiusSlider,并各指定一个标签(如图3)。

    The user controls: slider components and      their text box titles

    图3. 用户控件:滑动条组建和它们的标签文字

  3. 使用PolyStar工具在场景中创建一颗星星:点击Rectangle Tool并保持,然后在子菜单中选择PlyStar工具。在Tool Setting中设置样式为星型,边数为5,点的大小为0.48(如图4),这样便生成了一个由点与点之间的直线所组成的星星。

    Set the options on the PolyStar tool

    图4. 设置PolyStar工具中的选项

  4. 设置Stroke为2 pt White并将Fill设为Blue。在绘图时按住Shift键,这样可以让星星按照水平对齐。完成之后,将Width设为150。 
  5. 将对象转为MovieClip符号(选择Modify > Convert to Symbol),输入名称为Star,并将限制点放在中间(如图5)。选择“导出为ActionScript”(如果你找不到它们,那么请选择“高级”按钮)。如果系统提示说没有找到这个类的定义,可以忽略这个警告信息。让Flash使用自动生成的类型。

    Converting to a Symbol and exporting for      use in ActionScript

    图5. 转化为符号并导出为ActionScript

  6. 删除场景中的星星,你可以使用ActionScript向场景中添加星星。
  7. 添加一个播放/暂停按钮来启动和停止星星的移动。从Components面板中选择一个PlayPauseButton至左下角。在属性中将它的实例名设为playpause。在我的示例中,我将x设为3,y设为282,并将宽和高设为34

使用ActionScript进行3D操作

现在库中已经存在了这些元素,你可以使用ActionScript来操作这些内容,它们会向用户方向移动:

  1. 在第一帧的Actions面板中输入下列代码
    1. //显示的星星数量
    2. const NUMBER_OF_STARS:int = 14;
    3. //最上方的星星至底部的可视距离(所有的y值都相同)
    4. const ORIGINAL_YBORDER:Number = 35;
    5. //星星之间的z距离(任意值)
    6. const ZDISTANCE:int = 215;
    7. //第一个星星的初始Z值,因此最上方的星星的z等于0
    8. const INITIAL_STAR_DEPTH:Number = 2795;
    9. //星星离开视图时的Z值(通过视觉估计)
    10. const MINIMUM_STAR_DEPTH:Number = -221;
    11. //计时器的延迟数(毫秒)。30毫秒等于每秒大约33.3帧。
    12. const MYTIMERFREQUENCY:Number = 30;
    13. //星星面向用户的速度
    14. var zSpeed:Number = 15;
    15. //创建一个包含所有星星的数组
    16. var stars:Array = new Array();
    17. //为性能考虑,这些变量定义在变量外部:
    18. //表示星星深度的局部变量
    19. var starDepth:Number = INITIAL_STAR_DEPTH;
    20. //用于操作星星的局部变量
    21. var currentStar:Star;
    22. //默认情况下,星星消失在场景的中间。在现在的示例中,我们将其移动至顶部附近,这样我们便
    23. //可以“俯视”这些星星。
    24. root.transform.perspectiveProjection.projectionCenter =
    25. new Point(stage.stageWidth / 2, stage.stageHeight / 8);
    26. //创建星星的循环
    27. //从最远的星星开始创建并进行定位
    28. for (var i:int=0; i < NUMBER_OF_STARS; i++)
    29. {
    30. //创建一个新的星星
    31. currentStar = new Star();
    32. //所有星星的x和y坐标都相同:视图中间靠上
    33. currentStar.x = (stage.stageWidth/2);
    34. currentStar.y = stage.stageHeight - ORIGINAL_YBORDER;
    35. //设置深度,即z值,这表示它与用户之间的距离
    36. currentStar.z = starDepth;
    37. //设置对象的alpha值,这样越远的星星显得越模糊
    38. currentStar.alpha = Math.min(1.0, 0.25 +
    39. ((INITIAL_STAR_DEPTH - starDepth) /
    40. (INITIAL_STAR_DEPTH - MINIMUM_STAR_DEPTH) * 0.8));
    41. //将星星加入数组
    42. stars.push(currentStar);
    43. //将星星加入场景之中
    44. addChild(currentStar);
    45. //跟新下一个星星的起始深度,让它显得更近一些
    46. starDepth -= ZDISTANCE;
    47. }
    48.  
  2. 你需要使用Timer来移动星星。或者你可以使用ENTER_FRAME事件,这样你可以调整点击的帧频,动画会相应与之统一。每次计时器到点之后,星星便向用户靠拢。你可以使用如下代码在Actions面板中创建定时器及处理程序:
    1. //创建一个定时器,间隔为30毫秒,这大约是每秒33.3帧。
    2. var actionTimer=new Timer(MYTIMERFREQUENCY);
    3. actionTimer.addEventListener(TimerEvent.TIMER, timerHandler);
    4. //这个函数在每次计时器到点时执行
    5. function timerHandler(e:TimerEvent):void
    6. {
    7. //循环遍历数组中所有星星
    8. for (var i=0; i < NUMBER_OF_STARS; i++)
    9. {
    10. //使用局部变量访问星星
    11. currentStar = stars[i];
    12. //减少其z值,让星星开起来更近一些
    13. currentStar.z -= zSpeed;
    14. //如果z值比最小的星星深度还要小,那么我们知道它已经“经过”了,于是它便可以放到队列
    15. //末尾,于是我们设置其z值,并将其放到显示列表的第一位(如果z值小于零,说明星星已经
    16. //“很接近了”,但还是有可能在屏幕上)。
    17. if (currentStar.z < MINIMUM_STAR_DEPTH)
    18. {
    19. //设置到队列的尾部,离用户最远
    20. currentStar.z = INITIAL_STAR_DEPTH;
    21. //将星星移动至第一位(即显示列表的最后)
    22. setChildIndex(currentStar,0);
    23. }
    24. //设置对象的alpha值,于是越远的星星显得越模糊
    25. currentStar.alpha = Math.min(1.0, 0.25 + ((INITIAL_STAR_DEPTH - currentStar.z) /
    26. (INITIAL_STAR_DEPTH - MINIMUM_STAR_DEPTH) * 0.8));
    27. }
    28. }
    29. //响应播放/暂停按钮的事件来开启或关闭计时器
    30. playpause.addEventListener(MouseEvent.CLICK, playButtonHandler);
    31. function playButtonHandler(ev:MouseEvent):void
    32. {
    33. if (actionTimer.running)
    34. {
    35. actionTimer.stop();
    36. playpause.play_mc.visible = true;
    37. playpause.pause_mc.visible = false;
    38. } else
    39. {
    40. actionTimer.start();
    41. playpause.play_mc.visible = false;
    42. playpause.pause_mc.visible = true;
    43. }
    44. }
    45.  
  3. 选择Control > Test Movie并点击播放按钮来测试动画。你现在可以看到星星笔直朝用户移动。你可以从Moving-Stars-1.fla文件来浏览到目前为止的功能。

添加用户交互

现在我们来处理控制星星动画的用户输入,它们控制星星的水平震动及移动速度:

  1. 使用角度和半径输入来控制星星向用户移动时的左右位置。把这些代码添加到星星的创建循环中,以此配置滑动条的初始值及变量范围。
    1. //配置滑动条
    2. angleSpeedSlider.value = 10;
    3. angleSpeedSlider.minimum = 1;
    4. angleSpeedSlider.maximum = 30;
    5. zSpeedSlider.value = zSpeed;
    6. zSpeedSlider.minimum = 1;
    7. zSpeedSlider.maximum = 30;
    8. radiusSlider.value = 7;
    9. radiusSlider.minimum = 1;
    10. radiusSlider.maximum = 20;
    11. //设置每个星星的初始角度(稍后增加)
    12. var angle:Number = 0;
    13. //每个星星之间的角度差距
    14. const ANGLEDIFFERENCE:Number = 0.3;
    15. //角度变化的速度
    16. var angleSpeed:Number = angleSpeedSlider.value * 0.01;
    17. //移动半径
    18. var radius:Number = radiusSlider.value * 20;
    19. //用于存放星星角度的数组,与存放星星的数组相对应
    20. var angles:Array = new Array();
    21.  
  2. 你同样需要改变星星的创建循环,以此设置和跟踪其水平位置。在创建星星时,根据滑动条的位置设置x值。将以下这行设置x值得代码:
    1. currentStar.x = (stage.stageWidth / 2);
    2.  

    替换这句为:
    1. currentStar.x = (stage.stageWidth / 2) - Math.sin(angle)*radius;
    2.  
  3. 在星星创建循环的末尾,添加以下几行代码来保存及增加角度
    1. //将星星的角度保存起来,每个星星和它的角度可以通过相同的数组下标来联系起来
    2. angles.push(angle);
    3. //更新下一个星星的角度
    4. angle += ANGLEDIFFERENCE;
    5.  
    6. 此时星星的创建循环应该是这样:
    7.  
    8. //创建星星的循环
    9.  
    10. //从最远的星星开始创建并进行定位
    11.  
    12. for (var i:int=0; i < NUMBER_OF_STARS; i++)
    13. {
    14. //创建一个新的星星
    15. currentStar = new Star();
    16. //所有星星的x和y坐标都相同:视图中间靠上
    17. currentStar.x = (stage.stageWidth / 2) - Math.sin(angle)*radius;
    18. currentStar.y = stage.stageHeight - ORIGINAL_YBORDER;
    19. //设置深度,即z值,这表示它与用户之间的距离
    20. currentStar.z = starDepth;
    21. //设置对象的alpha值,这样越远的星星显得越模糊
    22. currentStar.alpha = Math.min(1.0, 0.25 + ((INITIAL_STAR_DEPTH - starDepth) /
    23. (INITIAL_STAR_DEPTH - MINIMUM_STAR_DEPTH) * 0.8));
    24. //将星星加入数组
    25. stars.push(currentStar);
    26. //将星星加入场景之中
    27. addChild(currentStar);
    28. //跟新下一个星星的起始深度,让它显得更近一些
    29. starDepth -= ZDISTANCE;
    30. //将星星的角度保存起来,每个星星和它的角度可以通过相同的数组下标来联系起来
    31. angles.push(angle);
    32. //跟新下一个星星的角度
    33. angle += ANGLEDIFFERENCE;
    34. }
    35.  
  4. 改变计时器函数的几个地方,将滑动条的值用于z方向的速度,并以此计算新的x值。新的计时器函数为:
    1. //这个函数在每次计时器到点时执行
    2. function timerHandler(e:TimerEvent):void
    3. {
    4. //首先从滑动条中获得动态速度
    5. angleSpeed =angleSpeedSlider.value*0.01;
    6. zSpeed = zSpeedSlider.value;
    7. radius = radiusSlider.value * 20;
    8. //循环遍历数组中所有星星
    9. for (var i=0; i < NUMBER_OF_STARS; i++)
    10. {
    11. //使用局部变量访问星星
    12. currentStar = stars[i];
    13. //获取星星的角度
    14. angle = angles[i];
    15. //减少其z值,让星星开起来更近一些
    16. currentStar.z -= zSpeed;
    17.  
    18. //如果z值比最小的星星深度还要小,那么我们知道它已经“经过”了,于是它便可以放到队列
    19.  
    20. //末尾,于是我们设置其z值,并将其放到显示列表的第一位(如果z值小于零,说明星星已经
    21.  
    22. //“很接近了”,但还是有可能在屏幕上)。
    23.  
    24. if (currentStar.z < MINIMUM_STAR_DEPTH)
    25. {
    26. //设置到队列的尾部,离用户最远
    27. currentStar.z = INITIAL_STAR_DEPTH;
    28. //根据前一个星星的角度设置下一个的角度
    29. if (i != NUMBER_OF_STARS - 1)
    30. {
    31. //根据角度之差来设置前一个星星
    32. angle = angles[i+1] - ANGLEDIFFERENCE;
    33. }
    34. else
    35. {
    36. //angles[0]已经增加了,因此在这个if块之后也要减去刚添加的angleSpeed,
    37. //以此来维护angledifference。
    38. angle = angles[0] - ANGLEDIFFERENCE - angleSpeed;
    39. }
    40. //将星星移动至第一位(即显示列表的最后)
    41. setChildIndex(currentStar,0);
    42. }
    43. //增加并保存星星的新位置
    44. angle += angleSpeed;
    45. angles[i] = angle;
    46. //计算星星的新x值
    47. currentStar.x = (stage.stageWidth/2) - Math.sin(angle) * radius;
    48. //设置对象的alpha值,于是越远的星星显得越模糊
    49. currentStar.alpha = Math.min(1.0, 0.25 + ((INITIAL_STAR_DEPTH - currentStar.z) /
    50. (INITIAL_STAR_DEPTH - MINIMUM_STAR_DEPTH) * 0.8));
    51. }
    52. }
    53.  

现在你让用户可以控制星星的速度及摆动幅度了。示例文件Moving-Stars-2.fla展示了用户可控制的星星移动。

额外功能:增加转动

你可以同时进行多种变化,2D或3D均可。添加z轴的旋转和2D的做法相同。你可以把以下代码添加至星星的创建循环中(假设是五角星):

//设置旋转,每个星星都比其它的略有旋转 
currentStar.rotationZ = i*360/(NUMBER_OF_STARS*5);

根据i变量进行旋转可以让每个星星看上比前一个有一些转动,以下代码可以让星星慢慢地顺时针旋转:

//慢慢的顺时针旋转 
currentStar.rotationZ += 2;

完成了!示例文件Moving-Stars-3.fla中包含了最终的版本。作为额外的功能,你可以让用户来控制旋转,使用一个滑动条的值来代替上一行中的“2”。

总结

本教程基于计时器函数使用了显示对象及动画对象的z属性。作为另一篇关于Flash和ActionScript 3中3D功能的教程,请参考我的另一篇文章:使用ActionScript 3开发Flash的3D视图控制器

你也可以参考以下讨论3D功能的在线文档:

关于作者

Rodney Smith在Adobe Systems工作了6年,领导Adobe在线应用更新技术及电子商务传输引擎创建。他是Flex和Flash平台技术的忠实拥护者,在加入Adobe之前,他涉及的领域有流媒体,移动设备及搜索。他的兴趣包括软件安全和用户界面设计。

riadevID: 
您给予的分值: None 平均分: 8.5 ( 4 票)

发表新评论

  • 网页地址和电子邮件地址将会被自动转换为链接。
  • 行和段被自动切分。
  • 您可以使用下面的标签来高亮显示您的评论内容: <code>, <blockcode>. 可以使用"[foo]".旁边显示标签样式 "<foo>" PHP代码可以用这样的区块来包含<?php ... ?> or <% ... %>

更多格式化选项信息

验证区域
系统验证:请回答下面的问题