走在网页游戏开发的路上(六)

简介:




本文转自吴秦博客园博客,原文链接:http://www.cnblogs.com/skynet/archive/2011/06/11/2078554.html,如需转载请自行联系原作者Flash动画原理

——动画是将静止的画面变为动态的艺术.实现由静止到动态,主要是靠人眼的视觉残留效应.利用人的这种视觉生理特性可制作出具有高度想象力和表现力的动画影片.

0. 前言

像所有的动画显示一样,Flash的动画原理也是通过不断的刷新屏幕,利用每次屏幕上显示对象位置的不同、大小色彩的变化等产生动画效果。动画编程的关键是一定要有变化,而且该变化需要在一定时间内来完成(以达到欺骗人眼,使分解的画面连续起来,达到运动的效果)。Flash中使用帧频来控制每秒钟刷新屏幕的次数,通过使用的帧频的范围在1260帧每秒,推荐使用的是24帧每秒这样的一个速度。

ActionScript 3.0的动画编程也是同样的原理,通过不断的刷新屏幕来实现动画效果。ActionScript 3.0的动画编程按照实现的方式可以分为两种:

F 对显示对象的显示属性进行控制,调整每次刷新屏幕时显示对象的显示属性实现动画效果,这种称为显示属性动画

F 利用绘制API在每帧中使用代码绘制不同的形状,从而产生的动画效果,这种称为代码绘制动画

通过编程实现刷新屏幕,ActionScript 3.0中提供了两种方式可供选择:

F 帧循环更新:利用Flash本身的帧频速度,在每次刷新屏幕时改变显示对象。此方法需要使用事件侦听器侦听显示对象的Event.ENTER_FRAME事件。

F 定时器更新:利用Timer类的定时更新功能,每隔一定的事件使显示对象改变一次。此方法需要使用事件侦听器侦听显示对象的TimerEvent.TIMER事件。

下面具体介绍帧循环更新、定时器更新实现动画效果,和它们的差异及选择。

1. Event.ENTER_FRAME事件做动画的原理

每当Flash运行器执行一次预定屏幕更新检查的时候它调度Event.ENTER_FRAME事件。注册以接收Event.ENTER_FRAME通知的任何函数都被反复执行,在由当前Flash运行器帧速率决定的一个频率下。由任何Event.ENTER_FRAME事件监听器做出的可见变化在它退出之前被描绘,这就是Event.ENTER_FRAME做动画的原理。

Event.ENTER_FRAME事件做动画的缺陷

每当想要改变指定的帧速率的时候,我们必须更新基于该帧速率计算速度的所有代码(除非使用基于速度的动画)

Flash运行器不总是能到达指定的帧速率,动画变慢。这种变慢甚至因系统负载而不同,帧速率可能只在短时期下降后恢复到它的正常速率。

在一般情况中,用稍有差别的速度播放一个动画是可以接受的,但是,当要求精确控制或者可见精度是有影响的时候,我们必须考虑帧速率的缺陷。此时,我 们用基于逝去的时间而不是关联于指定帧来计算移动一个移动的距离更合适(即基于速度的动画)

2. Timer做动画的原理

Timer类是一个一般的实用程序类,用于在一个特定的时间间隔后执行代码。每个Timer对象在一个程序员指定的频率下调度TimerEvent.TIMER事件。想要在该频率下执行的函数用Timer对象为TimerEvent.TIMER事件来注册。

帧速率对Timer的影响

尽管Timer类看似提供了一个完全随意的方式来在一个指定时期之后执行一个函数,但可能令人惊奇的是,它任然依赖于Flash运行器的帧速率。对 于每次预定屏幕更新检查一个TimerEvent.TIMER事件最多可以产生10次(10倍于帧速率)。例如,给定每秒1帧的一个帧速率,一个 TimerEvent.TIMER事件最多(快)只能每100毫秒执行一次,甚至在一个更小的delay值指定给一个Timer对象的时候。在每秒10 的时候,一个TimerEvent.TIMER事件每秒最多可以发生100次(10毫秒每次)。在每秒100帧的时候,一个 TimerEvent.TIMER事件最多每秒可发生1000次(1毫秒每次)。

当一个TimerEvent.TIMER事件设置为运行得比帧速率更不频繁的时候,它将执行于下一次预定屏幕更新的时间间隔之后。为了请求更新于下 次预定更新前,使用TimerEvent类的实例方法updateAfterEvent()

3. TimerEvent.ENTER_FRAME之间选择

帧速率受变化的控制:当一个.swf文件被另一个运用程序装载的时候,该运用程序的帧速率可能和.swf文件指定的帧速率有很大的不同(被装在的帧 速率被屏蔽,统一用运用程序的帧速率),从而导致.swf的动画播放太快或者太慢。被装载的.swf文件当然可以设置帧速率,但是改变帧速率可能导致在父 亲应用程序的不需要的播放行为Timer类提供了一些帧速率的独立性。

使用大量的Timer对象需要更多的内存:在分散动画管理体系结构中,使用一个单独的Timer来控制每个对象动画需要的内存要多于通过类似的 Event.ENTER_FRAME实现可能需要的。

使用大量的Timer对象会导致过度的更新请求:在分散动画管理体系结构中,使用一个单独的TimerupdateAfterEvent()相关 联来控制每个对象的动画导致了对屏幕的多个独立的请求,有可能导致性能的问题。

基于这些因素,这里是一个推荐的最佳操作:

在必须使程序内容的显示同步于手工创建于Flash authoring tool(Flash CS3,Flash CS4)的基于帧内容的显示的应用程序中,使用Event.ENTER_FRAME

Flash运行器中的帧速率的变动必须被减轻的应用程序中,使用单个的Timer对象来编制所有动画,并使用基于速度的动画

当在Flash运行器中的帧速率的变动当做是可以接受的时候,使用Event.ENTER_FRAME(因为使用基于 Event.ENTER_FRAME的动画代码一般要比基于Timer的等价物更简单和使用较少的内存)

避免使用单个的Timer对象来移动单个的显示对象。只要有可能,使用一个单个的Timer对象来编制所有的动画。另外提示一下,如果你想在不 同时间更新不同的对象,单独的Timer对象可能更合适

4. 代码实例

下面我们构建一个运动的矩形,分别使用Event.ENTER_FRAMETimerEvent.TIMER来实现。整个应用程序构成如下:

F 一个矩形

F 按钮1Event.ENTER_FRAME

F 按钮2TimerEvent.TIMER

点击按钮1,通过Event.ENTER_FRAME机制使矩形从屏幕的左到右运动起来;点击按钮2,通过TimerEvent.TIMER机制使矩形从屏幕的左到右运动起来。

解析:(1)使用Event.ENTER_FRAME机制,矩形必须监听Event.ENTER_FRAME事件(addEventListener(Event.ENTER_FRAME, animateFrame)),通过相应的事件处理函数animateFrame调整矩形的屏幕位置达到运动的效果。

2)使用TimerEvent.TIMER机制,timer通过监听TimerEvent.TIMER事件(addEventListener(TimerEvent.TIMER, animateTimer)),通过相应的事件处理函数animateTimer调整矩形在屏幕的位置达到运动的效果。

完整的代码如下:

package 
{
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.events.TimerEvent;
    import flash.geom.Rectangle;
    import com.bit101.components.PushButton;
    import flash.utils.Timer;
 
    
    /**
     * ...
     * @author Tyler
     */
    [SWF(width=500,height=300)]
    public class Main extends Sprite 
    {
        private var rect:Sprite;
        private var timer:Timer;
        
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            rect = new RectContainer(0, 0, 200, 200);                        
            addChild(rect);        
            
            //设置计时器,每50毫秒出发一次
            timer = new Timer(50);
        
            var enterFrameBtn:PushButton = new PushButton(this, 0, 250, "Event.ENTER_FRAME");
            enterFrameBtn.name = "ENTER_FRAME";
            enterFrameBtn.addEventListener(MouseEvent.CLICK, onClickHander);
            
            var timerBtn:PushButton = new PushButton(this, 120, 250, "Event.Timer");
            timerBtn.name = "TIMER";
            timerBtn.addEventListener(MouseEvent.CLICK, onClickHander);            
            
        }
        
        private function onClickHander(e:MouseEvent):void 
        {
            var name:String = e.target.name as String;
            if (name == "ENTER_FRAME")
            {
                timer.stop();
                rect.addEventListener(Event.ENTER_FRAME, animateFrame);
            }
            else if (name == "TIMER")
            {
                rect.removeEventListener(Event.ENTER_FRAME, animateFrame);                    
                timer.addEventListener(TimerEvent.TIMER, animateTimer);    
                timer.start();
            }
            
        }
        
        private function animateFrame(e:Event):void 
        {
            rect.x += 5;
            if (rect.x >= stage.stageWidth)
            {
                rect.x = 0;
            }
        }
        
        private function animateTimer(e:TimerEvent):void 
        {
            rect.x += 1;
            if (rect.x >= stage.stageWidth)
            {
                rect.x = 0;
            }
            
            e.updateAfterEvent();
        }        
    }
    
}
 
import flash.display.Shape;
import flash.display.SimpleButton;
import flash.display.Sprite;
 
class RectContainer extends Sprite 
{
    public function RectContainer(x:Number,
                                    y:Number,
                                    w:Number,
                                    h:Number) 
    {
        this.graphics.beginFill(0xFFFFFF * Math.random());
        this.graphics.drawRect(x, y, w, h);
        this.graphics.endFill();
    }
 
}

运行效果如下flash所示:

参考文献:

[1] ActionScript3殿堂之路

[2]ActionScript 3.0 Cookbook

你可能感兴趣的还有:

相关文章
|
4月前
|
前端开发 JavaScript 开发工具
震惊!前端小白到大神的蜕变之路,这些技巧你竟然还不知道?
前端开发是互联网技术的重要组成部分,从新手到大神需要掌握HTML、CSS和JavaScript的基础知识,熟练使用框架和工具,如React、Vue和Git,并注重性能优化。持续学习和实践是成长的关键。本文分享了一些实用技巧,帮助你在前端开发之路上快速进步。
50 4
|
4月前
|
前端开发 JavaScript UED
不可思议!前端小白如何靠这些技巧逆袭,成为团队中的闪耀之星?
前端开发对初学者来说充满挑战,但通过正确的方法和技巧,你可以从新手蜕变为高手。本文分享前端小白逆袭的秘诀,包括夯实HTML、CSS与JavaScript基础,掌握前端框架与库,提升性能优化技巧,以及持续学习与分享。示例代码展示了简单的HTML+CSS+JavaScript页面和Vue组件,帮助你逐步进阶。
52 4
|
6月前
|
算法 Python
编程之道:从小白到大牛的蜕变之路
【9月更文挑战第25天】在编程的世界里,每个人都是从零开始,但并非每个人都能成为大牛。本文将通过深入浅出的方式,分享我从编程小白到大牛的蜕变之路,包括学习编程的初衷、遇到的困难、解决问题的方法和心得体会。希望我的经历能给你带来启示和鼓舞,让你在编程的道路上越走越远。
|
5月前
|
前端开发 API 开发者
🥇前端宝藏:多项目掌握技能的冒险之旅🏆
在前端开发的学习旅程中,实践是提升技能的关键。本文介绍了多个前端项目,包括计算器、天气应用、经典游戏等,涵盖了从React到Svelte的各种技术栈。每个项目都附有在线演示和源代码,旨在帮助读者深入理解实现细节,激励更多人参与实际项目开发。通过这些项目,读者可以将理论知识转化为实践,拓展职业机会。
40 0
|
设计模式 前端开发 JavaScript
前端周刊-2018年9月第三期
前端周刊-2018年9月第三期
107 0
|
JavaScript Java
【游戏开发】自从遇见了口袋方舟后,我的世界变得精彩了起来
【游戏开发】自从遇见了口袋方舟后,我的世界变得精彩了起来
216 0
|
搜索推荐 Windows
分享5款2023年不容错过的宝藏软件
今天带来五款宝藏软件,身为宝藏男孩和宝藏女孩的你们,不试一下吗?
249 0
分享5款2023年不容错过的宝藏软件
|
架构师 测试技术 程序员
全攻略!!!告诉学弟学妹怎么入行游戏行业
有粉丝问我想要做游戏,怎么才能进入游戏行业呐?忆当年,已过十余载,当年大学毕业的时候有学姐进入珠海西山居做测试,当时觉得很高端,好想进去,可惜没有如意,当时觉得很遗憾,不过两年后我还是进入了游戏行业,只是为了弥补遗憾,为了不让和我有同样想法的同学有遗憾,今天聊一下具体入行攻略。
298 0
全攻略!!!告诉学弟学妹怎么入行游戏行业
|
前端开发 JavaScript Shell
前端周刊第三期
前端周刊第三期
|
机器学习/深度学习 人工智能 编解码
电子相册搭建(感悟)
电子相册搭建(感悟)
884 0