Skip to content

Dev Log · Smiley Battle, Combat, Mini-games, Sound

Ten levels, the same enemies: what was wrong with Smiley Battle's combat

31 August to 2 September 2026:

An index that was only half the job

A world's enemy pool was addressed by the wave index alone. Wave 1 therefore always drew pool[0], wave 2 always pool[1], and that in each of the ten levels. The consequence: all ten levels of a world showed the same enemies in the same order. A fix two days earlier had only varied the waves within a level and never touched the actual case.

It now indexes by wave index plus level slot. What remains: with four primary enemies the pattern repeats every four levels. That is written here because it is true, not because it is solved.

The guardian who never came

Nine finished mini-games sat in a side menu while all 200 story levels were the same side-scroller. The twenty challenge levels, the fifth in each world, now start a mini-game. The reward path for it already existed in full.

That promptly produced a follow-on bug: the level dialogue fires before the start. On all twenty of those levels it announced a mini-boss ("A powerful guardian blocks the way!") and then a mini-game started. Forty lines contradicting what actually happened.

Rather than delete the lines, the mini-boss now sits on slot 7. That gives every world three moments of its own instead of two: a mini-game on 5, a mini-boss on 7, the boss on 10. Along the way, an arithmetic error of my own: the shifting loop used w*10+5 and so hit level 15 for world 1, which is world 2. World 1 was left behind and only turned up through a completeness check across all twenty worlds.

133 milliseconds in the wrong place

The running fight is unremarkable at 8.5 ms a frame. The multiplayer fight, however, reproducibly stuttered at the start. The cause: a language file read cold from disk costs about 45 ms, and that landed on the first hit, exactly when the player is looking. On a phone it is several times that.

  • Multiplayer fight: 134 and 132 ms to 37 and 36 ms
  • Solo fight: 45, 63 and 399 ms to 49, 66 and 78 ms, the mean unchanged, the spike gone

The participants' voice clips are now preloaded on a worker thread. Preloading on the main thread would only have moved the spike into the intro. It is triggered where the character gets its ID, not at the six places that assign it, so it cannot be forgotten with the next new enemy type.

Two measurements with a negative result

Both are written down so nobody repeats them:

  • The world backgrounds are 33 MB as PNG and 2.3 MB in the game; the engine has been compressing them at import all along. The recommendation in the rules file to save 26 MB with WebP was simply wrong.
  • The 1,026 voice clips carry 192 kHz, four times more than needed. Re-encoding to 48 kHz saves no space at all and loads more slowly: 11.4 instead of 7.0 ms per file.

The playing field is a stage

The mini-games had two decorative ideas standing side by side: an audience and lighting. Each answers the other as soon as you think of them as one place. Why is there an audience? Because something is being performed. Why is it lit? Because a stage has spotlights.

Built from back to front: a hall gauze, a balcony with a curved balustrade, three spotlights in the colour of the companion's type, a stage floor with a vanishing point, the audience, and in front of it curtains and footlights. Four reactions hang off events that already existed: card flipped, pair found, wrong pick, win, loss. On a pair, the jump runs as a wave from left to right, a simultaneous jump looks like puppets.

Eight mini-games now have eight venues. A venue is not its own drawing routine but a selection from four building blocks plus colours: theatre, studio, jazz cellar, circus, arena, disco, festival, street party. Otherwise there would be eight special cases.

And the sound, measured

1,026 sounds and 11 pieces of music, before against after:

  • Level span across all emoji sounds: 28.2 dB to 10.1 dB
  • Clips below minus 30 dB, inaudible under music: 41 to 0
  • Clipped clips: 50 to 0
  • Onsets more than 120 ms late: 53 to 0
  • Distinct voice pitches: 11 to 123
  • Storage for audio: 44 MB to 40 MB

The 123 pitches came about without a single new recording: for each of the 236 characters a pitch is calculated from its type and ID. The eleven hand-picked values (zombie low, ghost high) already existed, but had only been applied once when the files were generated and did not exist in the game at all.

The Manufaktur dev

Warm regards from the workshopDigitale Manufaktur Rudolstadt