WARDLIGHT
Escape a dark dungeon with one continuous line of light. Lantern flames are your lives.


- Project
- Hallowmark
- Our role
- Game design · Architecture · Unity development
- Duration
- In development
01 / Behind the product
A real need. A considered solution.
Challenge
When a level generator applies rules first and tests solvability afterwards, two things happen: most candidates are thrown away and some unsolvable levels slip through the test. The client (Hallowmark) also wanted a measurable guarantee on top: proof that every level is solvable and identical for every player, plus analytics showing where players get stuck.
Approach
"Solution first, rules derived": the generator builds a valid Hamiltonian path, then derives every mechanic (arrows, walls, locks, embers, forbidden cells) from that path, so every level is solvable by construction. The seed is the level itself: seed = (uint)(level * 2654435761). The prototype’s generator was extracted verbatim to Node as the golden reference; the C# port is diffed against golden fixtures in EditMode.
The Wardlight.Core layer has zero UnityEngine references, enforced by asmdef. Ads, purchases, analytics and cloud sync sit behind interfaces, so the game runs in the editor with no SDKs. Cloud and analytics use REST rather than SDKs (IL2CPP-safe), and funnel events were added for the client’s question: which level the app was backgrounded on, and on which attempt the player was caught.
Result
3200 levels swept, 3200/3200 solvable; the same levels generated twice produce byte-identical output. The reports live in the repo and re-run on demand. 40/40 EditMode tests are green and a 34 MB release-ready Android bundle is built. The client approved the drag feel at the interim delivery; the seal effect, the Warden tension vignette, the Merchant shop and rewarded-ad placements landed in the second stage.
02 / Inside the product
The experience, on screen.
Open a screen to explore the details.03 images
03 / Scope & details
The details make the experience.
- 01
3200 levels swept, all solvable by construction
- 02
Byte-identical output on regeneration
- 03
Zero UnityEngine dependency in the Wardlight.Core layer
- 04
Ads, purchases, analytics and cloud sync all behind interfaces
- 05
Funnel analytics built around the client’s question: which level was abandoned, on which attempt
Technology
- Engine
- Unity 6000.5
- Languages
- TR · EN
Level generation & validation
Deterministic generation
seed = (uint)(level * 2654435761)3,200 levels swept
INSIDE THE PRODUCT
The engineering behind the interface.
Systems we built and concrete problems we addressed in this project.
The prototype is the golden reference
The oyna.html prototype’s generator was extracted verbatim to Node; the only change replaced searchHam’s wall-clock deadline with a fixed op cap (the determinism fix). Golden fixtures sit at every mechanic threshold, and GenerationParityTests diffs the C# generator against them.
Cloud and analytics without SDKs
Firebase and Supabase integrations use REST through UnityWebRequest; IL2CPP-safe with no bundle bloat. Anonymous auth, progress backup, raw event rows and the GA4 Measurement Protocol sit behind the same ICloudSync interface; an empty key simply means offline.
Drop-off is captured from two events
Backgrounding is listened for on both OnApplicationPause and OnApplicationFocus, because with Run-In-Background enabled the HOME key fires focus rather than pause; the event is deduped to log once. level_fail_caught carries the attempt count: the answer to the client’s "where do they get stuck" question.
Your idea could be next.
What could we build for you?
From a first release to a growing platform. Let’s plan the design, development and launch together.
From Türkiye to the US, UK, Australia and everywhere in between.