Conserving Energy in Physics Simulations
08-21-02
This is kind of a bone-headed trick, but it helps a lot and
it's very simple.
Lots of gameplay is physics, whether the coder thinks of it
that way or not. At some point you will start running into
issues like sampling problems, and the fact that your integrator
isn't very good, and that all causes weird glitchy behavior.
Let's consider a simple case - a ballistic object moving in
gravity. We'll work in one dimension - just "z" (vertical).
Your physics equations are :
z' = v
v' = - g
where ' means rate of change with respect to time (read "prime").
You can solve this exactly; the solution is :
z(t) = z(0) + t * v(0) - 0.5 * g * t^2
But in games you normally wouldn't do that, because there may be
other forces acting on your object which mean you can't just
use an exact solution. So what if I just used a simple Euler
integrator? The Euler integrator advances timesteps like this :
z(t+dt) = z(t) + v(t) * dt
v(t+dt) = v(t) - g * dt
That's ok if you do lots of steps with small dt, but if your time
step dt is ever large, the solution for z(t+dt) is very far off
what it should be.
Part of the problem is that we're violating conservation of energy
in each time step. We can easily fix this, though. The energy
of the system at any time is :
E = 0.5 * m * v^2 + m * g * z
This is just the sum of the kinetic energy and the gravitational
potential energy. So, we can easily figure out our initial energy :
E(0) = 0.5 * m * v(0)^2 + m * g * z(0)
This is equal to the energy at all times, so we can write
E(t) = E(0)
and solve for v(t) :
v(t) = sqrt{ v(0)^2 + 2 * g* ( z(0) - z(t) ) }
So, we can do a new integrator by incorporating this :
z(t+dt) = z(t) + v(t) * dt
v(t+dt) = sqrt{ v(t)^2 + 2 * g * v(t) * dt }
This is a simple Euler integration of z(t) , and then an exact
fix-up for v(t). If you're a physics wiz, what we're doing here
is keeping our particle on the correct curve in phase space. Phase
space is the {z,v} plane, and all particles should move on curves
in phase space which conserve energy. So, with this integrator,
we may not move the right *amount* on a curve, but we always stay
on the right curve.
If you preferred, you could Euler integrate "v" and exactly fix "z",
but in practice it's very useful for "z" to change only once, and
to change linearly, so that you can do collision detection with a
linear sweep.