AS3: Насколько точны метод getTimer () и класс Timer? - PullRequest
3 голосов
/ 18 июня 2009

Я нахожусь в процессе создания игры (shmup), и я начал подвергать сомнению точность таймеров в ActionScript. Конечно, они достаточно точны, когда вы хотите рассчитать время порядка нескольких секунд или десятых секунды, но, кажется, это работает довольно плохо, когда вы добираетесь до более мелких диапазонов. Это делает довольно трудным делать такие вещи, как космический корабль, запускающий сотни лазеров в секунду.

В следующем примере я проверил, как долго (в среднем) интервалы были между 1000 отметками таймера, предназначенными для 30 мс. Снова и снова результаты ~ 35-36мс. Уменьшая время, я обнаружил, что задержка таймера составляет ~ 16-17 мс. Это дает мне максимальный fps ~ 60, что замечательно визуально, но также означает, что я не могу запустить более 60 лазеров в секунду :-(. Я провел этот тест несколько раз на 100 и 1000 петлях, но для обоих 30 мс тест и 1 мс тест результаты не изменились. В конце я печатаю в textField, потому что использование trace () и запуск swf в режиме отладки, кажется, негативно влияют на тест. Так что мне интересно:

  • Является ли этот тест достойным показателем производительности класса Timer, или мои результаты сомнительны?
  • Значительно ли изменятся эти результаты на других машинах?

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

package 
{<br>
    import flash.display.Sprite;
    import flash.events.TimerEvent;
    import flash.text.TextField;
    import flash.utils.getTimer;
    import flash.utils.Timer;
</p> <pre>public class testTimerClass extends Sprite { private var testTimer:Timer = new Timer(30, 1000); private var testTimes:Array = new Array(); private var testSum:int = 0; private var testAvg:Number; private var lastTime:int; private var thisTime:int; public function testTimerClass() { testTimer.addEventListener(TimerEvent.TIMER, step); testTimer.addEventListener(TimerEvent.TIMER_COMPLETE, printResults); lastTime = getTimer(); testTimer.start(); } private function step(event:TimerEvent):void { thisTime = getTimer(); testTimes.push(thisTime - lastTime); lastTime = thisTime; } private function printResults(event:TimerEvent):void { while (testTimes.length > 0) { testSum += testTimes.pop(); } testAvg = testSum / Number(testTimer.repeatCount); var txtTestResults:TextField = new TextField(); txtTestResults.text = testAvg.toString(); this.addChild(txtTestResults); } }

}

Полагаю, лучший путь - нарисовать несколько лазеров в одном кадре с разными позициями и избегать использования более одного объекта Timer.

edit: я использовал stage.frameRate для изменения рендера frameRate и запускал тест на нескольких частотах кадров, но изменений не было.

Ответы [ 5 ]

4 голосов
/ 19 июня 2009

Tinic Uro (инженер Flash Player) написал интересный блог сообщение об этой проблеме некоторое время назад.

2 голосов
/ 19 июня 2009

Вместо того, чтобы вообще использовать класс таймера, я бы вставил свой код обновления и в Eventfistenme enterframe.

Затем используйте getTimer (), чтобы узнать, сколько времени прошло с момента последнего обновления, а затем немного математики для расчета движения, лазерного «нереста» и т. Д. Таким образом, игра должна работать одинаково, независимо от вы получаете 60FPS или 10FPS.

Это также предотвращает странное поведение игры, если падение FPS временно или игра запускается на медленной машине.

По большей части математика довольно проста, и когда это не так, вы обычно можете «срезать углы» и все же получить достаточно хороший результат для игры. Для простого перемещения вы можете просто умножить смещение движения на время с момента последнего обновления. Для ускорения вам нужно уравнение второй степени, но вы можете упростить повторное умножение.

В ответ на комментарий Джорелли ниже:

Проблема обнаружения столкновений может быть решена с помощью лучшего кода, который не только проверяет текущее состояние, но также и то, что произошло между предыдущим состоянием и текущим. У меня никогда не было проблем с клавиатурой. Может быть, вы должны попробовать этот классный маленький класс: http://www.bigroom.co.uk/blog/polling-the-keyboard-in-actionscript-3

1 голос
/ 16 марта 2011

BitmapData.scroll и BitmapData.copyPixels - это самый быстрый способ рендеринга во флэш-памяти прямо сейчас, хотя в некоторых системах использование мувиклипов с ускорением gpu немного быстрее. Каждый квад отображается в OpenGL как отдельная текстура, поэтому это не то, что вы ожидаете от обычного GPU-ускоренного приложения. Я знаю, потому что я профилировал оба пути в своей игре. Одна вещь, которую вы можете сделать, чтобы улучшить время отклика, - вызвать обновление игры из обработчиков входных событий. Таким образом, вы слышите правильно синхронизированные звуки, даже когда частота кадров в вашей игре ниже, и приложение будет чувствовать себя более отзывчивым.

1 голос
/ 19 июня 2009

Я только что попытался запустить ваш пример кода, в точности как есть, за исключением сценария кадра, и там, где я сижу, таймер работает точно так, как вы ожидаете. При таймере 30 мс среднее значение составляет около 33-34, а при таймере 3 мс - около 3,4 или 3,5. С таймером 1 мс я получаю от 1,4 до 1,6 более тысячи испытаний. Так работает во Flash и в браузере.

Что касается точности, см. Сообщение в блоге Тиника в ответе Люка. Но для верхнего предела по частоте, если вы получаете события с интервалом не более 16 мс, используя только пример кода, который вы разместили, либо что-то странное, либо, возможно, это верхний предел скорости, с которой ваш браузер выдает сообщения синхронизации Flash. Если вы получаете эти результаты в своей реальной игре, я думаю, что у вас просто есть синхронный код, который блокирует события таймера.

Еще одна вещь - я знаю, что это не то, что вы просили, но было бы разумнее обрабатывать вашу игровую логику в обработчике ENTER_FRAME. Не имеет значения, запускается ли код для запуска лазеров каждые 30 мс или каждые 3 мс, экран перерисовывается только один раз за кадр (если только вы не заставляете его обновляться чаще, чего, вероятно, не должно быть). Таким образом, все, что происходит между обновлениями экрана, - это просто накладные расходы, которые снижают общую частоту кадров, так как с небольшой хитростью должно быть возможно достичь точно таких же результатов, которые вы получите от таймера, который выполняется чаще. Например, вместо создания лазера каждые 3 мс, вы можете создавать 10 лазеров каждые 30 мс и перемещать каждый на определенное расстояние по траектории полета, как если бы он был создан на 3, 6 или 9 мс ранее.

Другой естественный способ запуска логики вне фреймовых событий - создание игрового цикла, который «теоретически» запускается каждые T миллисекунд. Затем в обработчике ENTER_FRAME просто выполните итерацию этого цикла F / T раз, где F - количество мс, прошедших с момента последнего события кадра. Таким образом, если вы хотите, чтобы игра теоретически обновлялась каждые 5 мс, и вы хотите, чтобы экран обновлялся со скоростью 30 кадров в секунду, вы просто публикуете со скоростью 30 кадров в секунду, а в обработчике ENTER_FRAME вы будете вызывать цикл основной игры 5-7 раз подряд в зависимости от того, сколько времени прошло с последнего кадра. На экране это даст вам такие же результаты, как если бы вы использовали событие таймера 5 мс, и это избавит от значительной части накладных расходов.

0 голосов
/ 18 июня 2009

Это дает мне максимальный fps ~ 60, что замечательно визуально, но также означает, что я не могу запустить более 60 лазеров в секунду: - (

Я бы сказал, что вам очень повезло, что вы получаете такой FPS, какой он есть, и большинство ваших пользователей (при условии, что ваша аудитория - это интернет в целом), скорее всего, не смогут достичь такой частоты кадров.

Я бы согласился, что возможностей флеш-плеера, вероятно, недостаточно для того, чего вы пытаетесь достичь.

...