Writing

Every reload is a history lesson

· 5 min read

The question that got me was the easiest one in the pack.

Which founding document opens with “We the People”? I pressed the wrong key. My musket was empty and a Hessian grenadier was across the street. The reload locked for two seconds, the card showed the answer (the Constitution, whose Preamble begins with those words), and then it dealt me a fresh question while the grenadier kept shooting.

At the end of the run, the After-Action Dispatch listed that question under “Drill these before the next battle.” That is the whole design, and it worked on me first.

The opening of the trailer, rendered frame by frame from the game. The fife and drum is the U.S. Army Old Guard Fife and Drum Corps playing “Lowlands Away / Trenton” (CC0, via Wikimedia Commons).

The reload is the lesson

Apex Arena: 1776 is a first-person shooter set at dawn on King Street in Trenton, December 26, 1776. You fight Hessian grenadiers with a musket. When you reload, a parchment card appears: “A Dispatch for the General.” Four answers, keys 1 to 4. The game keeps running.

A right answer reloads you. Right answers in a row build a streak, and the streak cuts the reload from 1.45 seconds down to 0.70. A wrong answer resets the streak, locks the reload for two seconds, and shows the correct answer with a one-line explanation. Then it serves a different question, so you can’t just read the answer back. The one you missed comes back a few draws later.

The pack has 70 questions, from colonial America to the modern era, ranked Militia, Regular, or Commander. That is why a Midway question can show up in 1776. The trailer forces a period question for its dispatch beat. The game does not.

First-person view down a musket barrel on a snowy colonial street. A parchment card titled A Dispatch for the General asks which founding document opens with the words We the People, and red text below the four answers says the U.S. Constitution was correct. A Hessian grenadier stands across the street, and the center of the screen reads WRONG - RELOAD LOCKED.

It started as a generic arena shooter

On June 21 I gave Codex a goal prompt for a narrow arena prototype: sprint, slide, mantle, crack a bot’s shield, clear the arena. It committed a verified playable loop 26 minutes later.

I played it, asked for sound and health bars, and then asked for something else:

i would like to test the idea of everytime i have to reload, it asks me some us history based question. if i get the question right i can reload, else im stuck or something. we can have the game running live, no need to pause

The reload quiz was committed eight minutes later. A few minutes after that I asked, “maybe we’re the americans fighting the british?”

On July 2 I came back to it with Claude and made it specific: King Street at dawn, snow, Hessian grenadiers, a musket, a period HUD, the question pack, and the After-Action Dispatch.

I directed. The agents wrote the code.

Codex and Claude wrote most of the code. I worked from my Mac, over SSH to the Linux workstation that runs Unreal Engine 5.8, and watched the game over a stream. My job was to decide what the game was, set the constraints, review what came back, and send it back when it was wrong.

That shaped the architecture. The whole world is built in C++ at runtime: houses, snow, cannon, wagon, lanterns, even the musket in your hands, which is a stack of cylinders and cubes. The first build log says why: it avoids fragile Blueprint asset generation. Code is text. An agent can write it and I can review it. The game module is about 5,600 lines.

Review was real work too. On July 2 I set a group of agents loose on the code to find bugs, then kept only the ones that held up. My favorite: ragdoll corpses blocked visibility traces. Dead Hessians were eating my shots and blinding the live bots for 45 seconds.

Giving the agents eyes

An agent can’t tell whether a game looks right from a log. So much of the tooling is capture.

  • -ApexAutoDemo plays a scripted run and saves a screenshot at each milestone.
  • -ApexShot=x,y,z,pitch,yaw renders one frame offscreen from any camera, then quits.
  • The trailer renders in two passes. One writes every frame on a fixed 60 fps timestep, with no sound. The other runs in real time and records only the audio. A script finds the first volley in the recording and lines the two up.
  • capture_runs.sh records three of my runs back to back. It watches the game log for the exact win line, because the startup banner also contains “ARENA CLEAR”, then restarts the game.

The “We the People” clip came from run one.

A JSON dump found the sun bug

-ApexDump writes the running world to JSON: 713 meshes, plus lights, cameras, and grade settings. A Python script rebuilds that in Blender and renders it in Cycles, about 12 seconds per 1080p frame.

The dump exposed a bug I had been looking at in every screenshot. Unreal’s directional light has a built-in pitch, and spawning it composed that pitch with ours. Every grade put the sun about 46 degrees higher than designed. The default dawn was supposed to have the sun 9 degrees above the horizon. It rendered at 55. It took a list of numbers to see it.

A path-traced Blender rebuild of King Street from the game’s scene dump. A low dawn sun throws long shadows across the snow beside a brick house with glowing windows.

Sound with receipts

The agent that built the musket sounds could not hear them. Its session on the workstation was headless, so it measured attack time, tail length, and low-end energy, and left me a note to audition before import. The second version is built from original-quality files pulled through my Freesound account, and it is louder.

Every source has a receipt in a manifest: author, license, and URL. Most are CC0 or public domain. The musket firings in the current build come from J8054’s “1777 Charleville Firing Samples,” which are CC BY 4.0. So: musket firing samples by J8054 (freesound.org, CC BY 4.0).

VR, the next day

On July 3 we ported it to VR through OpenXR. -ApexVRTimings logs frame-time percentiles after a warmup, then quits. On a simulated headset at 2x pixel density, a VR settings layer took GPU time from 10.4 ms to 8.1 ms. The budget at 90 Hz is 11.1 ms. Instanced stereo crashed the engine, so it stays off.

What I’m keeping

I decided what the game should be, built ways to see it, and checked what came back. That is the part I am getting better at.

The hook came from one lowercase message. The rest came from treating every claim as something to verify: a screenshot, a log line, a frame time, a license page. Every reload is a history lesson. Apparently that includes mine.