Таймеры различных типов используются во всех играх. Они являются важной частью игровых циклов. Таймеры используются для периодической перерисовки экрана, обновления позиций спрайтов, а также участвуют в обработке многих других игровых событий.
Разрешающая способность таймеров в Symbian OS не велика, но ее вполне достаточно для типичных целей - таймеры ядра обеспечивают точность 1/64 секунды на устройстве и 1/10 на эмуляторе (Чтобы узнать фактическое значение, используйте UserHal::TickPeriod). Данный интервал округляется вверх до самого близкого значения разрешающей способности частоты сигнала системного времени.
Ниже приведенывиды таймеров, применяемых в Symbian OS:
Простой таймер, реализуемый RTimer, обеспечивает генерацию событий после заданного промежутка времени или в определенное время. RTimer обеспечивает самый низкий уровень доступа к системному таймеру. Этот таймер можно использовать, когда движок игры пишется с использованием Active Objects.
Периодический таймер (CPeriodic) генерирует события периодически через определенный промежуток времени. Сообщение передается приложению через обратный вызов (TCallBack), который задается при создании таймера. Именно этот таймер наиболее широко используется в играх. Он очень прост, и, как правило, для игры хватает одного таймера.
Таймер биения (CHeartBeat) подобен периодическому, но имеет некоторые дополнительные возможности. Он использует обратный вызов для информирования о пропуске события таймера (то есть о том, что таймер не обработан во время). CPeriodic неточен. Он задерживает события пока приложение не будет готово его обработать.
Дополнительные возможности таймера биения (информирование о пропущенных событиях MBeating::Synchronize) редко используются, поскольку игры, как правило, не требуют очень точной синхронизации. Обычно нужно знать только время, прошедшее с предыдущего вызова. Точное время необходимо, например, чтобы рассчитать новое положение спрайта, основываясь на его предыдущем положении и скорости движения. Небольшая погрешность таймера не оказывает существенного влияния на результат.
Как уже было сказано, возможностей периодического таймера CPeriodic вполне достаточно для игры. В следующем показано использование одного таймера для вычисления интервалов времени и управление состоянием игры.
// запуск таймера void CMyGameView::StartTimerL() { // интервал в миллисекундах; 100000 соответствует 1/10 секунды const TInt KTickInterval=100000; iPeriodic= CPeriodic::NewL(CActive::EPriorityLow); iPeriodic->Start(KTickInterval, KTickInterval,TCallBack(Tick, this)); } // Останавливаем таймер void CMyGameView::StopTimer() { iPeriodic->Cancel(); delete iPeriodic; iPeriodic=NULL; } // Вызывается таймером по прошествии заданного интервала. // Этот метод должен быть статическим TInt CMyGameView::Tick(TAny* aObject) { // вызываем не статический метод ((CMyGameEngine*)aObject)->NextTick(); // позволяем таймеру продолжить его работу return1; } // нестатическое событие таймера, вызываемое статическим Tick() void CMyGameView::NextTick() { TTime currentTime; currentTime.HomeTime(); TInt64 currentTick= currentTime.Int64(); // Вычисляем время, прошедшее с послдеднего вызова в миллисекундах // iLastTick текущее время с момента предыдущего срабатываения таймера TInt64 frameTime= currentTick- iLastTick; iLastTick= currentTick; // Обновляем экран согласно frameTime switch(iGameState) { case EPlayingTheGame: // Вычисляем положение спрайта и перерисовываем экран HandleGameTick(frameTime); break; case EWatchingTheIntro: // Заставка к игре может обрабатываться этим же таймером HandleIntroTick(frameTime); break; // Здесь же могут обрабатываться любые другие состояния игры } }
Использования нескольких таймеров обычно не требуется. Это делает структуру игры не такой наглядной, и может вызвать разные неприятные эффекты, особенно если несколько таймеров используются в одном игровом состоянии. Поскольку таймеры используют Active Object, на Active Scheduler ложиться дополнительная нагрузка, кроме того расходуются системные ресурсы.
Приоритет таймера и загруженность Active Scheduler сказывается на точности таймера. Вы не должны перегружать задачами планировщика. События таймера должны обрабатываться как можно быстрее. Таймер должен иметь низкий приоритет, а интервал таймера должен быть по возможности велик. Эти предосторожности позволят избежать ошибок, когда планировщик не справляется с входящими событиями и приложение начинает тормозить.
Перевод:aRix.