Simple Physics: Fun with Verlet Integration

In any fun game, things move around the screen. Space ship, rain, whales and flower pots falling from the sky all need to move in order for the scene to be interesting.

For our test case – rewriting the classic game, Asteroids – our needs are simple. We’d like to have a ship that can accelerate and decelerate, and rotate on its own axis. We also need asteroids that can move along a potentially random vector at a constant speed, and react to outside forces – like a missile hitting an asteroid – in a visually convincing way.

There are few ways to accomplish this. Two common methods are Euler integration, and Verlet integration, detailed below.

Euler Integration

(pronounced “oy ler”)

  • Perhaps the most physically accurate method
  • Uses familiar calculation methods from physics and calculus.
  • Slightly more computationally expensive than Verlet
  • Accuracy lessens when time step is inconsistent (frame rate drops, etc.)

In this model, we need to keep track of a few different pieces of information:

  • Acceleration (meters per second per second)
  • Velocity (meters per second)
  • Position (meters, probably from the origin)

Recall that velocity is the first derivative of position, and acceleration is the first derivative  of velocity, we can see that, for every frame, we must:

  1. Calculate or retrieve dt, the amount of time that has elapsed since the last frame (typically on the order of 16.6ms or 33.3ms, depending on current frame rate)
  2. Multiply total acceleration by dt
  3. Add this value to velocity
  4. multiply velocity by dt
  5. Add this value to position

Then, repeat for the angular components.

To start movement, modify acceleration or velocity and the change will take effect at the next frame.

Some pseudocode:

[code language=”cpp”]
/*
Given:
Acceleration : meters per (second squared)
Velocity : meters per second
Position : meters
*/

dV = acceleration * elapsedSeconds; /// meters per second
velocity = velocity + dV;

dX = velocity * elapsedSeconds; /// meters
position = position + dX;
[/code]

While this isn’t too complex or computationally intensive, it’s annoying to have to keep up with acceleration and velocity in addition to position for every object in our scene.

Fortunately, we have a much simpler option available: Verlet integration.

Verlet Integration

My personal favorite! Verlet integration is simpler and easier to implement than Euler integration, and gives visually convincing results that are, for the most part, identical to those obtained through Euler.

Things we need to keep track of for each object:

  • Position
  • Previous position (from the prior frame)

(For translation-only, that’s all we need. Really!)

For each frame,

  1. Find Velocity = (current position) – (previous position).
  2. Calculate or retrieve dt, the amount of time that has elapsed since the last frame (typically on the order of 16.6μs or 33.3μs, depending on current frame rate)
  3. Set previous position = current position
  4. Set current position = Velocity + (all_forces * dt)

To apply a force, add a value to the current position. This has the effect of changing the velocity (which is the change in position between frames).

Repeat for rotation.

Note that, unlike Euler

Some pseudocode:

[code language=”cpp”]
/*
Given:
currentTranslation : meters
previousTranslation: meters
previousFrameTime : seconds
*/

/// compensate for changing frame rate
timeScale = elapsedSeconds / previousFrameTime;
previousFrameTime = elapsedSeconds;

velocity = (currentTranslation – previousTranslation) * timeScale;

previousTranslation = currentTranslation;
currentTranslation += velocity;
[/code]

To start moving, add something to the current translation or rotation. This will be reflected in the next frame when velocity and angular velocity are calculated.

[code language=”cpp”]
currentTranslation += force;
currentRotation += spin;
[/code]

I find this a much simpler approach than Euler: we do not need to track acceleration or velocity. Those values are implicit in the change in position between frames.