Цикл, основанный на времени, и цикл на основе кадра

Попытка понять концепции установки постоянной скорости в игровом цикле. Моя голова болит. Я прочитал страницу deWiTTERS, но я не вижу, почему/как... когда я ее получаю... она проскальзывает.

while(true)
{
      player->update() ;
      player->draw() ;
}

Это будет работать как можно быстрее в зависимости от того, насколько быстро процессор... Я получаю это.

Чтобы работать с одинаковой скоростью на всех компьютерах, логика - это то, чего я не получаю. Если я пытаюсь работать со скоростью 60 кадров в секунду, то это означает, что каждые 16 мс объекты перемещаются кадром, да? Я не понимаю, как update() или draw() могут быть слишком медленными.

Пример

deWiTTERS (я использовал 60):

const int FRAMES_PER_SECOND = 60;
const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;

DWORD next_game_tick = GetTickCount();
// GetTickCount() returns the current number of milliseconds
// that have elapsed since the system was started

int sleep_time = 0;

bool game_is_running = true;

while( game_is_running ) {
    update_game();
    display_game();

    next_game_tick += SKIP_TICKS;
    sleep_time = next_game_tick - GetTickCount();
    if( sleep_time >= 0 ) {
        Sleep( sleep_time );
    }
    else {
        // Shit, we are running behind!
    }
}

Я не понимаю, почему он получает текущее время до начала цикла. И когда он увеличивается на SKIP_TICKS, я понимаю, что он увеличивается до следующего 16 мс интервала. Но я тоже не понимаю эту часть:

sleep_time = nextgametick  - GetTickCount() 

Что означает Sleep(sleep_time)? Процессор покидает цикл и делает что-то еще? Как он достигает 60 кадров в секунду?

Ответ 1

В тех случаях, когда функции update_game() и display_game() выполняются за меньшее время, чем один интервал кадра в 60FP, цикл пытается обеспечить, чтобы следующий кадр не обрабатывался до тех пор, пока этот интервал не достигнут, спящий (блокирующий поток) с избыточным временем кадра. Похоже, он пытается обеспечить, чтобы частота кадров была ограничена до 60FPS и не выше.

Процессор не "покидает цикл", а поток, в котором работает ваш цикл, блокируется (предотвращается от продолжения выполнения вашего кода) до тех пор, пока время ожидания не закончится. Затем он продолжается до следующего кадра. В многопоточном игровом движке, спящий поток основного игрового цикла, как это дает процессорное время для выполнения кода в других потоках, которые могут управлять физикой, AI, микшированием звука и т.д., В зависимости от настройки.

Почему GetTickCount() вызывается до начала цикла? Мы знаем из комментария в вашем коде, что GetTickCount() возвращает миллисекунды с момента загрузки системы.

Так что скажем, что система запускалась в течение 30 секунд (30 000 мс) при запуске вашей программы, и скажем, что мы не вызывали GetTickCount() перед входом в цикл, но вместо этого инициализируется next_game_tick на 0.

Мы делаем обновления и набираем вызовы (например, они берут 6 мс), а затем:

next_game_tick += SKIP_TICKS;  // next_game_tick is now 16
sleep_time = next_game_tick - GetTickCount();   

// GetTickCount() returns 30000!
// So sleep_time is now 16 - 30000 = -29984 !!!

Поскольку мы (разумно) спать только когда sleep_time положителен, игровой цикл будет работать как можно быстрее (потенциально быстрее, чем 60FPS), который не является тем, что вы хотите.