0
\$\begingroup\$

First of all, this is different to this question, because that question doesn't update entities the same way I am.

My update loop goes something like this (for the player):

  • Apply acceleration based on arrow keys input
  • Apply some downwards acceleration for gravity
  • Multiply the acceleration by something like 0.999
  • Add the acceleration to the velocity
  • Multiply the velocity by about 0.6
  • Add the velocity to the player's position
  • Render the player
  • Repeat

So, this is a fairly nice smooth movement, and makes it easy to implement jumping by applying a sharp upwards acceleration in one frame.

The only problem is that it seems to make it very difficult to make the movement speed frame-rate independent. I've tried many different ways, like multiplying the movement acceleration by the delta time when it's applied, multiplying the acceleration by the delta time just before being added to the acceleration, multiplying the velocity by the delta time, etc., but none of these worked. They pretty much all had the same effect of varying movement speed and jump height dependant on the frame-rate.

My question is: with this, or a similarly working, movement system, how can I use delta-time or otherwise to ensure the movement speed, gravity, and similar, is not dependant on the frame-rate.

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Most of what you want can be handled by typical deltaTime multiplication just like the example you link. For example, with a simple Euler integration:

acceleration = GetTotalAccelerationOnBody();

velocity += acceleration * deltaTime;

position += velocity * deltaTime;

The one trick is your damping constants - the * 0.999 and * 0.6. They need a slightly different treatment.

Typically we don't accumulate acceleration from frame to frame - it's computed from scratch based on the forces acting on the body over our current timestep. So you might not need the * 0.999 multiplier at all.

If you do choose to use it, you don't need deltaTime correction on it - all it's doing is scaling the units your forces/accelerations are measured in, not applying a timeslice-sized portion of them. So it can be as simple as

acceleration = 0.999 * GetTotalAccelerationOnBody();

The * 0.6 damping coefficient on velocity is a different matter. Velocity does have memory from frame to frame, so this acts a bit like a friction to slow the body down in the absence of forces. The trick is that this deceleration isn't linear: multiplying by a fraction between 0 and 1 over and over will give you an exponential falloff (eg. 0.5, 0.25, 0.125, 0.0625...)

So we need a different style of time correction for this type of effect, one that preserves the exponential tail shape even when stepped at different increments.

velocity += acceleration * deltaTime;

// eg. referenceDamping = 0.6 
// and referenceFPS = the framerate where your old value seemed right (eg. 30 or 60)
scaledDamping = Pow(referenceDamping, deltaTime * referenceFPS);
velocity *= scaledDamping;

position += velocity * deltaTime;

This way, when deltaTime is close to zero, the multiplier gets close to 1 (diminishing your velocity less over the timestep), and when running close to your reference framerate, you get your reference damping value back out. Running at half your reference framerate gives the square of the damping value (two reference frames' worth of damping compounded), etc.

This will smooth variations in framerate so the game behaves more consistently, but because our math has only finite precision and longer timesteps introduce more integration error, you still won't get exactly reproducible movement at different framerates.

In particular, even with infinite-precision numbers, we've only correctly calculated the change in velocity by the end of the time step. Our formula for computing position doesn't take into account the fact that the velocity was changing continuously over the course of that time step. Accounting for all this continuous change is not particularly practical, so we typically accept this integration error as a consequence of our discrete simulation, and a worthwhile trade for simplicity and performance.

To truly get framerate independent game behaviour, you need to fix your timestep. More on the motivation for this here)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .