Skip to content

TECHNOLOGY

Unity mobile game development with measured levels and tested systems

From our Unity mobile games: deterministic level generation, solver-measured difficulty, readability audits and server-verified simulation.

SHORT ANSWER

AMG Software Studio builds mobile puzzle and casual games in Unity and C#. Tilting Jar and Puzzle Reveal are live on the App Store; Screwsmith, SUNTHREAD, WARDLIGHT and BEEing are in development, and One Shot is a prototype. We generate levels deterministically, measure them with a solver and keep the report in the repo: all 3200 levels swept in WARDLIGHT are solvable and byte-identical on regeneration.

Which Unity games have we built, and where does each one stand?

We build our mobile games in Unity and C#; Tilting Jar, Puzzle Reveal, SUNTHREAD, WARDLIGHT, Mosquito and BEEing run on Unity 6. Here is where each one actually stands:

  • Tilting Jar: live on the App Store. 300 levels, 15 chapters, 12 mechanics, 5 languages.
  • Puzzle Reveal: live on the App Store, version 1.1.2. 50 hand-tuned levels plus an endless mode.
  • Screwsmith: in development. 200 baked levels and 16 machines are in; the privacy and support page for 1.0 is live.
  • SUNTHREAD: in development. All 320 levels pass the verification report with zero failures.
  • WARDLIGHT: client work for Hallowmark, in development. The client approved the drag feel at the interim delivery, and a 34 MB release-ready Android bundle is built.
  • BEEing: in development, with launch planned for the winter season.
  • Mosquito: in development; 15 levels, 20 human variants and the store are done.
  • One Shot: a prototype. The playable slice is verified end to end; art, virality and monetisation are the next milestones.

The figures on this page come from the README, plan, progress and report files in each game’s repo.

Why do we generate levels deterministically?

If players are not on the same level, you can neither tune difficulty nor reopen a bug report. So level generation is built to be repeatable:

  • In Tilting Jar, the first 24 levels are a handwritten teaching ladder; after that the seed is the level index, and level 137 is identical on every device. That is not a claim of bit-identical physics across devices; what is identical is the starting conditions.
  • In WARDLIGHT, the generator first builds a valid Hamiltonian path and derives arrows, walls and locks from it, so every level is solvable by construction. The seed is seed = (uint)(level * 2654435761).
  • In Screwsmith, randomness comes from xorshift32 rather than System.Random, giving the same output on every platform and Unity version.

Determinism is protected by keeping game logic out of the engine. Screwsmith.Core and Wardlight.Core contain zero UnityEngine references, enforced by asmdef. The SUNTHREAD and WARDLIGHT generators were written in Node first as the reference; the C# ports are diffed byte for byte against golden fixtures. In Screwsmith, level generation and measurement run headless with -batchmode, without opening an editor window.

How do we measure difficulty with a solver?

Difficulty measured against perfect play is misleading, because almost every level survives perfect play. So Screwsmith’s solver produces two numbers: the minimum rail slots needed under perfect play, and how often a plausible human loses the level. It plays every level 24 times with a shallow strategy, and the loss rate is the dominant term of the difficulty score (×11). Of 200 baked levels, 157 landed exactly on their target band and 34 missed by one. The curve is deliberate: a 0.8% average human loss rate over levels 1–35, a wall at level 36 (37.5%), and 34% over 101–200.

In SUNTHREAD the question is different: does the player actually make decisions? On the WARDLIGHT engine, 100% of moves at level 400 were forced. SUNTHREAD’s generator sets five gates: exactly one solution, no removable clue, a human-style solver that uses only named deduction rules, a forced-move ratio inside 35–70%, and a fingerprint that rejects a rule multiset matching any of the last five levels. The measured ratio runs from 0.44 to 0.79, averaging 0.59.

In Puzzle Reveal, the first version’s formula made the game unwinnable past level three. The 50 levels were hand-tuned instead, and the difficulty manager targets a 65% win rate.

Why are readability and undo audited automatically?

A level being solvable does not mean the player can read it. Screwsmith’s ReadabilityAudit catches pixel facts before any play test: it renders the board to a RenderTexture at real device resolution and measures again at every state of a playthrough.

  • The touch target must be at least 8.5% of screen width, about 4.7 mm. Level 178 had three screws at 0.084 of screen width, and the audit caught it.
  • Colour distance is measured with CIE76 ΔE: at least 11 for normal vision, at least 9 after a red-green colour-blindness simulation. A seventh colour broke the dimmed floor, so the palette stays at six.
  • Screws hidden under the HUD are caught in the same loop.

Undo gets the same treatment. The solver backtracks with the game’s own undo, so an undo bug corrupts measurements too. UndoAudit.Run plays up to 60 random legal moves on each of the 200 levels and compares the board before each move with the board after undoing it. Before trusting the audit, we showed it fails against a broken simulation using UseLegacyCrateUndo and corruptSnapshot.

In Mosquito, a similar quality gate checks for overflow across seven screens at six device resolutions.

Server-verified simulation: how does One Shot catch cheating?

One Shot is a competitive physics puzzle with one shot per day, and it is currently a prototype. Competition requires the same scene to behave identically for everyone, yet floating-point physics can drift across engines and compilers.

The simulation core has no UnityEngine dependency, and CI enforces that. The same .cs files compile on three runtimes: Unity Mono, an IL2CPP player build and a .NET 10 server. 20 golden fixtures produce byte-identical results on all three.

A shot travels to the server with its input log, and verify-shot re-simulates it. An honest shot passes; a forged "1 µm hit" is flagged as a mismatch and the leaderboard keeps the server’s number; an impossible input log is rejected. The shots table is unique on user_id + scene_seed, so a second attempt gets a 409, and the spoiler wall is closed by Supabase RLS. Visual smoke tests render the scene to PNG and assert it is not blank, which caught four real visual defects.

Leaving the final word to the server carries over to other games: in BEEing, score plausibility checks and server-timed offline earnings, and in Mosquito, scores and gates are validated in Supabase Edge Functions.

How are the release pipeline, ads and purchases set up?

On the way from Unity to the store, one missing setting can mean a crash on launch. After a launch crash in Puzzle Reveal’s build 13, the chain was scripted: compile, visual tests, build number, iOS export and Info.plist gates (AdMob id, ATT copy, SKAdNetwork) in one command, and if a gate fails the chain stops. Our later games reuse that approach. In Tilting Jar, Unity → Xcode → pod install runs as one command, and the build refuses to proceed without the AdMob app id.

Ads and purchases are built provider-agnostic. With no key configured, Tilting Jar runs on a fake store. In WARDLIGHT, ads, purchases, analytics and cloud sync sit behind interfaces, so the game opens in the editor with no SDKs at all. Cloud and analytics use REST through UnityWebRequest rather than SDKs, which is IL2CPP-safe and keeps the bundle small.

The offer engine runs on measurement too. In Tilting Jar, "remove ads" is suggested only after five interstitials were actually seen, rewarded ads are never blocked, and progress is never locked behind money. We do not guarantee downloads, revenue or retention; economy design and integrations are scoped separately.

Frequently asked questions

Can you turn our existing prototype into a product?

Yes. In WARDLIGHT, the level generator from the oyna.html prototype was extracted verbatim to Node and became the golden reference; the only change replaced a wall-clock search deadline with a fixed operation cap. The C# port is checked against that reference in EditMode tests. At the prototype stage we evaluate the core loop, the controls and the player’s decisions, and the move to full production is decided after that review.

How do you prove the levels are solvable?

With a report. In WARDLIGHT, 3200 of 3200 swept levels are solvable, and generating the same levels twice gives byte-identical output. In SUNTHREAD, each of 320 levels is rebuilt from JSON and tested in five steps, with zero failures. The reports live in the repo and re-run on demand. In Screwsmith, every winning candidate gets a full proof search before it ships.

Can we see which level players quit on?

Yes; we build the analytics events around your question. In WARDLIGHT the client asked exactly that: which level the app was backgrounded on, and on which attempt the player was caught. Backgrounding is captured from both OnApplicationPause and OnApplicationFocus, because with Run-In-Background on, the HOME key fires focus rather than pause, and the event is deduplicated. level_fail_caught carries the attempt count.

What backend should a Unity game use?

It depends on the game, and sometimes none is needed. Screwsmith’s Daily Bench is seeded from the date and needs no server or account. Tilting Jar backs progress up to iCloud and merges it field by field with the best value. Our games that do use a server use Supabase: in BEEing, 7 migrations and 14 Edge Functions handle score checks, offline earnings and remote config, and WARDLIGHT talks to Supabase and Firebase over REST rather than SDKs.

How many languages can a game ship in?

The language count is set as part of the scope. Tilting Jar ships in five languages (TR, EN, DE, ES, FR) and detects the device language. For BEEing, store screenshots, trailers and ASO copy are ready in six languages: 8 scenes × 6 languages, separately for phone and tablet. Screwsmith’s Turkish and English store copy is written to Apple’s character limits.

How do you run tests on Unity projects?

Engine-free C# layers are tested in EditMode, gameplay in PlayMode. WARDLIGHT has 40/40 EditMode tests green, SUNTHREAD 27 EditMode and 34 PlayMode. BEEing has 152 EditMode, 123 simulation and 23 cooperative smoke tests, and a PolicyBot plays seven days as three personas while checking invariants. In Mosquito, 25 PlayMode tests run with an autopilot bot.

Who owns the source code and accounts?

You do. App Store and Google Play developer accounts, servers and service accounts are opened in your name. At delivery we hand over the full source code, the design files and every credential.

Do you use templates or off-the-shelf admin panels?

No. We design the user screens and the admin panel screens for your project and build them from scratch, so the panel follows your workflow.

RELATED STUDIO NOTES

If you have an idea in mind,
let’s talk.

It starts with an intro call. We define the scope and timeline together, then send you a written proposal.

hello@amgsoftwarestudio.com