If you're not familiar with the Gaffer on Games article "Fix your Timestep", you can find it here: https://gafferongames.com/post/fix_your_timestep/
I'm building a game engine, and in an effort to get more comfortable with std::chrono I've been trying to implement a fixed time step using std::chrono for.. a couple of days now and I can't seem to wrap my head around it. Here is the pseudo-code I'm working towards:
double t = 0.0;
double dt = 0.01;
double currentTime = hires_time_in_seconds();
double accumulator = 0.0;
State previous;
State current;
while ( !quit )
{
double newTime = time();
double frameTime = newTime - currentTime;
if ( frameTime > 0.25 )
frameTime = 0.25;
currentTime = newTime;
accumulator += frameTime;
while ( accumulator >= dt )
{
previousState = currentState;
integrate( currentState, t, dt );
t += dt;
accumulator -= dt;
}
const double alpha = accumulator / dt;
State state = currentState * alpha +
previousState * ( 1.0 - alpha );
render( state );
}
Goals:
- I don't want rendering to be frame-rate bound. I should render in the busy loop
- I want a fully fixed time-step where I call my update function with a
float
delta time - No sleeps
My current attempt (semi-fixed):
#include <algorithm>
#include <chrono>
#include <SDL.h>
namespace {
using frame_period = std::chrono::duration<long long, std::ratio<1, 60>>;
const float s_desiredFrameRate = 60.0f;
const float s_msPerSecond = 1000;
const float s_desiredFrameTime = s_msPerSecond / s_desiredFrameRate;
const int s_maxUpdateSteps = 6;
const float s_maxDeltaTime = 1.0f;
}
auto framePrev = std::chrono::high_resolution_clock::now();
auto frameCurrent = framePrev;
auto frameDiff = frameCurrent - framePrev;
float previousTicks = SDL_GetTicks();
while (m_mainWindow->IsOpen())
{
float newTicks = SDL_GetTicks();
float frameTime = newTicks - previousTicks;
previousTicks = newTicks;
// 32 ms in a frame would cause this to be .5, 16ms would be 1.0
float totalDeltaTime = frameTime / s_desiredFrameTime;
// Don't execute anything below
while (frameDiff < frame_period{ 1 })
{
frameCurrent = std::chrono::high_resolution_clock::now();
frameDiff = frameCurrent - framePrev;
}
using hr_duration = std::chrono::high_resolution_clock::duration;
framePrev = std::chrono::time_point_cast<hr_duration>(framePrev + frame_period{ 1 });
frameDiff = frameCurrent - framePrev;
// Time step
int i = 0;
while (totalDeltaTime > 0.0f && i < s_maxUpdateSteps)
{
float deltaTime = std::min(totalDeltaTime, s_maxDeltaTime);
m_gameController->Update(deltaTime);
totalDeltaTime -= deltaTime;
i++;
}
// ProcessCallbackQueue();
// ProcessSDLEvents();
// m_renderEngine->Render();
}
Problems with this implementation
- Rendering, handling input, etc is tied to the frame rate
- I'm using SDL_GetTicks() instead of std::chrono
My actual question
- How can I replace
SDL_GetTicks()
withstd::chrono::high_resolution_clock::now()
? It seems like no matter what I need to usecount()
but I read from Howard Hinnant himself this quote:
If you use count(), and/or you have conversion factors in your chrono code, then you're trying too hard. So I thought maybe there was a more intuitive way.
- How can I replace all the
float
s with actual std::chrono_literal time values except for the end where I get the float deltaTime to pass into the update function as a modifier for the simulation?
Aucun commentaire:
Enregistrer un commentaire