gpt4 book ai didi

flash - AS3 计时器与 ENTER_FRAME 性能

转载 作者:行者123 更新时间:2023-12-04 01:58:18 24 4
gpt4 key购买 nike

我正在构建一个游戏,它总是有一些移动的东西,所以我使用了很多 Timer 实例来控制重复和触发 Action 。

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

相关:您是否建议任何其他可以提高性能的游戏的库/方法?简单的 Tween 库本身是不够的。

最佳答案

拥有 或许更有意义只有一个计时器在运行对于这个问题 ...
据我所知,一个正在运行的 Timer 需要一个完整的线程......
把它放在伪代码中,Timer线程的主代码是这样的......

while (input.isEmpty()) {
wait(interval);
output.add({timerId:thisId, tickId: tickId++});
}

输出是主线程(执行 ABC)每隔一段时间检查一次...有很多计时器,您将有很多线程,这是不必要的开销...此外,对于每个事件,从主线程的定时器需要从双端队列中弹出,这很昂贵,因为它必须是线程安全的......然后必须找到相应的定时器,必须创建一个定时器事件(分配也很昂贵) 然后分派(dispatch),这也是多次调用的问题......

所以尝试使用 ONE 计时器,或者使用 setInterval ...另外,考虑一下,Flash 中的 Event 模型非常好,但很昂贵......它用于解耦,以确保良好的架构......出于同样的原因,这对于性能危急的情况是不利的......再一次,调度一个事件是昂贵的......

我做了一个小课,这是一个更手动的(这只是为了说明我的观点,尽管它在理论上可以使用):
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 目标,我得到 6.4 的平均 FPS(3 分钟后)和 10-14 MB 的内存使用量(波动来自于 TimerEvent 对象需要被垃圾收集的事实),用于使用计时器调用 10000 个函数...使用其他类,我得到 55.2 FPS 和 95.0 MB 内存使用量(非常恒定,波动低于 1%),直接调用 200000 个函数......这意味着,在 20 倍时,你得到的帧速率是 9 倍更高,并且您只使用了 8 倍的内存...这应该让您了解计时器创建了多少内存...

这应该让你有个大概的想法,往哪个方向走……

[编辑] 有人问我,为什么我使用私有(private)变量……哲学问题……我的规则:永远不要让外部的任何人直接改变你的对象的状态……想象一下 Ticker::_tickLengthprotected ...有人对其进行子类化,并写入该变量...有什么效果? Ticker::tickLength 的值将不同于间隔长度......我真的没有看到优势......

此外,私有(private)字段仅在一个类中有效......这意味着任何人都可以在子类中重新定义它们而不会发生任何冲突......

如果我认为,子类应该有 protected使父类(super class)中定义的状态生效的方法,我创建了 protected setter ...但是,我仍然可以使用react...我可以更改/验证/钳位值,随意抛出参数和范围错误,调度事件等等...如果你写一个类,你自己负责为了保持其状态的完整性及其对其行为的影响......

不要暴露你的类的内部工作......你可能需要改变它们,破坏依赖代码......而且:子类化被严重高估了...... :)

所以这就是为什么... [/编辑]

问候

back2dos

关于flash - AS3 计时器与 ENTER_FRAME 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1102839/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com