Я создал свой собственный игровой цикл на C ++ и SDL на основе этих статей:
Шаблоны игрового программирования: http://gameprogrammingpatterns.com/game-loop.html
Исправьте свой временной шаг !: https://gafferongames.com/post/fix_your_timestep/
Для образовательных предложений я хотел сделать все самостоятельно, например, рассчитать фиксированный временной шаг в зависимости от частоты обновления монитора, рассчитать время, прошедшее для каждого кадра с помощью PerformanceCounter, и выполнить синхронизацию цикла с использованием спящего режима вместо включение VSYNC.
Все обновления физики работают хорошо, и если я измеряю количество кадров в секунду, я получаю надежную частоту кадров в зависимости от значения частоты обновления и фиксированного временного шага.
Проблема в том, что я не знаю, как (и где) делать интерполяционный рендеринг. Я знаю, что мне нужно интерполировать предыдущую позицию с текущей позицией объектов с оставшимся временем, а затем визуализировать ее, но движение моих объектов все еще не плавное.
Другая проблема заключается в том, что, хотя синхронизация игрового цикла равна частоте обновления монитора, у меня все еще есть некоторые эффекты разрыва, которых я не понимаю, даже если я жду правильное количество времени между заменами буфера. Если я активирую VSYNC и прокомментирую часть синхронизации, эффект разрыва исчезает, но мой счетчик кадров не считает 60 кадров в секунду, как ожидалось, поскольку в документе SDL говорится, что с включенным VSYNC SDL_RendererPresent будет синхронизироваться с частотой обновления.
Мой код:
Main.cpp
int main()
{
App game;
game.Init();
game.Run();
return 0;
}
App.cpp
void App::Init()
{
// SDL Init, window and render creation
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS);
window_.CreateCustomWindow("Window", SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED, kWindow_width,
kWindow_height,SDL_WINDOW_FULLSCREEN);
renderer_ = SDL_CreateRenderer(window_.SDLWindow(), -1,
SDL_RENDERER_ACCELERATED /*|
SDL_RENDERER_PRESENTVSYNC*/);
// Player creation with a position, velocity and acceleration
player_ = new Player(Vector2{0.0f,0.0f},
Vector2{600.0f,0.0f},
Vector2{0.0f,0.0f});
//Calculate the fixed time step of the update based on the monitor refresh rate
// refresh_rate = 60Hz
// fixed time step = 1000 / 60 = 16ms
SDL_DisplayMode current_display;
SDL_GetCurrentDisplayMode(0,¤t_display);
time_step_ = 1000.0 / current_display.refresh_rate;
//Get clock frequency
clock_frequency = SDL_GetPerformanceFrequency();
}
double App::GetTime(uint64_t start, uint64_t end)
{
double elapsed_time = ((1000.0 * ((double)end - (double)start)) /
(double)clock_frequency_);
return elapsed_time;
}
void App::Run()
{
uint64_t start = SDL_GetPerformanceCounter();
double lag = 0.0;
double frame_time = 0.0;
uint64_t end = 0;
while(window_.IsWindowRunning())
{
InputProcessing();
while(lag >= time_step_)
{
Update((float)time_step_);
lag -= time_step_;
}
window_.Clear(renderer_);
player_->Draw(renderer_);
window_.SwapBuffer(renderer_);
end = SDL_GetPerformanceCounter();
frame_time = GetTime(start,end);
if(time_step_ > frame_time)
{
SDL_Delay((uint32_t)(time_step_ - frame_time));
frame_time = GetTime(end,SDL_GetPerformanceCounter());
end = SDL_GetPerformanceCounter();
}
double frames = (double)clock_frequency_ / ((double)end - (double)start);
printf("MS: %f - FPS: %f \n",frame_time,frames);
start = end;
lag += frame_time;
}
Shutdown();
}