- Unity 6
- C#
- Cloud Code (JavaScript)
- UGS Leaderboards
- Google AdMob
- Google Play
What it is
-
Waves and bosses
Helicopter types with their own attack patterns, mini-bosses and a final boss, weapon crates dropping from the kills, and health and ad crates on separate timers so they never crowd each other out.
-
Commandos and ultimates
A roster of characters, each with a signature weapon and an ultimate ability, unlocked and upgraded with the gold and OP earned in runs.
-
Leagues and a global board
Seasonal leagues with promotion and a global all-time leaderboard. Ranking goes by wave reached first, score second.
-
Missions and maps
Daily, weekly, and monthly missions, and maps unlocked with gold.
Keeping the leaderboard honest
In the first version the game wrote scores to the leaderboard directly. That is how most tutorials do it, and it means anyone who can read the network traffic can post any score they like. The current version takes that permission away from the player entirely.
-
The server writes, the player asks
An Access Control policy denies players write access to leaderboards and cloud storage. Scores go to a Cloud Code script, which validates them and writes them with a service token. A modified client has nowhere to write to.
-
The server keeps the clock
When a run starts, the server records the time itself. On submission it checks the claimed wave against how long the run actually lasted, and the claimed score against a ceiling that grows with the wave. Without a run start on record the score is rejected.
-
One number, two keys
The board sorts a single number, so wave and score are packed into it as wave × 109 + score. That sorts by wave first, leaves room for nine-digit scores, and stays well under 253, where a double stops representing integers exactly.
-
Old scores still read correctly
The packing factor changed once, from 106 to 109. Every old value is below 109 and every new one is above it, so the decoder can tell them apart and migrate device records without a reset.
Ads without punishing players
-
Rewarded ads are a choice
Revives, reward multipliers, ad crates, and one daily mission are paid for with an optional ad. Nothing in the core loop requires one.
-
Interstitials on a debt, not a timer
A full-screen ad is owed every second finished run. If the player has just watched a rewarded multiplier on that screen, the ad is postponed to the next run instead of stacking two ads back to back, and the debt carries over rather than being forgotten.
-
When there is no ad, say so
Testing on my own phone, every ad button disappeared. The cause was a private DNS that blocks ad servers, which is common. Hiding the buttons looked like a bug, so they now stay visible and explain that no ad is available, and revive keeps its countdown running so a failed ad no longer means losing the run.
Found in testing
- Weapon crates dropped in the same order every run, because the picker walked the list from index zero each time. It now draws from a shuffled bag, so the order changes but no weapon repeats until every other one has appeared.
- Retry on the results screen reloaded the scene without the skip-menu flag and dropped the player back on the main menu.
- The release script restored a hard-coded version name after the version bump had already run, so every build shipped as 0.1.0.
Scope and limits
- Android only.
- The in-game store is a mock. Google Play Billing, with purchases verified on the server, comes before anything is sold.
- Gold and progress are still kept on the device. Moving the wallet behind Cloud Code, the same way scores already work, is the next piece of work and has to land before paid currency goes live.
What I took from it
A client you ship is a client you do not control. Anything the game is allowed to decide for itself, someone will eventually decide for it, so the useful question for every feature was not whether it works but who is allowed to say that it happened.