Step 1 of 5 · Reading · ~4 min
Read
Math & Integration
What Physics Engines Solve
A rigid-body engine answers one question over and over: given where every body is and how fast it moves, where is each one a moment later, and which of them now touch? After this lesson your program will run the heartbeat of that loop: it turns the uneven frame times a game produces into a steady series of fixed physics steps, without losing time and without inventing any.
The pipeline you will build
One engine step is a fixed sequence of stages. Each later lesson in this course builds one of them:
forces -> integrate -> broad phase -> narrow phase -> solve contacts and joints -> sleep idle bodies
- Integrate: forces change velocities, velocities change positions (Newton: F = m a).
- Broad phase: cheaply list the pairs of bodies that might touch.
- Narrow phase: test each candidate pair exactly and describe the contact.
- Solve: change velocities (impulses) and positions so bodies bounce, slide or rest instead of overlapping.
Real engines (Box2D, Bullet, PhysX, Havok, Rapier) have exactly these stages. By the last lessons you will step a small world through all of them.
Why every step has the same length
The step advances the world by dt seconds. If dt changed from frame to frame, three things would break:
- Determinism. The same inputs would give different results on a fast and a slow machine, so replays, tests and lockstep networking all stop working.
- Stability. A spring that behaves at 16 ms can explode at 50 ms, because integration error grows with the step.
- Tunnelling. A body moves further per step when the step is longer, so thin walls get skipped more often.
So physics advances in fixed steps (say 20 ms, i.e. 50 Hz) however fast or slow the renderer runs.
The accumulator
Frames arrive with irregular elapsed times. Keep a bank of time that has passed but has not yet been simulated. Add each frame's elapsed time to the bank, then spend it in coins of exactly dt.
Trace with dt = 25 ms and four frames of 40, 10, 60 and 5 ms:
elapsed bank before steps bank after
40 40 1 15
10 25 1 0
60 60 2 10
5 15 0 15
The leftover is never thrown away. It stays in the bank and is spent when enough time has accumulated. Note the second row: a bank of exactly dt buys a step now.
The render fraction
After spending, the bank holds a value from 0 up to (but not including) dt. Divided by dt it gives a fraction alpha in [0, 1): how far the displayed moment lies between the last two simulated states. A renderer draws each body at previous * (1 - alpha) + current * alpha. Without it, a frame that runs 0 steps followed by one that runs 2 makes motion visibly stutter.
The spiral of death
Suppose one step costs more real time to compute than the dt it simulates. The next frame is then longer, so it needs more steps, so it takes even longer, and the game freezes. The standard fix is to clamp the elapsed time you accept per frame (for example 100 ms). The simulation then falls behind the wall clock, which looks like slow motion instead of a hang.
Common mistakes
- Running at most one step per frame. A 60 ms frame at
dt = 20must run 3 steps, not 1. - Resetting the bank to zero after stepping. Up to
dtof time is lost every frame and the world runs slow. - Testing
bank > dtinstead ofbank >= dt. A bank of exactlydtmust step. - Clamping the bank, or forgetting to clamp at all. The clamp applies to the elapsed time of the frame, before it is banked.
Your exercise
Implement the accumulator. The starter reads the script and prints one line per frame; you write the function that decides how many steps a frame buys and what is left in the bank. All times are whole milliseconds, so the arithmetic is exact. The most likely mistake is losing the leftover time.
Further reading: Glenn Fiedler, "Fix Your Timestep"; Erin Catto's Box2D documentation on the simulation loop.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…