Таймеры AS3 и производительность ENTER_FRAME

Я создаю игру, в которой все время есть движущиеся объекты, поэтому я использую множество экземпляров Timer для управления повторением и запуска движения.

Теперь дело в том, что я начал замечать некоторые «лаги» производительности. Это из-за таймеров? и вы предлагаете использовать событие ENTER_FRAME вместо этого?

Связанный: Вы предлагаете какую-либо другую библиотеку / метод для таких игр, которые могли бы повысить производительность? Простые библиотеки Tween сами по себе недостаточны.

Ответы на вопрос(4)

Я рекомендую использовать ENTER_FRAME в качестве основного тега & quot; для вашего игрового движка. ENTER_FRAME точно соответствует частоте кадров Flash Player, которая является истинной максимальной частотой кадров, с которой будет работать ваш код. Таймеры и т. Д. Являются всего лишь приблизительными значениями и не могут выполняться быстрее, чем ENTER_FRAME.

На самом деле, хотя я изначально использовал таймеры для всех своих вещей, я медленно удалялся от них из-за проблем с алиасами. Если вы установите таймер на 30 кадров в секунду, но проигрыватель Flash Player будет работать на скорости 15 кадров в секунду, тогда таймер будет в итоге дважды отправлять событие TIMER между событиями ENTER_FRAME. Если эти события ТАЙМЕРА приводят к дорогостоящему коду (что было бы, если бы это было тиковым движком вашего игрового движка), тогда он потенциально может подтолкнуть игрока.actual частота кадров ниже (потому что теперь вы тикаете дважды за ENTER_FRAME).

Таким образом, таймер хорош, если у вас есть что-то, что вы хотите периодически запускать, но для запуска чего-либо, близкого к фактической частоте кадров вашего SWF, я рекомендую просто использовать частоту кадров SWF и при необходимости корректировать свою логику.

Один из подходов состоит в том, чтобы рассчитать дельты времени для каждого ENTER_FRAME. Если у вас есть логика, основанная на времени, это лучший подход. Другой подход, если ваш SWF-файл предполагает фиксированную частоту обновления (например, код на основе таймера), состоит в том, чтобы вызывать тиковый метод вашей игры, если-и-только-если вы превысили дельту времени для любого заданного ENTER_FRAME.

я мог быnot Рекомендую делать два тика на ENTER_FRAME, если вы отстаете (или вы столкнетесь с той же ситуацией, что и таймеры). В определенный момент ваша игра должна замедлиться или она становится неиграбельной (потому что дельты становятся слишком большими). Выполнение более одной отметки за ENTER_FRAME, когда вы уже замедлились, только замедлит вас. Пользователи могут лучше справлятьсяslowed геймплей, чем они могутskipping геймплей.

 Makram Saleh22 июл. 2009 г., 08:38
Спасибо за ценную информацию! Дело в том, что я использую комбинацию обоих. ENTER_FRAME для моего персонажа, перемещенного мышью, и таймер для движущихся машин ... Хорошая вещь заключается в том, что 'lag' apos; Я упоминал в своем предыдущем вопросе только опыт в инструменте разработки Flash. Когда я открывал только SWF (работает в автономном флеш-плеере), скорость была идеальной, 30 кадров в секунду.

если вы еще не используете библиотеку анимации, я бы посмотрел на tweenlite или tweenmax. он включает задержанный вызываемый таймер, а также группирует анимацию вместе. он имеет отличную производительность и прост в использовании.

посмотрите здесь на тесты производительности

http://blog.greensock.com/tweening-speed-test/

мистифицировать

 Makram Saleh09 июл. 2009 г., 12:05
Попробую.

Вероятно, проблема связана с тем фактом, что таймеры не очень надежны в том смысле, что они не настолько независимы от fps, как мы думаем. Когда частота кадров падает, по какой-то причине таймеры также будут вызываться реже. Это сильно отличается от поведения в C, C ++ или других языках ООП, и поэтому многие попадают в эту ловушку.

Чтобы избежать этого, попробуйте использовать событие ENTER_FRAME в качестве основного игрового цикла, и в этом цикле оцените время, чтобы узнать, нужно ли вам сделать одно или несколько обновлений игровой логики. Это сделает ваш код полностью независимым от fps. Вы можете использовать вызов flash.utils.getTimer, чтобы узнать время с момента запуска.

Я написал пост об этом на моем сайте: http://fabricebacquart.info/wordpress/?p=9

 13 мая 2013 г., 05:12
Теперь это работает. Благодарю.
 12 мая 2013 г., 21:15
Ваша ссылка не работает ..
Решение Вопроса

может быть, было бы больше смысла иметьonly one timer running в этом отношении ... насколько я знаю, работающему таймеру нужна целая нить ... чтобы поместить его в псевдокод, основной код потока Timer выглядит примерно так ...

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

вывод, являющийся очередью, основной поток (который выполняет ABC) проверяет каждый сейчас и потом ... имея много таймеров, у вас будет много потоков, что является ненужными издержками ... также, для каждого события - сообщение, отправленное из таймер для основного потока должен быть извлечен из очереди, что дорого, так как он должен быть потокобезопасным ... и затем должен быть найден соответствующий таймер, должно быть создано событие таймера (выделение также довольно дорого ), а затем отправили, что также является вопросом нескольких звонков ...

так что попробуйте иметь ОДИН таймер или используйте setInterval ... также, учтите, что модель 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);
        }
    }
}

он работает довольно хорошо ... здесь класс бенчмаркинга (вы можете просто использовать его как класс документа в FLA, если вы используете CS3 / CS4):

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 таргетингом я получаю средний FPS 6,4 (через 3 минуты) и 10-14 МБ использования памяти (колебания происходят из-за того, что объекты TimerEvent нужно собирать мусор) для 10000 функций, вызываемых с таймерами ... используя другой класс, я получаю 55,2 FPS с использованием 95,0 МБ памяти (очень постоянный, колебания не превышают 1%) с непосредственным вызовом 200000 функций ... это означает, что при коэффициенте 20 вы получаете частоту кадров, равную 9 раз выше, и вы используете только 8 раз памяти ... это должно дать вам представление о том, сколько места занимает таймер ...

это должно дать вам приблизительное представление, в каком направлении идти ...

[edit] меня спросили, почему я использую частные переменные ... вопрос философии ... мое правило: никогда не позволяйте никому извне напрямую изменять состояние вашего объекта ... представьте себеTicker::_tickLength былоprotected ... кто-то подклассы его и пишет в эту переменную ... с каким эффектом? значениеTicker::tickLength будет отличаться от длины интервала ... я действительно не вижу преимущества ...

Кроме того, закрытые поля действительны только в классе ... это означает, что любой может переопределить их в подклассах без каких-либо коллизий ...

если я думаю, что подклассы должны иметьprotected способ воздействия на состояние, определенное в суперклассе, я делаюprotected сеттер ... но все же, я могу реагировать ... я могу изменять / проверять / фиксировать значение, генерировать аргумент и ошибки диапазона по желанию, отправлять события и так далее ... если вы пишете класс, вы сами несете ответственность для поддержания целостности своего состояния и воздействия на его поведение ...

не раскрывайте внутреннюю работу вашего класса ... вам может понадобиться изменить их, нарушив зависимый код ... а также: подклассы сильно переоценены ... :)

так вот почему ...[/edit]

Greetz

back2dos

 10 июл. 2009 г., 15:17
речь идет об инкапсуляции и написании надежного кода, который либо всегда будет работать как положено, либо выдает ошибки времени выполнения, если таковые имеются. делает глупости с этим. для меня хороший API - мощный, маленький и ориентированный на проблемы. не заботиться о том, как, пока это то, что является надежным. это то, что я ожидаю, и именно поэтому я делаю это сам. Кстати, смысл подкласса заключается не в том, чтобы возиться с некоторыми свойствами суперкласса, а для конкретной реализации абстрактного поведения и является очень хорошим инструментом для создания IOC. Вы можете задать вопрос обо всем этом, если вы действительно хотите серьезно обсудить этот вопрос.
 10 июл. 2009 г., 02:12
Что относится к «региону»? материал?
 10 июл. 2009 г., 02:14
Ой. И почему вы делаете переменные-члены частными, а не защищенными?
 10 июл. 2009 г., 09:42
@luke про регионы: я использую flashdevelop для развития ActionScript ... регионы позволяют настраивать свертывание, поэтому я могу сворачивать различные части класса ... плюс это дает коду дополнительную структуру ... просто мое соглашение по кодированию, так сказать ...
 Makram Saleh10 июл. 2009 г., 08:44
Спасибо за бесценную информацию относительно таймеров. Я должен попробовать твой код; тикер звучит многообещающе!

Ваш ответ на вопрос