Я хочу, чтобы что-то происходило каждые N секунд, как бы я это сделал? Может быть, использовать таймер, но как?
Я хочу, чтобы что-то происходило каждые N секунд, как бы я это сделал? Может быть, использовать таймер, но как?
Ответы:
Обычный подход для этого заключается в использовании цикла обновления и дельта-времени.
float timerCurrent = 0f;
float timerTotal = 5f;
Update() {
timerCurrent += deltaTime;
if(timerCurrent >= timerTotal) {
//do timer action
timerCurrent -= timerTotal;
}
}
Это предполагает, что у вас есть доступ к deltaTime
переменной. Дельта-время - это просто время, прошедшее с последнего кадра. Многие движки будут иметь это в наличии, или вы можете узнать больше о настройке своего здесь .
В большинстве языков вам нужно запустить часы с помощью какого-либо вызова функции по аналогии с clock.startTimer()
тем, как вы входите в основной цикл. Затем вы должны сохранить значение времени часов перед тем, как ввести основной цикл в переменную с такой функцией time = clock.getTime()
. Сохраните время вашего шага в переменной n
.
В вашем основном цикле проверка, которую вы хотите сделать, является чем-то вроде
if(time + n <= clock.getTime())
//do stuff
time = clock.getTime() //important to assign new time value here
Примечание: я не уверен, на каком языке / библиотеке вы смотрите, так что это всего лишь общий алгоритм, который я придумал не покладая рук. Это может не совсем соответствовать вашим потребностям. Некоторые языки / библиотеки требуют, чтобы вы создали объект часов, в то время как другие вы можете просто сделать вызов функции напрямую.
Согласно комментариям ниже, вы должны быть осторожны с проблемами многопоточности в зависимости от того, какой язык вы используете. Вы также должны использовать двойной поплавок для хранения time
.
time
будет храниться прошедшее игровое время, он должен использовать формат с плавающей запятой двойной точности.
Как уже отмечалось в других ответах, реализация таймера может быть выполнена путем увеличения сохраненного значения на каждый кадр deltaTime
и сравнения его с ожидаемой продолжительностью. Для полноты я добавлю код, который очень похож на другие ответы:
float elapsed = 0.0f;
float duration = 4.0f;
void update(float deltaTime) {
elapsed += deltaTime;
if (elapsed <= duration) {
// Run code.
elapsed = 0.0f;
// Maybe remove from update loop?
}
}
То, как вы хотите справиться с // Run code.
частью, зависит от вас. Возможно, у вас есть Timer
класс, экземпляр которого принимает delegate
(C #) или callback
(C ++) и вызывает его по истечении таймера. Кроме того, приостановка таймера является вопросом удаления его из цикла обновления.
Альтернативный подход заключается в том, чтобы отметить время запуска таймера и вместо этого выполнить вычисление прошедшего времени по требованию:
double startTime = 0.0; // This is a double for a reason, I'll explain below.
bool running = false;
void start() {
startTime = getTime(); // This is whatever system time call you want to use.
running = true;
}
double getElapsed() {
double elapsed = getTime() - startTime;
return elapsed;
}
Этот стиль дает немного больше контроля пользователю таймера. Сама структура не заботится о том, что делать, когда истекшее время достигает определенной точки; это даже не обновление. Пользователь может просто запросить истекшее время, когда это необходимо. Этот паттерн имеет неприятный побочный эффект - он не подходит для отладчика. Системное время продолжается, так как ваша программа останавливается на отладчике, поэтому таймеры, подобные этому, будут вести себя по-разному при переходе между программами. Потенциальным решением этой проблемы является сохранение значения истекшего времени для конкретной программы, которое обновляется каждый кадр с использованием системных дельт времени.
Я думаю, что наиболее важными являются две причуды синхронизации на компьютере, особенно когда речь идет о разработке игр. Первый - это точность с плавающей запятой, а второй - собственное разрешение таймера.
Вы заметите, что во втором примере я использовал double
тип вместо a float
, как и в первом примере (имя типа, конечно, зависит от языка, который вы используете). Причиной этого является второй пример - хранение общего прошедшего игрового времени . Если игра остается запущенной в течение очень долгого времени, значения формата с плавающей запятой одинарной точности будут иметь недостаточную точность для точного измерения истекшего времени , что приведет к проблемам со стабильностью. Первый пример подходит для использования float
типа, если не ожидается большой продолжительности.
На другом конце спектра, если ожидаемое время обновления достаточно мало, вы, возможно, не сможете достичь его изначально, в зависимости от того, как вы получаете системное время. Таймеры часто зависят от разрешения таймера ОС, на которой вы работаете. Например, разрешение таймера по умолчанию в Windows 7 и ниже составляет 15,6 мс . Это означает, что наименьшая точность таймера, которую вы могли бы достичь, используя такие функции, как timeGetTime
или GetTickCount
, составляет 15,6 мс, если вы не измените разрешение таймера ( с этим тоже связаны опасности ).
Что ж, это оказалось немного долго, но это важные вещи, о которых следует помнить при реализации таймеров.