Вот как я справляюсь с игровым циклом:
while (running) {
diff = duration_cast<milliseconds>(end - start).count();
start = clock::now();
dt = diff / (16.0);
handleInput(); // get input
update(dt); // game logic
render(); // render game
SDL_GL_SwapWindow(window); // swap frame buffer
end = clock::now();
}
Предполагается, что это игра с фиксированным временным шагом, рассчитанная на 60FPS (это переработанная эмуляция игры SNES), однако она работает на 144 тактах на моем экране 144 Гц, что делает ее слишком быстрой. Vsync не может решить эту проблему, так что же можно?
Вот быстрый пример того, как игровой цикл может быть реализован:
int32_t tickInteval = 1000/FPS; // frequency in Hz to period in ms
uint32_t lastUpdateTime = 0;
int32_t deltaTime = 0;
while (running) { // running condition
uint32_t currentTime = SDL_GetTicks();
deltaTime = currentTime - lastUpdateTime;
int32_t timeToSleep = tickInteval - deltaTime;
if(timeToSleep > 0)
{
SDL_Delay(timeToSleep); // energy saving
}
update(deltaTime); // game logic
lastUpdateTime = currentTime;
}
Я бы рекомендовал присмотреться к этой теме.
UPD.
Кто-то может быть обеспокоен uint32_t
переполнение. И да, это переполнится. После почти двух месяцев непрерывного запуска игры (точнее 49,7 дня). Что будет потом? currentTime
будет очень маленькое положительное целое число, lastUpdateTime
будет очень большое положительное целое число. Но вычитание двух не будет переполнено, несмотря ни на что. Более того, если разница не вписывается в int32_t
это будет обернуто вокруг модуля UINT_MAX + 1
в результате получается небольшое положительное целое число, которое будет точным числом тиков, эти два значения различны (в отношении беззнакового переполнения единицы).
Других решений пока нет …