Devlog: I remade a Siemens phone game, and my own architecture caused every bug I shipped
I finished Builder: Match 3, a remake of Stack Attack 2 — the game that came preinstalled on old Siemens phones. A crane drops coloured crates, you play the builder below, three of a colour clears, a crate on your head kills you. The one thing I added is carrying: you can pick a crate up, walk with it and put it down where you want. In the original you could only push them, so a bad drop quietly ended your run.
Here is what actually happened while making it.
The decision that shaped everything
I made the grid the source of truth. The moment a crate is released it already owns its final cell, and the sprite then glides down over about a second to catch up. Logic is instant, visuals are late. It is a clean model and it caused both bugs my testers found.
First one: push two red crates into two red crates, and only three cleared instead of four. The first crate to land started the match check while its neighbour was still moving, so the flood fill only ever saw three. Fix: collect all landings of the frame and resolve once in LateUpdate.
Second one: the board is full, one free cell left, and the bonus that clears half the field drops into it — instant game over. My game over check asked the grid "is every top cell taken?" and got "yes" the moment the bonus left the crane, while it was still visibly falling towards the player. Fix: only a crate that has settled and arrived seals a column, plus a short grace window before the loss is real.
Same mistake twice: I read logical truth at a moment when the player was looking at the visual lie. If your logic runs ahead of your animation, every check has to say which of the two it means — and the ones that decide winning or losing must mean what the player sees.
Difficulty by adding cranes, not by turning up a number
My first attempt was the obvious one: speed the crane up forever. It got unfair long before it got hard. What worked was adding cranes instead — a second one at 500 points, a third at 2000, each owning its own set of columns so they never drop into the same place. Same speed, more crates in the air. Now carrying a crate costs you time you can feel.
The 56 MB that was not textures
My first Android build was 56 MB. I spent a while blaming art. It was not art. libil2cpp.so was 48 MB and libunity.so 33 MB, because I was building with the Development flag on. Release build, managed stripping on Medium, IL2CPP set to optimise for size: 21 MB, nothing cut from the game. Textures were around 15 MB the whole time. Measure the .so files before you touch your art.
Testing a board state I could never reach by hand
The game over bug needed a very specific setup: field full to the top, one free cell, player standing in it, bonus falling into it. I could not reliably reach that by playing. So I drove the editor from a script — step one frame at a time, build the exact board, drop the bonus, and check what happened frame by frame. That is how I know the fix works and that a real full board still ends the run.
The store took longer than the last feature
Screenshots, a feature graphic, store text in a lot of languages, data safety, target audience, and a content rating questionnaire that gave a cartoon game about a flattened builder a 16+ badge. Also: one category, there is no second genre field. Budget days for this, not an evening.
What I would tell myself at the start
Write down, on day one, which of your systems is truth and which is decoration. Every bug I shipped lived on that line.
AI disclosure: I used an AI coding assistant while writing the code, and AI help for the store text, the localisation and this post. The art was drawn by a human artist. The design, mechanics and tuning are mine.


Discussion