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


    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

    你可能感兴趣的还有:

  • 相关阅读:
    C# 多线程详解 Part.02(UI 线程和子线程的互动、ProgressBar 的异步调用)
    ubuntu 18.04安装ftp服务器
    ubuntu 18.04设置开机自动挂载移动硬盘
    使用apache commons net进行ftp传输
    Navicat连接MySQL 8出现2059
    Eclipse配置tomcat
    MySQL从.ibd文件中恢复数据
    MySQL建表时添加备注以及查看某一张表的备注信息
    在node.js中使用Set
    Java测试当前应用所占用的内存示例
  • 原文地址:https://www.cnblogs.com/skynet/p/2078554.html
Copyright © 2020-2023  润新知