Slip & Diedevlog

Episode 2 · May 2026 ·

The camera was stuck inside a tractor

329 commits in one day produced a game that mostly did not run, and the browser build we used to test it found the player spawned inside the tractor.

An early greybox scene with brown cylinder trees, a large red tractor block and a basic HUD

The first tech demo looked finished on paper and was almost empty in play. A browser build with telemetry showed why the camera misbehaved: the player had spawned inside the tractor.

On 24 May I reported that the camera was behaving randomly and that seven shots had all missed. The browser build sent a small telemetry snapshot a few times per second, so we had a trace to read.

The camera had spent 92 percent of the session at its minimum distance, 30 centimetres from the player. The player had spawned at the exact spot where the tractor was.

An early greybox scene with brown cylinder trees, a large red tractor block and a basic HUD
21 May. The trees are cylinders and the red block in the foreground is the tractor.

Why the camera sat at 30 centimetres

The camera hangs on a spring arm. When something sits between the camera and the player, the arm shortens so the view is not blocked. With the player inside the tractor, there was an obstacle from the very first frame, and the arm stayed at its shortest the whole time.

That explains why the camera felt random: it was not following my movement, it was pinned against the obstacle.

The tractor had already caused trouble two days earlier. It was an ordinary rigid body, so every contact pushed the tiny player and shook the camera. We changed it to a sensor, which has no contact response. The game does its own distance check and applies the damage separately, so the tractor no longer solves a collision by throwing the player away.

329 commits and an almost empty game

The tractor ended up on the spawn point because of how the demo was made. The original roadmap allowed ten or eleven weeks for a tech demo. On 19 May, 329 commits landed on the development line in one day.

Different agents took successive slices of the roadmap. Most of the 329 were small issue and review commits, not 329 separate features. By the end of the day the repository held eleven weapons, cartoon gore, fatalities, the picker, tractor and monkey, enemy state machines and behaviour trees, a wave director, a HUD, saves and a day/night cycle.

Then the five reviewers played it. Many systems compiled but were not connected to the app. Nine AI systems had never been registered with Bevy, so the enemies stood still. Their line-of-fire check always returned false, so they could not shoot. Weapon fire rates were in the data files and ignored at runtime.

The review found these faults because it tested behaviour instead of counting finished files. Ninety-seven of the 110 findings were fixed that night.

What changed after that review

A compiling game and a plausible test suite do not prove that a system is registered, that a camera is controllable or that an enemy can land a shot. From then on every playable slice needed a run, a capture, a measurement or a focused test matching the claim being made.

Developing without the final assets

The main agent machine has no discrete GPU and does not run Blender, so gameplay could not wait for finished meshes. The first versions therefore use plain shapes with known sizes: a capsule for the banana, boxes for crates and the tractor, cylinders for trees. That is why they look so bare, and it lets gameplay tests run without a window or any model files.

The later player and Brute models serve the same purpose at a more advanced stage: they let us measure foot contact, weapon sockets, hit zones and animation timing. GattixGGX’s final characters will replace them once those contracts are stable.

The browser build

The first playtest loop ran in a browser: we compiled the game to WebAssembly and uploaded each build for me to try. By the end of May the browser version was hiding more problems than it showed, so native builds became the reference and the web target was later removed from the game repository.

An early low-poly jungle with palms, tall grass, a banana character and a complete HUD
31 May. The first scene that read as a jungle instead of a collection of test objects.
Under the hood: placeholders, headless tests, the tractor sensor and the browser build

Placeholders. The decision is ADR-0016, from 20 May. The PlaceholderAssets resource in sd-level holds a mesh and material handle for each entity role. The banana is a Capsule3d of 0.3 m, a palm trunk a 12 m cylinder, a wooden crate a 1 m cuboid, the picker a 1.8 m cuboid and the tractor a 4x2x2 m cuboid. A system checks whether the real .glb exists on disk. If it does not, it logs a warning and spawns the primitive. Spawn code checks whether the asset handle has loaded and falls back to the placeholder if not.

Headless tests. Because spawn code does not need a render context, tests build an App with MinimalPlugins, add the plugin under test and step the schedule. The workflow note (docs/testing-without-assets.md) also lists LLVMpipe on the agent machine, which is software rendering and only good for checking that the game starts. Since the web target died, visual checks happen on a separate test machine with a GPU.

The tractor. The tractor is RigidBody::Kinematic with a Sensor collider. A sensor generates collision events but no contact forces, so it cannot push the player. The damage check is manual, a distance test inside the tractor system. It stays a valid hurtbox for projectiles through a marker component:

RigidBody::Kinematic,
Collider::cuboid(1.5, 0.5, 1.0),
CollisionLayers::from_bits(hazard_layers.memberships, hazard_layers.filters),
// Sensor: a trigger volume, it does not push the player.
Sensor,
ProjectileHurtbox,

The spring arm. The arm length shrinks when something sits between target and camera. A spawn point inside a collider therefore makes the obstacle permanent, and the arm never gets a frame in which to extend. The trace we read reported the camera distance over time, which is how the 92 percent figure came out.

The browser build. The game was compiled to WebAssembly, uploaded and labelled with a build number. Build 14, made at two in the morning on 21 May, was called “FINAL”. Ten builds later, build 24 was the first one described simply as “working”. The failures were ordinary: balance files were not packaged, so they had to be embedded; WebGL2 could not use the 16-bit terrain textures we had exported; some rendering plugins had to be disabled, and the first attempt to disable them was wrong. WebGL2 also has lower visual limits than the native renderer, and audio needed browser-specific handling. The wasm release profile used lto = "fat" (ADR-0014). That decision and the wasm asset-check rule (ADR-0013) went away with the web target.

Sources

Next time: a 30 centimetre player breaks the camera and the ground collision, and the level generator loses to an editor.

Spotted a mistake, or solved the same problem another way? Write to devlog@slipanddie.com.

← All episodes