[英]AS3 Timers vs. ENTER_FRAME performance
我正在構建一個總是有一些移動的游戲,所以我使用了很多Timer實例來控制重復和觸發運動。
現在的問題是,我開始注意到一些性能“滯后”。 這是由於計時器嗎? 你建議使用ENTER_FRAME事件嗎?
相關:您是否建議任何其他可以提高性能的圖書館/方法? 簡單的Tween庫本身是不夠的。
也許它會更有意義, 只有一個計時器運行 ...據我所知,一個正在運行的Timer需要一個完整的線程......把它放在偽代碼中,Timer線程的主要代碼是什么東西像那樣 ...
while (input.isEmpty()) {
wait(interval);
output.add({timerId:thisId, tickId: tickId++});
}
輸出是一個出列主線程(執行ABC)現在每次檢查...有很多計時器,你將有很多線程,這是一個不必要的開銷...同樣,對於每個事件,從主線程的計時器需要彈出deque,這是昂貴的,因為它必須是線程安全的...然后必須找到相應的計時器,必須創建一個計時器事件(分配也相當昂貴) )然后發送,這也是多個電話的問題......
所以嘗試擁有一個計時器,或者使用setInterval ...另外,考慮一下,flash中的事件模型非常好,但價格昂貴......它用於解耦,以確保一個漂亮的架構......出於同樣的原因,這對性能危急情況不利......再一次,派遣一個活動很昂貴......
我做了一個小班,這是一個更多的手冊(這只是為了說明我的觀點,雖然它在理論上被使用):
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);
}
}
}
它執行得很好......這里是一個基准測試類(如果使用CS3 / CS4,你應該可以簡單地將它用作fla中的文檔類):
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("");
}
}
}
在我的機器上,使用60 FPS targetstet,我得到平均FPS為6.4(3分鍾后)和10-14 MB內存使用(波動來自於TimerEvent對象需要被垃圾收集的事實),用於定時器調用10000個函數...使用其他類,我得到55.2 FPS,95.0 MB內存使用率(非常恆定,波動率為1%),直接調用200000個函數...這意味着,在20倍時,你得到的幀率是9倍更高,你只使用8倍的內存......這應該可以讓你了解一個計時器創建的足跡...
這應該給你一個粗略的想法,在哪個方向去......
[編輯]我被問到,為什么我使用私有變量...哲學問題...我的規則:永遠不要讓任何人從外面直接改變你的對象的狀態...想象Ticker::_tickLength
受到protected
。 ..有人將其子類化,並寫入該變量......具有什么效果? Ticker::tickLength
的值將與間隔長度不同......我真的沒有看到優勢......
此外,私有字段僅在類中有效...這意味着任何人都可以在子類中重新定義它們而不會發生任何沖突......
如果我認為,那個子類應該有一個protected
方式對超類中定義的狀態生效,我做一個protected
setter ......但是,我仍然可以做出反應...我可以更改/驗證/鉗制值,拋出隨意的參數和范圍錯誤,調度事件等......如果你寫一個類,你自己負責維護其狀態的完整性及其對行為的影響......
不要暴露你的類的內部工作...你可能需要更改它們,破壞相關的代碼......還有:子類化被嚴重過度... :)
這就是為什么... [/ edit]
格爾茨
back2dos
我建議使用ENTER_FRAME作為游戲引擎的主“勾號”。 ENTER_FRAME與Flash Player的幀速率完全對齊,幀速率是代碼運行的真正最大幀速率。 定時器等只是近似值,並且不能以比ENTER_FRAME更快的速度執行。
事實上,雖然我最初使用Timers來處理所有的東西,但由於混疊問題,我正在慢慢地離開它們。 如果將Timer設置為30fps,但Flash Player最終以15fps運行,那么Timer將最終在ENTER_FRAME事件之間兩次調度它的TIMER事件。 如果這些TIMER事件導致昂貴的代碼(如果它是你的游戲引擎的嘀嗒聲),那么它有可能將玩家的實際幀率降低(因為現在你每個ENTER_FRAME滴答兩次)。
所以,如果你有想要定期運行的東西,那么Timer很好,但是為了運行接近你的SWF實際幀速率的任何東西,我建議只使用SWF的幀速率並根據需要調整邏輯。
一種方法是計算每個ENTER_FRAME的時間增量。 如果你有基於時間的邏輯,這是最好的方法。 另一種方法是,如果你的SWF假設一個固定的更新速率(比如基於計時器的代碼),如果你已經超過了任何給定的ENTER_FRAME的時間增量,那就是調用游戲的tick方法。
如果你落后(或者你最終會得到與計時器相同的情況),我不建議每個ENTER_FRAME做兩個滴答。 在某個時刻,你的游戲必須放慢速度或變得無法播放(因為增量太大了)。 當你已經放慢速度時,每個ENTER_FRAME執行多次勾選只會進一步降低你的速度。 用戶可以比跳過游戲玩法更好地處理減慢的游戲玩法。
如果你沒有使用補間庫,我會看看tweenlite或tweenmax。 它包括一個延遲的被叫定時器以及將補間分組在一起。 它性能卓越,使用簡單。
看一下性能測試
http://blog.greensock.com/tweening-speed-test/
玩笑
問題可能來自這樣一個事實,即計時器不是真正可靠的,因為它們不像我們認為的那樣獨立於fps。 當幀速率下降時,由於某種原因,定時器也會被不頻繁地調用。 這與C,C ++或其他OOP語言中的行為非常不同,因此很多人都陷入了這個陷阱。
為了避免這種情況,嘗試使用ENTER_FRAME事件作為主游戲循環並在該循環內部,評估時間,以了解您是否需要對游戲邏輯進行一次或多次更新。 這將使您的代碼完全獨立於fps。 您可以使用flash.utils.getTimer調用來獲取自啟動以來的時間。
我在我的網站上寫了一篇關於此的帖子: http : //fabricebacquart.info/wordpress/?p = 9
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.