Стандартный таймер во flash. Стоит ли его использовать в играх?
День добрый, сегодня на наш разделочный стол ложится его величество Timer, и цель нашей вивисекции: определить так ли уж он точен, как нам кажется на первый взгляд.
Ведь многие считают – миллисекунды они и в Африке миллисекунды. Например частота вызова события ENTER_FRAME может упасть с увеличением нагрузки на CPU, но наш таймер то работает с временными отрезками и не зависит от какой то там производительности
поэтому время он отмеряет весьма точно.
Что ж вносите пациента в операционную, будем его резать. Код нашего примерчика довольно прост:
//Временной квант, по прошествии которого //срабатывает событие таймера var timeQuant:uint=2000; //Инициализация таймера var myTimer:Timer = new Timer(timeQuant); myTimer.addEventListener(TimerEvent.TIMER, TimerElapsed); myTimer.start(); //Будем проверять наш таймер с помощью системного таймера. //Запоминаем начальное время var startTime:Number = getTimer(); //Функция вызывается каждый квант времени function TimerElapsed(event:TimerEvent):void { //Cчитаем сколько времени прошло по системному таймеру var diffTime:Number=getTimer() - startTime; //Cчитаем ошибку в процентах var errPercent:Number=(timeQuant-diffTime)*100/timeQuant; //Выводим в консоль данные trace("Квант времени:" + timeQuant + " Прошло времени:" + diffTime + " Ошибка:" + errPercent + "%"); //Запоминаем время начала нового кванта времени startTime = getTimer(); }
Всё как видите просто. С помошью функции getTimer() получаем системное время, и с помощью него проверяем точность таймера флеша. В данном примере квант времени, с которым работает таймер равен 2 секундам. Давайте запустим и узнаем результаты:
Квант времени:2000 Прошло времени:2071 Ошибка:-3.55%
Квант времени:2000 Прошло времени:2014 Ошибка:-0.7%
Квант времени:2000 Прошло времени:2013 Ошибка:-0.65%
Квант времени:2000 Прошло времени:2020 Ошибка:-1%
Квант времени:2000 Прошло времени:2016 Ошибка:-0.8%
Квант времени:2000 Прошло времени:2013 Ошибка:-0.65%
Квант времени:2000 Прошло времени:2014 Ошибка:-0.7%
Квант времени:2000 Прошло времени:2011 Ошибка:-0.55%
Квант времени:2000 Прошло времени:2014 Ошибка:-0.7%
Ошибка менее 1%. Впринципе это вполне хорошо и даже отлично. Теперь давайте уменьшим квант времени до 1 секунды var timeQuant:uint=1000;
Результаты получаются следующие:
Квант времени:1000 Прошло времени:1024 Ошибка:-2.4%
Квант времени:1000 Прошло времени:1069 Ошибка:-6.9%
Квант времени:1000 Прошло времени:1044 Ошибка:-4.4%
Квант времени:1000 Прошло времени:1047 Ошибка:-4.7%
Квант времени:1000 Прошло времени:1044 Ошибка:-4.4%
Квант времени:1000 Прошло времени:1044 Ошибка:-4.4%
Квант времени:1000 Прошло времени:1047 Ошибка:-4.7%
Квант времени:1000 Прошло времени:1044 Ошибка:-4.4%
Квант времени:1000 Прошло времени:1047 Ошибка:-4.7%
Хехе, теперь ошибка уже составляет 4.5%. Продолжим наши эксперименты. Для кванта времени в 500 миллисекунд:
Квант времени:400 Прошло времени:409 Ошибка:-2.25%
Квант времени:400 Прошло времени:429 Ошибка:-7.25%
Квант времени:400 Прошло времени:475 Ошибка:-18.75%
Квант времени:400 Прошло времени:423 Ошибка:-5.75%
Квант времени:400 Прошло времени:461 Ошибка:-15.25%
Квант времени:400 Прошло времени:429 Ошибка:-7.25%
Квант времени:400 Прошло времени:429 Ошибка:-7.25%
Квант времени:400 Прошло времени:432 Ошибка:-8%
Квант времени:400 Прошло времени:428 Ошибка:-7%
Квант времени:400 Прошло времени:429 Ошибка:-7.25%
Квант времени:400 Прошло времени:428 Ошибка:-7%
Квант времени:400 Прошло времени:429 Ошибка:-7.25%
Еее, ошибка около 8%! Продолжаем
квант времени в 10 миллисекунд:
Квант времени:10 Прошло времени:61 Ошибка:-510%
Квант времени:10 Прошло времени:24 Ошибка:-140%
Квант времени:10 Прошло времени:10 Ошибка:0%
Квант времени:10 Прошло времени:16 Ошибка:-60%
Квант времени:10 Прошло времени:19 Ошибка:-90%
Квант времени:10 Прошло времени:22 Ошибка:-120%
Квант времени:10 Прошло времени:13 Ошибка:-30%
Квант времени:10 Прошло времени:14 Ошибка:-40%
Квант времени:10 Прошло времени:15 Ошибка:-50%
Квант времени:10 Прошло времени:19 Ошибка:-90%
Квант времени:10 Прошло времени:14 Ошибка:-40%
Таймер становится весьма не стабильным то ошибка 30% то и все 120%. На этом этапе уже невозможно говорить о какой либо точности измерения времени. Ну и конечно мы должны довести наш эксперемент до конца и узнать всю правду о нашем поциенте. ИТААААКК…. 1 миллисекунда! Поехали:
Квант времени:1 Прошло времени:13 Ошибка:-1200%
Квант времени:1 Прошло времени:5 Ошибка:-400%
Квант времени:1 Прошло времени:5 Ошибка:-400%
Квант времени:1 Прошло времени:6 Ошибка:-500%
Квант времени:1 Прошло времени:6 Ошибка:-500%
Квант времени:1 Прошло времени:13 Ошибка:-1200%
Квант времени:1 Прошло времени:4 Ошибка:-300%
Квант времени:1 Прошло времени:6 Ошибка:-500%
Квант времени:1 Прошло времени:5 Ошибка:-400%
Квант времени:1 Прошло времени:6 Ошибка:-500%
Квант времени:1 Прошло времени:36 Ошибка:-3500%
Таймер просто колбасит, видимо предел его точности – чтото около 35-45 миллисекунд. Ну и да ладно, скажете вы. Точность вполне сгодится…. Чтож давайте поставим снова квант времени 1 секунда и добавим слегка вычислений в код нашего таймера:
//Функция вызывается каждый квант времени function TimerElapsed(event:TimerEvent):void { //Cчитаем сколько времени прошло по системному таймеру var diffTime:Number=getTimer() - startTime; //Cчитаем ошибку в процентах var errPercent:Number=(timeQuant-diffTime)*100/timeQuant; //Выводим в консоль данные trace("Квант времени:" + timeQuant + " Прошло времени:" + diffTime + " Ошибка:" + errPercent + "%"); //Запоминаем время начала нового кванта времени startTime = getTimer(); var i:uint; var j:uint; var k:Number=0; for(i=0;i<1000;i++) for(j=0;j<1000;j++) { k=k+Math.sin(k*Math.random()); } }
В предыдущий раз ошибка была около 4.5%, посмотрим сколько сейчас выйдет:
Квант времени:1000 Прошло времени:1350 Ошибка:-35%
Квант времени:1000 Прошло времени:1349 Ошибка:-34.9%
Квант времени:1000 Прошло времени:1357 Ошибка:-35.7%
Квант времени:1000 Прошло времени:1357 Ошибка:-35.7%
Квант времени:1000 Прошло времени:1360 Ошибка:-36%
Квант времени:1000 Прошло времени:1355 Ошибка:-35.5%
Ну не фига, почти 36%! Тихий ужас. Оказывается таймер не только неточен на целые 45 миллисекунд, его точность ещё и зависит от объёма вычислений находящихся в обработчике события.
Как это не печально но факт. Мораль сей басни очень проста –необходимо замерять интервалы времени только с помошью getTimer(). Только так мы можем быть уверенны, что вне зависимости от производительности компьютера игрока, наши объекты будут двигаться с одинаковой скоростью на всех компьютерах.
На этом всё, Адью!
Если кто хочет сам погонять на своей машине пример, то его можно качнуть здесь: http://www.anegmetex.com/devblog/files/TimerTest.rar

Март 21st, 2010 at 14:49
ну хорошо, все ясно, таймер – shit.
как будет реализована замена таймера через getTimer()?
Март 21st, 2010 at 21:26
Полноценной замены мы сделать не можем. Для точного вызова нужного куска кода через заданный промежуток времени необходима многопоточность (чтобы не зависить от других вычислений), которой во флеше к сожалению нет. Однако получить информацию насколько миллисекунд произошла ошибка – можем. И исходя из этого можем скорректировать допустим движение объекта.
Например стоит организовывать движения объекта вот таким образом:
var diffTime:Number=getTimer() – startTime;
myMovieClip.x+=vx*diffTime; //где vx – скорость движения объекта в пиксель\мс
startTime = getTimer();
И не стоит писать вот так:
myMovieClip.x+=5;
Думаю во многих других случаях мы тоже можем избавиться от стандартного таймера.
Апрель 16th, 2010 at 17:46
вызов таймера синхронизирован с framerate. Соответственно:
- таймер не может быть быстрее чем 1/framerate msec
- если он попал в “междуфрейменье” то ждет начала следующего фрейма.
В AS3 эксперименты не проводил, но в старой виртуальной машине флеша было так. Да и в концепцию вписывается.
Апрель 19th, 2010 at 20:56
ПОЦиент – goood! )))))
Апрель 22nd, 2010 at 20:20
Игорь прав: таймер не может быть быстрее чем 1/framerate, у вас скорее всего стандартные 30fps, следовательно для отрезков меньше 33мс его нет смысла использовать
Апрель 22nd, 2010 at 23:51
Да, похоже на то )
Декабрь 8th, 2010 at 7:34
таймер не может быть быстрее чем 1/framerate
Так и в чем тогда мнимое преимущество перед ентерфреймом?
Получается что таймер тоже по ентерфрейму запускается)))
Декабрь 10th, 2010 at 2:31
Впринципе в играх лучше вообще таймер не использовать, а всё держать в одном ENTER_FRAME. Паузу делать удобнее опять же
Март 1st, 2011 at 21:28
для этого дела есть
stage.frameRate
устоновите его хотяб 1000
и посмотрите что будет=)
Август 24th, 2011 at 20:41
из Адоб хелпа :
Примечание. Не рекомендуется задавать свойству delay значение меньше 20 миллисекунд. Частота объекта Timer ограничена 60 кадрами в секунду, то есть задержка меньше 16,6 миллисекунд вызывает проблемы во время выполнения.
тесты после < 500 не объективны.