Flash AS3定时器与输入帧性能

Flash AS3定时器与输入帧性能,flash,actionscript-3,actionscript,Flash,Actionscript 3,Actionscript,我正在构建一个游戏,它一直有一些移动的东西,所以我使用了很多计时器实例来控制重复和触发运动 现在的问题是,我开始注意到一些性能“滞后”。这是因为计时器吗?您是否建议改用ENTER_FRAME事件 相关:你有没有为这类游戏推荐其他可以提高性能的库/方法?简单的Tween库本身是不够的。如果你还没有使用Tween库,我会看看tweenlite或tweenmax。它包括一个被称为定时器的延迟器,以及将两个对象分组在一起。它具有很好的性能和简单的使用 看看这里的性能测试 Josh也许让只运行一个计时器

我正在构建一个游戏,它一直有一些移动的东西,所以我使用了很多计时器实例来控制重复和触发运动

现在的问题是,我开始注意到一些性能“滞后”。这是因为计时器吗?您是否建议改用ENTER_FRAME事件


相关:你有没有为这类游戏推荐其他可以提高性能的库/方法?简单的Tween库本身是不够的。

如果你还没有使用Tween库,我会看看tweenlite或tweenmax。它包括一个被称为定时器的延迟器,以及将两个对象分组在一起。它具有很好的性能和简单的使用

看看这里的性能测试


Josh

也许让只运行一个计时器会更有意义。。。 据我所知,一个正在运行的计时器需要一个完整的线程。。。 把它放在伪代码中,计时器线程的主代码是这样的

while (input.isEmpty()) {
    wait(interval);
    output.add({timerId:thisId, tickId: tickId++});
}
输出是一个出列主线程(执行ABC)检查每一个现在,然后。。。有很多定时器,你会有很多线程,这是一个不必要的开销。。。此外,对于每个事件,从计时器发送到主线程的消息都需要从deque弹出,这很昂贵,因为它必须是线程安全的。。。然后必须找到相应的计时器,必须创建一个计时器事件(分配也相当昂贵),然后进行调度,这也是一个需要多次调用的问题

所以试着用一个计时器,或者用setInterval。。。此外,考虑到,Flash中的事件模型相当不错,但价格昂贵…它用于解耦,以确保良好的体系结构。。。出于同样的原因,在性能关键的情况下,这并不好。。。再一次,派遣一个事件是昂贵的

我做了一个小类,这是一个多一点的手册(这只是为了说明我的观点,虽然它可以在理论上使用):

它的表现相当好。。。这里是一个基准类(如果使用CS3/CS4,您应该能够简单地将其用作fla中的文档类):

包{
//{地区进口
导入flash.display.*;
导入flash.events.*;
导入flash.sampler.getSize;
导入flash.system.system;
导入flash.text.*;
导入flash.utils。*;
//}端区
公共类Main扩展了MovieClip{
//{区域配置
private const timers:Boolean=false;//对于计时器为true,对于Ticker为false
专用常数延迟:数字=500;
private const baseCount:uint=10000;//要调用的函数的基计数
private const factor:Number=20;//Ticker的因子,性能稍高一点
//}端区
//{区域变量/常量
专用常量计数:uint=基本计数*(计时器?1:系数);
private const nullMem:uint=System.totalMemory;//这是虚拟机的占用空间……我们将减去它……好的,文本字段没有考虑在内,但应该可以……我想。。。
私有变量监视器:TextField;
私有变量帧计数:uint=0;
私有var secCount:uint=0;
//}端区
公共函数Main():void{
var t:Ticker=新的Ticker(延迟);
var genHandler:Function=Function():Function{
返回函数(e:TimerEvent):void{};
}
var genCallback:Function=Function():Function{
返回函数(tick:uint):void{};
}
对于(变量i:uint=0;i0)?sep:“”;})。reverse().join(“”);
}
}
}
在我的机器上,使用60 FPS targetet,对于使用计时器调用的10000个函数,我的平均FPS为6.4(3分钟后)和10-14 MB内存使用率(波动来自需要对TimerEvent对象进行垃圾收集的事实)。。。使用另一个类,我得到了55.2 FPS,95.0 MB内存使用率(非常恒定,波动不超过1%),200000个函数被直接调用。。。这意味着,在因子20时,帧速率会高出9倍,并且只使用8倍的内存。。。这应该能让你了解定时器会产生多少占用空间

这应该会让你大致知道该往哪个方向走

[编辑]有人问我,为什么要使用私人变量。。。哲学问题。。。我的规则:永远不要让任何来自外部的人直接改变你物体的状态。。。想象一下,
Ticker::_tickLength
受到了
保护
。。。有人将其子类化,并写入该变量。。。有什么效果?
Ticker::tickLength
的值将为b
package  {
    import flash.utils.*;
    public class Ticker {
        //{ region private vars
            private var _interval:int;
            private var _tick:uint = 0;
            private var _tickLength:Number;
            private var _callBacks:Dictionary;
        //} endregion
        public function Ticker(tickLength:Number = 0) {
            this.tickLength = tickLength;
            this._callBacks = new Dictionary();
        }
        //{ region accessors
            /**
             * the current tick
             */
            public function get tick():uint { return _tick; }
            /**
             * the tick length. set to a non-positive value, to stop ticking
             */
            public function get tickLength():Number { return _tickLength; }
            public function set tickLength(value:Number):void {
                if (this._tickLength > 0) clearInterval(this._interval);
                if ((this._tickLength = value) > 0) this._interval = setInterval(this.doTick, value);
            }       
        //} endregion
        /**
         * add a callback, to be called with every tick
         * @param   callback function (tick:int):*
         */
        public function addCallback(callback:Function):void {
            this._callBacks[callback] = callback;
        }
        /**
         * removes a callback previously added and returns true on success, false otherwise
         * @param   callback
         * @return
         */
        public function removeCallback(callback:Function):Boolean {
            return delete this._callBacks[callback];
        }
        /**
         * executes a tick. actually this happens automatically, but if you want to, you can set tickLength to a non-positive value and then execute ticks manually, if needed
         */
        public function doTick():void {
            var tick:uint = this._tick++;//actually, this is only superspicion ... amazingly, this makes no difference really ... :D
            for each (var callback:* in this._callBacks) callback(tick);
        }
    }
}
package {
    //{ region imports
        import flash.display.*;
        import flash.events.*;
        import flash.sampler.getSize;
        import flash.system.System;
        import flash.text.*;
        import flash.utils.*;   
    //} endregion
    public class Main extends MovieClip {
        //{ region configuration
            private const timers:Boolean = false;//true for Timer, false for Ticker
            private const delay:Number = 500;
            private const baseCount:uint = 10000;//base count of functions to be called
            private const factor:Number = 20;//factor for Ticker, which is a little more performant     
        //} endregion
        //{ region vars/consts
            private const count:uint = baseCount * (timers ? 1 : factor);
            private const nullMem:uint = System.totalMemory;//this is the footprint of the VM ... we'll subtract it ... ok, the textfield is not taken into account, but that should be alright ... i guess ...
            private var monitor:TextField;
            private var frameCount:uint = 0;
            private var secCount:uint = 0;      
        //} endregion
        public function Main():void {   
            var t:Ticker = new Ticker(delay);
            var genHandler:Function = function ():Function {
                return function (e:TimerEvent):void { };
            }
            var genCallback:Function = function ():Function {
                return function (tick:uint):void { };
            }
            for (var i:uint = 0; i < count; i++) {
                if (timers) {
                    var timer:Timer = new Timer(delay, 0);
                    timer.addEventListener(TimerEvent.TIMER, genHandler());
                    timer.start();                  
                }
                else {
                    t.addCallback(genCallback());
                }
            }
            this.addChild(this.monitor = new TextField());
            this.monitor.autoSize = TextFieldAutoSize.LEFT;
            this.monitor.defaultTextFormat = new TextFormat("_typewriter");
            this.addEventListener(Event.ENTER_FRAME, function (e:Event):void { frameCount++ });
            setInterval(function ():void { 
                    monitor.text = "Memory usage: " 
                        + groupDidgits(System.totalMemory - nullMem) 
                        + " B\navg. FPS: " + (frameCount /++secCount).toPrecision(3) 
                        + "\nuptime: " + secCount + "\nwith " + count + " functions"; 
                }, 1000);
        }
        private function groupDidgits(n:int,sep:String = " "):String {
            return n.toString().split("").reverse().map(function (c:String, i:int, ...rest):String { return c + ((i % 3 == 0 && i > 0) ? sep : ""); } ).reverse().join("");
        }
    }
}