Postmortem Devlog


Pivoted from Unreal to Unity with ~5 days left, then coded a complete, one-click web game: main menu → guided tutorial → 3-arena campaign → champion, with a "spin to strike/parry" core, cohesive UI, juice, organic art + credited audio. Shipped as a WebGL build (primary, for the CrazyGames "Best Web Game" target) + Windows fallback.

The build itself came together fast. The last 20%... making the core feel right and consistent... was 80% of the pain, almost all of it in two areas: the ball-lethality model (which turned out to be ~5 independent bugs stacked on each other) and WebGL/Unity deploy traps that don't show up until the production build.


The struggles (the good part)

1. The production-only WebGL crash

The dev build ran fine; the production build died instantly with Maximum call stack size exceeded. Cause: Unity's release WebGL defaults to ExplicitlyThrownExceptionsOnly, which compiled a recursion the dev build's full-exception mode tolerated. Fix: WebGL.exceptionSupport = FullWithStacktrace. Lesson: "works in dev" ≠ "works in the shipped build" — the exception/stripping settings change behavior, and a WebGL build is ~3 MB (stripped) vs ~10–11 MB (full exceptions); that size delta became a useful sanity signal.

2. itch.io's stale-cache gremlin

Repeated 404s; at one point the browser asked for build.wasm, a filename that wasn't even in our zip file (ours is WebGL.wasm.unityweb). The build was provably correct (verified locally with curl); itch was serving a stale index.html from a prior upload. Fix that finally stuck: delete the upload → Save → re-upload → Save → open in Incognito (bypasses the cached/service-worker version). Lesson: when the served artifact references a name your build doesn't contain, it's a deploy/cache problem, not a build problem... don't rebuild, re-deploy.

3. The lethality saga — five bugs in a trenchcoat

The core "ball kills you" rule went through more iterations than anything else, because each fix revealed the next problem:

  1. Any-touch-kills → bots walked into the ball and died; parrying felt like an alien, twitchy timing-puzzle bolted onto a brawl.
  2. Speed-gated lethality (slow ball safe, fast ball deadly) → over-corrected: enemies basically never died.
  3. The ball was parking in a "dead zone" speed band — neither clearly safe nor clearly lethal — so most contacts read as harmless.
  4. Bots never weaponized the ball: one spinSkill value gated both striking and parrying, so tuning bots to be beatable (parry less) also made them stop spin-striking. Split it: always spin-strike (offense), parry by skill (defense). Suddenly the arena had stakes.
  5. "Looks red but doesn't kill": the heat-tint read the ball's maintained speed; the kill-check read instantaneous velocity. They diverge for a frame after a bounce → a visibly-red ball could be treated as safe. Aligned both to the same value → red = lethal, always.

Plus a design fork mid-saga (should a moving ball always kill, or only a fast one?) that resolved into: keep the speed nuance, but make danger unmistakable with a progressive heat-up tint so "slow doesn't kill" reads as intuitive instead of buggy. Lesson: when a mechanic feels "wrong," it's often a readability problem masquerading as a rules problem.

Files

ThunderBashers-itch.zip Play in browser
48 days ago

Leave a comment

Log in with itch.io to leave a comment.