Humanity’s Last Stand is a two-person Android aerial-combat game set over London. I contributed across game design and programming; my teammate and I built the game from the ground up and took it to a public release. The Google Play listing currently shows its rounded 1K+ downloads badge. That is a store threshold, not an exact count or a result attributable to one person.
My bachelor’s thesis used the game to examine a broader engineering question: how should a mobile game respond when performance and difficulty need to change? It contains Unity Profiler screenshots, a proposed LiveOps flow and a Monte Carlo balance model. Those are three different kinds of evidence, so I keep them separate here.
HUMANITY’S LAST STAND / EVIDENCE MAP
Three kinds of evidence. Three different claims.UNITY PROFILER / THESIS FIGURES
Reported observations · not a repeatable benchmarkOne high frame-time spike described in the thesis.
The thesis describes a steadier trace; raw samples and device details are unavailable.
MACHINATIONS / MONTE CARLO MODEL
A harder setting lost in the simulation.
The model reports D7 retention of 36.5% versus 19.0%. It represents virtual agents and a simplified challenge model; it does not show what real players did.
Start with the game players can see
The gameplay capture shows a plane moving through a 3D London scene, aiming and shooting controls, targets and status feedback. The store listing describes enemy encounters, aircraft and weapon upgrades, missions and progression. Together they make the product concrete: this is a mobile aerial shooter where scene readability, input and combat feedback have to coexist on a small screen.
The project was collaborative throughout. My stated contribution is game design and programming, alongside one teammate. We built it together from the ground up. I do not assign individual ownership of each feature because a system-by-system task record was not preserved.
Reuse projectiles instead of rebuilding them
The thesis describes a prewarmed pool of 100 projectiles and 20 enemies. The intended lifecycle is straightforward: create a bounded set of objects ahead of time, activate one when gameplay needs it, then deactivate and return it for reuse. That avoids repeatedly paying the full Instantiate/Destroy cost during combat.
In the thesis, the pre-pooling Profiler capture is described as having frame-time spikes up to roughly 66 ms. The post-pooling capture is described as steadier and close to 100 FPS. The available material does not identify a target phone, Unity build configuration, repeatable encounter script, warm-up procedure or raw Profiler export. I treat those numbers as observations reported in the thesis, not as a portable benchmark or a guarantee about the released build.
There is also a useful code-review lesson in the appendix. The pool calls GetComponent for the pooled-object interface during spawn and looks up the pool identifier during return. Those lookups do not by themselves prove a managed-heap allocation, but they are repeated work in a hot path. The excerpt therefore does not establish a strict zero-allocation result. I would cache the references in each pool entry, then profile the exact release-matched build under the same scripted workload before making that claim.
The thesis’s PerformanceMonitor example has another detail I would revisit: every frame over its 33 ms threshold increments a counter and writes several Crashlytics custom keys. I would record the transition into a slow-frame incident, throttle updates and send a compact summary after the encounter. Diagnostics should preserve evidence without adding repeated work to the frame that is already late.
Use a model to challenge the first idea
The thesis models two enemy firing-rate settings in Machinations: a control at enemyFireRate = 0.4 and a harder variant at 0.6. It runs a 14-day Monte Carlo simulation with 1,000 virtual players. In that model, the higher rate lowers predicted retention at every reported checkpoint:
| Simulated checkpoint | Control · 0.4 | Harder variant · 0.6 |
|---|---|---|
| Day 1 | 96.3% | 94.7% |
| Day 7 | 36.5% | 19.0% |
| Day 14 | 2.9% | 0.3% |
The result challenged the initial hypothesis that raising difficulty would improve longer-term retention. It did not show that real players churned at those rates. The thesis explicitly notes that the simplified model represents frustration from excessive challenge but does not represent players leaving because a game is too easy. It also says that the live A/B test and real telemetry were still needed.
That distinction changes the engineering conclusion. The simulation is useful as a pre-deployment warning and a way to make assumptions visible. It is not a substitute for a measured cohort, and it cannot tell us which difficulty setting real players prefer.
Separate the LiveOps design from a production claim
The thesis outlines a wider control and measurement path: remote configuration for tuning, server-side reward validation, diagnostics, and event analysis in BigQuery. It also includes a SQL query intended for comparing player-death events across variants. The thesis describes this as a path toward validating a change with live data; it does not provide the completed A/B result.
The public portfolio does not link a release-matched source revision, a deployed backend or a reproducible cloud test for those pieces. I therefore present the cloud flow as thesis-described architecture, not as independently verified production infrastructure. The same rule applies to the 128 configurable values mentioned in the thesis: without a source revision and a test that demonstrates the full runtime path, that number is context from the document rather than a production capability claim.
What I would measure next
To turn the Profiler comparison into evidence another engineer could reproduce, I would record the released build and source revision, device model and OS, Unity version, quality settings, warm-up, and a deterministic combat scenario. I would capture repeated paired runs and report frame-time percentiles, slow-frame share, GC allocation and recorder availability—not only a best-looking graph.
For balance, I would first make the model represent both sides of the challenge curve, then treat its output as a hypothesis for a guarded live experiment. The experiment would need a defined assignment method, minimum sample, D1/D7 definitions, instrumentation checks and stop conditions. Only the resulting player data could support a claim about actual retention.
The useful result
This project gives me a product story and an engineering story: a game built by a small team and released publicly, plus a written investigation into pooling, diagnostics and balance. The strongest version of the case is not “the model proved the game’s retention” or “the game runs at 100 FPS everywhere.” It is that I can show what was built, explain what the thesis measured or simulated, find where its claims need tighter evidence, and describe the next experiment I would run.
See the game page and gameplay capture or open the Google Play listing.