The Canonical Game Loop, Fixed Timesteps, and State Interpolation
At the core of every interactive game engine (Unreal, Unity, Godot, custom C++) lies the Game Loop. Running physics calculations with a variable delta time ($\Delta t$) introduces non-deterministic physics glitches, tunneling, and frame-rate dependent mechanics. This guide covers the "Fix Your Timestep" algorithm, accumulator loops, and rendering interpolation.
⚡ Quick Dive
Timestep Models Comparison
| Model | Physics Behavior | Frame Rate Dependency | Glitch / Tunneling Risk |
|---|---|---|---|
Variable Timestep (dt = frame_time) |
❌ Non-deterministic | 💥 Speed changes with FPS drops | ⚠️ High (Fast objects tunnel through walls) |
| Fixed Timestep (Lockstep) | 🔒 Deterministic | Slows down simulation on lag | Low |
| Semi-Fixed Accumulator Loop | 🔒 100% Deterministic Physics | ⚡ Smooth rendering at any Hz | 🔒 Zero (Standard production engine model) |
The Canonical Accumulator Game Loop (C++)
double t = 0.0;
const double dt = 1.0 / 60.0; // Fixed 60Hz physics tick (16.66ms)
double currentTime = getCurrentTime();
double accumulator = 0.0;
while (!quit) {
double newTime = getCurrentTime();
double frameTime = newTime - currentTime;
if (frameTime > 0.25) frameTime = 0.25; // Prevent "Spiral of Death"
currentTime = newTime;
accumulator += frameTime;
while (accumulator >= dt) {
previousState = currentState;
integrate(currentState, t, dt); // Fixed physics step
t += dt;
accumulator -= dt;
}
// Alpha interpolation factor between physics states [0.0, 1.0]
const double alpha = accumulator / dt;
State renderState = interpolate(previousState, currentState, alpha);
render(renderState);
}
📖 Extended Guide
1. State Interpolation for High-Refresh Displays (144Hz+)
When the monitor refresh rate (e.g. 144 FPS / 6.94ms) does not match the physics update rate (e.g. 60 FPS / 16.66ms), rendering the raw latest physics state produces visual stutter (micro-jitter).
$$\text{RenderPosition} = \text{Position}{\text{prev}} \times (1 - \alpha) + \text{Position}{\text{curr}} \times \alpha \quad \text{where } \alpha = \frac{\text{accumulator}}{\Delta t}$$