The Last Line: Building a Zombie Shooter for Phones, Solo
Unreal Engine Mobile Solo Development Optimization The Last Line
How I built The Last Line solo in five months: a 150-zombie horde and Souls-style bosses running on a phone, and the systems engineering underneath.
The Last Line
The Last Line is the game I always wanted to make and never had the time for. A layoff earlier this year gave me the time. Five months later it is live on Google Play: a zombie survival shooter that holds a barricade against 150-zombie waves and drops a Souls-style boss into the arena, on a phone. The systems in it are mine.
The loop is a siege. You hold a barricade with a gun, a bow, or a flamethrower while waves cross a gas station, a subway platform, a flooded arena. Heavies soak damage, flyers come over the wall, and bosses fight like they wandered in from a slower game. Free to play, no ads.

A phone gives you a fraction of a PC's compute, memory, and thermal budget, and punishes overruns with heat, throttling, and the OS killing the app mid-fight. Solo work puts the same squeeze on time. I pay full price for what the player sees and cut or fake the rest. I made that trade across the systems below.
A horde that doesn't think

On screen: over a hundred zombies at the barricade, more behind them, flyers overhead. A hundred-plus actors each ticking and pathing is a slideshow on my Galaxy Tab S7 FE test device.
I disable actor tick on every zombie and let one batch component on the spawner run the crowd, scanning the live list each frame and moving whoever is due. Close zombies update every frame, mid ones about every 15th, far ones about every 25th, spread across rotating groups so the cost stays flat. Each move scales by the time since its last one to cover the right ground, and the crowd returns to full rate as a wave thins. Far zombies also fall back to cheaper animation.
In this debug build each zombie is tinted by its update tier: the front rank refreshes every frame, the far ranks in bursts, and the perf overlay holds while they do.
There is no navmesh; path queries for that many agents missed the frame budget, so each zombie rides a designer-authored spline toward the wall. While the barricade holds they crowd it. Once it breaks they draw from a target subsystem that keeps a registry of live targets and spread across them instead of piling onto the player, each taking a fixed offset so the crowd fans into a loose ring. Nothing spawns or dies mid-fight while the prewarmed pool is sized for the wave: it checks zombies out and takes corpses back. On my iPhone test device it holds a full 150-zombie wave with room to spare.
I found my favorite bug in the pool. A watchdog that kills zombies stuck on geometry scanned every actor in the world, found the prewarmed ones idle in the pool with a last-moved time of zero, judged them the most stuck things on the map, and executed them before they could spawn. I pointed it at the batch's live list and made it check state and age first.
One boss, one expensive brain

The horde is cheap because it is dumb. The bosses are the inverse, and affordable because each arena runs one at a time. A boss scores candidate actions against your distance, angle, and health, picks the winner, reacts to hits with staggers and dodges, and jitters its recovery timing so the rhythm stays unreadable.
The hit detection matters most. A once-per-frame melee trace lets a fast arc tunnel past the player between two frames at mobile frame rates, and the miss looks unexplainable. Each swing instead sweeps from the socket's previous position to its current one in capped substeps. It does not make a miss impossible; it turns a constant problem into a rare one.
None of this is boss-specific. I built the boss combat as a standalone framework during this project, the Soulslike Enemy Combat plugin, and the game runs on it. It is on the Unreal marketplace too, and those sales paid for part of the five months.
I built it because of how the last one went. My previous mobile shooter ran its enemy AI as one monolithic behavior tree, and every new behavior meant threading another branch through the same graph until it was unreadable.

Now a StateTree assigns the enemy's role, short behavior-tree sequences drive each beat, and the moves themselves are self-contained ability chunks that chain together. Adding an attack is a new ability, not another branch in a graph nobody wants to open.

Damage numbers without UMG
Hits throw numbers, and a busy wave throws hundreds. I first built each one as a UMG widget, and construction, layout, and GC churn spiked frame times. The shipping system preallocates a pool of 32 plain entries and reuses them. A single HUD post-render callback draws them on the canvas without UMG. When all 32 slots are active, a new hit recycles the oldest. Each number projects to screen space once, then drifts in 2D.
Fitting a $150 phone

A $150 phone and a flagship from the same year are not the same machine, and Unreal starts everyone at the top quality preset. So I set sensible presets per device tier and tested them across a spread of real phones on Firebase Test Lab, dropping quality wherever the frame budget needed it. The trick is to cut what the player cannot see: a shadow here, a render scale there. If it still reads right in motion, the cut is free. A lot of mobile work is deciding what to fake.
The repetitive parts, handed to Claude
A solo project is mostly surface area. Every subsystem needs the same scaffolding around the interesting part: a class in the house pattern, save data wired through the sync layer, data-table row types, delegates hooked up. That work is repetitive rather than hard, and it is where a solo dev loses days.
I used Claude Code for most of it. I would point it at a subsystem I had already written, describe the next one in the same shape, and it produced the boilerplate, the registration, and the save hooks to match. I kept the design and the systems thinking, read every line it wrote, and spent my own hours on the parts that earned them: the horde scheduler, the boss AI, the device tiering. It let one person cover the ground of a small team.
Where it landed
The Last Line has passed ten thousand downloads on Google Play with no ads behind them, and player reports keep surfacing what my test devices miss. Apple is reviewing the iOS build. Five months on no salary was harder than any bug, but it shipped, and the oversized update is next on the list.
App Store version coming soon.
