2026-09-07 · AFTERSHOCK

Title to credits in Unity, and why the levels feel alike

The Unity port now runs from the title screen to the credits, with sound and saves. The key tracker stopped printing chopped variable names, and a measurement pass put numbers on why six of the seven levels feel like one.

The Unity port reaches the end of its list

Four seams closed today.

  • The front end draws. The eleven-way draw routine was ported whole, with the biome plate memoised on its five rounded offsets: 58.7ms down to 0.94ms, proven bit-identical across a 288-frame differ rather than assumed.
  • The game sounds. The wrong import setting is not an error. GetData on a compressed clip returns true and fills the buffer with zeroes, so what you get is fifty-seven silent one-shots. An import postprocessor now sets the rules.
  • All seven levels populate. The enemy table went from 9 archetypes to 55, and two levels went from zero enemies to 255 and 250.
  • Progression is wired. It had been fully ported with nothing calling it: 740 lines of scoring, relics, unlocks and records behind a host that answered zero. The stage tally now counts six real rows instead of none.

The bug that would have bitten anyone else

91 of 107 scripts had no committed .meta file, and the scene refers to its game loop by GUID. Unity mints a fresh GUID on every clean import, so the scene worked in whichever working copy generated it and came up with a missing script everywhere else: a clone, a build machine, a fresh checkout. It surfaced exactly once, on the first verification copy made from scratch. All 194 meta files are committed now, and the check reads the component back out of the committed scene rather than trusting the file.

The two painted pictures

This game draws everything in code. There are exactly two exceptions, the world map and the studio plate, and neither had been ported. The map was drawing its route on flat black at 2.9% ink: faithful to the JavaScript's failed-load path, and indistinguishable from broken. Both are imported now, box-filtered straight from the importer's pixel array so the 50MB full-size canvas is never built. The map lands at 405x270 and the plate at 480x270, both in 294ms at boot.

Checking a frame differentially

The final level had never been run end to end, because three hundred frames of walking right does not reach it. It does now, and is photographed through, with checks written to fail the way it actually broke in the JavaScript: the wrong machine drawn for a boss.

An ink percentage cannot tell a drawn boss from a painted backdrop. So each frame is rendered twice, with the thing under test and without it, and the check is the fraction of the frame that changes. A countdown clock moves 0.99% of the frame. A boss entering from off screen moves 5.85%, which is low because it enters 600px beyond the camera edge and the capture catches it mid-entry. The check knew that and the eye did not.

The key tracker

The sockets at the bottom left answer "what do I still need before the boss door opens", with a name under each one. Two faults, both found by looking at a frame.

The row was a smear. The pitch was two constants, 30 and 26 once there were more than four, both chosen when this was the first level's two sockets. The slot list later grew to read the stage, so one level puts up seven, and seven nine-character names drawn 26px apart is not seven names. The pitch is now derived from the widest label and the room before the minimap, measured in whichever face is drawing. The browser's 5px monospace is about three pixels to a character and the Unity port's 5x7 cell is six, so it was the same fault at two severities: letters touching in one, an unreadable smear in the other.

The names were chopped variable names. A key id is a variable name. It was also, uppercased and cut to nine characters, the label under its socket, so the player was reading DEMOLITIO, AUTHORIZA and CONTAINME. Every one of these keys already has a name, announced when it drops and again when its door opens. The tracker now says what the toast said.

key idwasis now
demolitionDEMOLITIODEMO CORE
authorizationAUTHORIZACOMMAND
containmentCONTAINMECONTAINMENT
firstlightFIRSTLIGHFIRST LIGHT

The cut is gone rather than lengthened. A width applied to a string, inside a function that has no idea how wide anything is, was the whole bug. A new probe renders the real HUD against both faces and fails if a single character was dropped: 112 checks, up from none.

Why the levels feel alike

A note about the spiked plates showing up in every level turned into a measurement pass across all seven built stages. The numbers are worse than "similar".

measuredresult
set pieces87 crushers, 104 vents, 101 collapses, 90 conveyors, all four kinds in all seven levels
props17 of the 21 kinds appear in all seven levels
tile alphabetthe whole game is five characters
prop artone atlas for all seven levels, baked in the first level's ramps
per-biome effect on a propa 0.13-alpha colour wash over the bottom 45% of the frame

A buried tomb complex contains conveyor belts, coolant tanks and duct runs. They are not similar props, they are the same sprites in another level's colours. The re-shading function that would fix that exists, and is called only for terrain.

The water is paint, and it is countable. Outside the flooded level, every column of water is exactly one row deep, because the land renderer paints the stage's ditch character on the bottom row of each declared gap. It is a skin on a notch. One level promises an immense underground lake and has 152 columns of it, one row deep. And the sharpest single number in the pass: the water query has three call sites and all three are cosmetic bubbles, while the lava query has nine, covering pathing, enemy footing, player damage and respawn choice. The entire difference between a mechanic and a decoration is who bothered to ask about it.

Only the first level has an underground. Its segments are authored in two bands, the surface and twenty rows beneath it, and the first level's underground is real: open tunnels, entered by a shaft, with a door inside. The other six levels contain no underground blocks at all. With one flat band, the only way to make a key feel earned is distance, which is why levels two through six carry five or six keys and walk the player back 26 to 58 tiles to collect them. The first level needs three keys and walks back zero, because its second key is hidden down rather than behind.