Game development, from the loop outward
Build the engine parts you would otherwise take on faith.
A small game written from first principles in C++, starting at the simulation loop and working outward through world storage, rendering, audio and procedural generation. It is for programmers who can already ship something in an engine and want to know what the engine is doing underneath. The material comes out of ABOARD, a procedural ship simulation, so the worked examples are the ones that survived contact with a real project.
Level: Intermediate · Hours: 10 · Price: £69
What you build: You build a small ship simulation: a fixed-step world with a generated deck plan, an interpolated renderer, spatial audio in each compartment, and a generator that is tested across thousands of seeds.
What you will be able to do
- Write a fixed-timestep loop with interpolated rendering that behaves the same at 30 and 240 frames per second
- Store a world in flat arrays and explain the cache behaviour that makes it faster than an object graph
- Get geometry to the GPU in a shape it wants, with a draw call budget you can defend
- Place sounds in space and keep the audio callback inside its deadline
- Generate a world from declared rules, seed it reproducibly, and test the generator across thousands of runs
What you need first
- Comfortable C++: classes, templates, references, and a build you can run
- Some linear algebra: vectors, dot and cross products, and what a 4x4 matrix does
- You have shipped or nearly shipped something small in an engine, so you know what you are replacing
The syllabus
- The loop and time: The loop, and why your first one is wrong. You can write a fixed-timestep loop with an accumulator, render interpolated state between ticks, and cap the frame time so a slow frame cannot cascade. (free to read)
- The loop and time: Clocks, drift and a time source you can trust. You can choose between monotonic and wall clock time, measure your own frame cost honestly, and explain why the display refresh rate is not the tick rate.
- The loop and time: Input latency, and where the frames go. You can trace a button press from the device to the lit pixel, count the frames it waited at each stage, and remove the ones you put there yourself.
- How the world is stored: Entities and components, and the end of the object graph. You can represent a world as identifiers plus component arrays and say what that buys you over a class hierarchy of game objects.
- How the world is stored: Data layout: arrays, cache lines and the profile you did not expect. You can lay out component storage so a hot loop reads contiguous memory, and use a profiler to show the cache misses before and after.
- How the world is stored: Systems, ordering and a world you can save. You can order systems within a tick so dependencies are explicit, and serialise the whole world to a buffer that reloads into an identical state.
- Rendering: What the GPU wants from you. You can describe the path from a vertex buffer to a pixel, and name which parts of that path your code controls.
- Rendering: Batching, instancing and a draw call budget. You can draw a ship's worth of repeated fittings in a handful of instanced draw calls and hold yourself to a stated per-frame budget.
- Rendering: Cameras, projection and drawing between ticks. You can build view and projection matrices by hand and feed the renderer the interpolated transforms the fixed-step loop produces.
- Audio and feel: The audio callback is a hard real-time contract. You can move data between the game thread and the audio thread without a lock, an allocation or a dropout.
- Audio and feel: Spatial audio: putting a sound in a compartment. You can position a source in three dimensions, apply distance attenuation and occlusion, and make a sound read as coming from the next compartment.
- Audio and feel: Mixing under pressure: buses, ducking and voice limits. You can route sounds through buses, duck one against another, and choose which voices to steal when more sounds are requested than you can play.
- Procedural generation that stays coherent: Generating to rules instead of random draws. You can express a layout as constraints that must hold and generate from those rules, so every result has an explanation you can trace.
- Procedural generation that stays coherent: Seeds, streams and a world you can reproduce. You can give each generation stage its own seeded stream so one change does not reshuffle the entire world.
- Procedural generation that stays coherent: Testing a generator across thousands of ships. You can run a generator over thousands of seeds in CI, check every result against its own rules, and shrink a failing seed to the smallest case that still fails.