Basic

The game loop and time that is not frames

The loop has to run at whatever rate the machine allows, and the game has to move the same distance per second on any machine. That means the update takes the elapsed time as an argument, and no rule multiplies by a per-frame constant.

What you will see

0.05, not 2. A two-second gap must not become two seconds of movement.

About 1 min of reading

Try It

Write a time-based loop

1

Measure the gap

printf 'let prev = Date.now(); function frame() { const now = Date.now(); const dt = Math.min((now - prev) / 1000, 0.05); prev = now; return dt; } ' | tee game/loop.js

Before you run it, say out loud what it will print. Then open the answer.

What it prints
dt in seconds, capped at 0.05. The cap is what stops a teleport.
2

Move by seconds

printf 'function step(w, dt) { w.player.x += w.player.vx * dt; } ' | tee -a game/loop.js

Before you run it, say out loud what it will print. Then open the answer.

What it prints
The speed is per second. Nothing in the file multiplies by a bare per-frame constant.
3

Check the same distance per second

node -e 'const m=require("./game/loop.js"); let w={player:{x:0,vx:100}}; m.step(w,1); console.log(w.player.x)' 2>/dev/null || echo 'vx 100 per second must give x=100 after one second'

Before you run it, say out loud what it will print. Then open the answer.

What it prints
100 after one second of dt. At dt 0.01 sixty times it must also be 100.
4

Test the cap

call frame twice with a two-second stall between and print dt

Before you run it, say out loud what it will print. Then open the answer.

What it prints
0.05, not 2. A two-second gap must not become two seconds of movement.
Do not use a per-frame constant for speed. On a twice-as-fast machine the game moves twice as far.

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

Clock to a bounded update
1 Read the clock
↓
2 Subtract the previous timestamp
↓
3 Cap the gap
↓
4 Update with the seconds elapsed
Why this works
The order inside the loop

Measure, update with the measured time, draw. Measuring after the update means the wrong number is used.

When fixed steps are better

When the rules must be identical across machines, run fixed steps and accumulate the leftover time. That costs a loop and buys determinism.

Examples

Measure the gap

Take the timestamp at the start of the frame and subtract the previous one. That number is the frame length, and it varies.

Move by time, not by frame

Distance is speed times seconds. A speed times frames constant is a speed times machine, which is the bug.

Cap the gap

A long gap after a stall would teleport everything. Cap the step so a stall becomes a small step and a hitch, not a jump.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← A state machine for anything with modes
Previous Lesson
A state machine for anything with modes
Next Lesson
Input that does not repeat or drop
Input that does not repeat or drop →