You will want to separate the game speed from frame rate.
Check out this article:
http://gafferongames.com/game-physics/fix-your-timestep/
I'll sum up parts that apply to your problem.
We face 2 issues:
Frame rates may fluctuate and vary across different hardware, so you
want to keep game speed constant and independent of frame/rendering
rate.
It is also important to keep each time step you advance your physics
to be a constant because that keeps the physics stable due the way most physics engines are designed (such as Box2D which Angry Bird uses); they use something called an integrator to do the math
To solve these problems, as the article states, we want our loop to be structure so that "the renderer produces time and the physics simulation consumes it in
discrete dt sized chunks."
Each loop we update physics according to how much time is given, then render the final state. See code from article (I simplified it a bit and commented):
const double dt = 0.01;
double currentTime = current_time_in_seconds();
double accumulator = 0.0;
while ( !quit )
{
double newTime = current_time_in_seconds();
double frameTime = newTime - currentTime; //total time available this frame,
//or the amount of time passed since
//last iteration of main loop
currentTime = newTime;
accumulator += frameTime; //accumulator keeps track of how much time left
while ( accumulator >= dt ) //consume available time in fix-sized chunks (dt)
//Why fixed? for stable physics
{
integrate( &state, dt ); //step forward physics/game engine by time dt
accumulator -= dt; //we used up dt amount of time, update accumulator
}
//NOTE: there might still be some time left in accumulator, see explanation below
render(state); //Render current state of the game given by physics/game engine
}
Because we consume time in fix-sized chunks (dt), there might be say half a (dt) amount of time left in accumulator
. In above code this may cause physics to fall behind for a few iterations until accumulator sums up to at least 1 (dt). This may cause stuttering as we render state of the game advanced by different time steps each iteration. Therefore, we smooth this out by linearly interpolating between current and previous physical states; I'll explain more below the code:
const double dt = 0.01;
double currentTime = current_time_in_seconds();
double accumulator = 0.0;
State currentState;
State nextState;
while ( !quit )
{
double newTime = current_time_in_seconds();
double frameTime = newTime - currentTime;
if ( frameTime > 0.25 )
frameTime = 0.25; // note: max frame time to avoid spiral of death
currentTime = newTime;
accumulator += frameTime;
while ( accumulator >= dt )
{
currentState= nextState;
integrate( nextState, dt );
accumulator -= dt;
}
const double alpha = accumulator / dt;
State state = nextState*alpha + currentState * ( 1.0 - alpha );
render( state );
}
I saw a lot of confusion about the above code from article's comments about the interpolation so I changed the variable names to make it more understandable.
I'll explain it in my way here:
When accumulator
= 0, alpha
= 0, state
= currentState
. We
simply render currentState
.
When accumulator
= 1/3*dt, this means we need to render a state that is
currentState plus 1/3*dt
ahead in time. To do this, we actually
make our physics engine advance 1 dt ahead of rendering to produce
nextState
and linearly interpolate between currentState
and
nextState
to get currentState plus 1/3*dt
.
In this case, our physics engine is actually running 1 time step(dt) ahead of rendering for the sake of keeping game speed and rendering consistent.
Any questions about above explanation, please do ask.
Note: I take no credit for above approaches; they all from the article mentioned.