2026-09-20 · MITTENS THE KITTENS

A guard flag that outlives what it guards

A recompile mid-game could freeze Mittens permanently, and the reason was a rule we had written down wrong. Plus a jump test that had been passing an impossible jump for a fortnight, and a background check that measures instead of trusting the eye.

The game now builds to a playable .exe with zero errors and zero warnings. Most of the day went on the last of a family of bugs, and the rest on three smaller lessons about checks that were telling us what we wanted to hear.

The rule was written down wrong

For months the codebase carried a rule about what survives a script recompile during play. It was backwards. A recompile keeps every field whose type Unity can serialise, private bools included, and destroys only what it cannot: plain classes, interfaces, delegates and statics.

So a guard flag survives the very thing it vouches for. The flag says "this is set up"; the subscription it was vouching for is a delegate, and the delegate is gone.

What broke after a recompileWhat the player saw
Hit-stopA recompile during any hit-stop, so every stomp and every claw hit, left the clock at zero with the flag that guards the restore already reset. The game froze permanently.
Game flowA death went uncounted, a checkpoint was ignored, and reaching the goal did not end the level. The next call then built a second game flow beside the first.
Enemy and boss hurtboxesThe cached collider survived while the cached target died, so the fast path took a cache hit on nothing. Enemies and every boss stopped hurting the cat.
Swim and thicket filtersThe input filter was never reinstalled, so Mittens could not surface and drowned.
MenusRecompile while paused and the pause menu could not be dismissed.

Everything on that list is fixed. One thing is left alone on purpose: a recompile still resets the run, meaning score, lives and wallet. That only happens in the editor, and no shipped build ever reloads.

Two-player co-op had the same disease. The pairing of pads to cats was never broken. But the spawner hooked scene loads from a method that only runs when play starts, so one script edit meant every level loaded afterwards spawned a single cat for the rest of the session, with no error anywhere. Seats are now sticky by device id, because a wireless pad that idled out and woke up used to swap player one and player two mid-game.

A gate that was lying by omission

The compile check reported 9,629 warnings on a clean project. Every one was a duplicate-type complaint, caused by the check itself compiling the runtime twice. It now reports zero, and that mattered at once: the next build surfaced the project's one real warning, which had been buried in that noise for as long as it existed.

That warning was on a field, and the compiler said a keyword was unnecessary. Removing the keyword produced the opposite warning. The editor assembly sees an inherited member and requires the keyword; the player assembly does not see it and calls it redundant. Editing the keyword only trades one warning for the other, so the keyword stays, the player-side warning is suppressed in place, and the reasoning is written above it so nobody tries the same fix again.

A jump test that passed an impossible jump

One platform in a garden stage could not be reached, and the stage's own test had been reporting "44 authored steps, 0 failing" the whole time.

The test held two numbers, about six units of rise and about eight of reach, and passed any jump that cleared each one separately. They are opposite ends of the same arc. Six units is the top of a jump plus an air jump fired at that top. Eight is the drift over the whole flight, most of it on the way down. At eight across you are on the floor, so a jump asking for most of both at once asks you to be in two places at one time.

The test now flies the real arc on both axes together at 120 Hz and sweeps the air jump across the flight to find the best moment to spend it. Rebuilt that way, the disputed jump cleared by 0.163 of a unit, under three pixels, and it was retired.

The class of bug is closed rather than the instance. There is now a precision floor of a quarter of a unit of spare height, about four pixels and three frames of the air jump's usable window. Under that the stage warns, because clearing a ledge has stopped being a jump and become a frame count. Every step in the stage now prints its spare height, hardest first. The tightest is 0.630 of a unit.

The plough, and the sneeze it nearly deleted

The ruling on thickets was that they should not hold the player up. Now the stems go down under his feet as he runs, he keeps his full 9 units per second, and the claw still cuts a wider swathe than a footfall. Clawing opens a room; running crosses one.

The trap was pollen. Exposure and movement speed read the same sample of the grass, so a plough that reported open ground underfoot would have reported no pollen either, and the whole hay-fever mechanic of that world would have gone quiet without a single error. The plough now takes two samples: the cell underfoot drives movement, the cell ahead drives exposure.

Two world-building notes

A giant lawnmower in the garden world was built wrong twice before it was built right. The blade first rotated the way a wheel does, which swept an eight-unit bar through a huge arc far outside the box that actually hurts you. A rotary blade spins about a vertical axis; seen from the side it stays flat and its apparent length changes, full width broadside and almost nothing edge-on. Its picture and its hitbox now agree. It was also exactly as wide as the trench, which made the only two ways out the only two guaranteed hits, so it is inset a unit at each end. It costs one heart and a knockback and respects invulnerability, because a lawnmower that deleted a run would be the cruellest object in four worlds.

The pond world was brown on brown because it had no backdrop builder at all. Rather than judge the new backgrounds by eye, the build measures them against the tiles the player stands on:

CheckWhat it enforces
Luminance ceilingThe standable edge of every bench top measures L195.1. No background pixel may exceed L138, so a cloud can never be mistaken for a ledge.
Play-plane gateEvery layer's mean must sit 12 under the bench face. It caught a sky at 101.7 and a cloud bank at 106.2, both brighter than the surface the player stands on.
Depth ladderSaturation must rise with nearness: far 0.105, mid 0.434, near 0.535.
Seam checkCaught three tiling faults, including a reed standing on the seam column itself.

Five mistakes in that pass were found by arithmetic rather than by eye, among them a sky gradient inverted so it brightened upward and a far bank drawn fully opaque, which made it a wall that hid the whole sky behind it.

A floor compared against itself

Another world is specified to have no pits and nothing lethal, and it had two. The floor ran from -5 to 52 while the walls stood at -8 to 60, leaving 3 units of void on one side and 8 on the other, both walkable, both over a kill plane. Nothing caught it because the floor check compared the floor slab to the two constants the slab was built from. It asked "is the floor one run?" and never "does the floor reach the walls?"