← Back to projects
Case Study · Systems Design

Dead Saints Parade

A third-person immersive sim stealth game, built solo in Unity. You play a fragile spy in a Gothic-Victorian city where information is the weapon and being seen is the fail state. This is a case study about scope: which systems earned depth, which were refused, and the rule used to decide.

Role Game Director · Technical Game Designer · Sole Developer
Team 1, plus a part-time support programmer
Engine Unity 6.3 · C#
Scope ~2–3 hr · 3 linked regions · ~45–55 scenes
Platform PC + Steam Deck
Status 15 of 18 systems complete · demo Feb 2027
01 · Problem

The genre wants breadth I can't afford

Immersive sims earn their reputation through systemic depth — the reason to play is that systems collide in ways nobody scripted. One developer cannot build breadth. Every system has to justify its slot, and "it would be cool" is not a justification.

02 · Process

Make the pillars adjudicate

Four pillars, plus an explicit priority order for when they conflict. Scope arguments stop being about taste and start resolving by rule. Everything that survives gets tuned with real numbers, exposed on ScriptableObjects so tuning never needs code.

03 · Solution

Four systems deep, the rest shallow

Depth concentrated in cover, lockpicking, guard perception, and sound propagation — the four that carry the stealth loop. Everything else is built to the minimum that serves them. 18 named systems, 15 complete.

04 · Outcome

Systems done, content next

The vertical slice's systems are complete and tuned. Combat, dialogue, and the Canvas UI pass remain. Steam Next Fest demo targeted for Feb 2027, full release Q2 2027.

Four Pillars, and What Happens When They Fight

Most projects list pillars. Fewer say what happens when two of them want opposite things. That second part is where the pillars actually do work — without it, a pillar list is decoration you can argue your way around.

1 · beats everything
Vulnerability
2
Tactical Stealth
4
Information as Power
3 · yields
Non-Linear Exploration

Read as 1 > 2 > 4 > 3. Vulnerability wins outright: any feature that lets the player shortcut stealth is cut regardless of how good it feels in isolation. Information-richness beats exploration convenience: a long detour to reach a document is preferred over auto-collecting it.

Cut by rule 1

Bullet-time combat

Fun, genre-adjacent, and a direct shortcut past the stealth layer. Cut without debate because Pillar 1 outranks everything it would have served.

Cut by rule 4 > 3

Auto-collect on documents

Would have smoothed exploration and cost the game its central act — going to the thing, and reading it. Exploration convenience yields to information.

Why this is the first section Every remaining decision on this page is downstream of that ordering. The cover system's corner behaviour, the hard cap on lockpick skill, and the refusal to let walls fully block sound are all the same rule applied three times.

Cover — Designing a Refusal§7.4

Cover is the primary defensive tool, and the system I'd point at first. Not because of the raycasts — because of the feature it deliberately doesn't have.

Automatic, not button-activated

Walk toward a wall and the player snaps into cover after 4 frames of sustained contact — about 66 ms at 60 fps. There is no cover button. The whole point is to remove a button press from the moment where the player's attention is most expensive. The 4-frame requirement is what stops single-frame flicker from a glancing wall.

WALL +30° −30° +60° −60° 0.42 m PLAYER 0.7 m range wallTangent = Cross(CoverNormal, up)
Detection. A 5-ray fan at 0°, ±30° and ±60° from player forward, cast 0.7 m from chest height. Walls must be vertical, so floors and ceilings are rejected. Approach angle must be under 60° — running parallel past a wall won't trigger cover. Once in, a magnetic standoff holds the player at 0.42 m, far enough to avoid controller penetration, close enough to read as contact.

Wall-tangent movement, no special cases

Camera-relative input is projected onto the wall's tangent, so the control mapping falls out of the geometry instead of being authored per wall:

wallTangent   = Cross(CoverNormal, Vector3.up).normalized
worldInput    = camRight × input.x + camForward × input.y
tangentSlide  = Dot(worldInput, wallTangent)

A wall facing the camera slides with A/D. A wall on the player's left slides with W/S. Any arbitrary angle works with no branches, which is the difference between a system and a pile of cases.

ParameterValuePurpose
Walk speed in cover1.5 m/sSlower than normal walk; sprint disabled
Magnetic standoff0.42 mController radius + 0.1, corrective nudge at speed 10
Rotation slerp10Player rotates to face away from the wall
Footstep interval0.45 mLateral travel; feeds AI hearing at 1 m radius
Exit thresholddot > 0.65~49° into the wall — deliberate exits only
Snap-in delay4 frames≈66 ms at 60 fps; kills flicker
The refusal

Corners don't wrap, and that's the feature

Nearly every third-person cover system lets you round an outer corner with a button — the character slides around, the camera follows, you're on the new wall. It feels good. It was built and it is not in the game.

In DSP the system commits to one wall. At an outer corner the peek camera looks around it, but the body stays clamped at the edge. To get onto the next wall the player has to push away from the current one and re-engage — a deliberate act, taken while exposed.

Because free auto-wrap glides the player blindly into space they haven't looked at yet. That is a shortcut past observation, and Pillar 1 outranks the comfort it would buy.

Inside corners had the opposite problem — the fan would alternate between two surfaces and jitter. That's fixed by normal-locking (NormalLockDot), which keeps the player committed to the original wall. Same principle: one wall at a time, always known.

What it costs, stated plainly

No cover-to-cover dash. No scripted corner turn. No vaulting over cover. Peek uses FindObjectsByType to save and restore the scene camera, which allocates. Single-surface tracking only, so genuinely complex geometry can still break it. These are known and written down rather than discovered later.

Lockpicking — Tuning a Curve That Can't Trivialise Itself§10.4

A pick-and-turn minigame across five tiers. The design problem isn't the minigame, it's the progression: character skill has to matter without ever letting a maxed player skip the thing they're maxed at. Drag the slider and watch the sweet spot widen — then look at how little it widens on a Master lock.

Live — lockpick tuning model Every value below is computed from the shipped formulas.
Lock tier
Finesse stat
level 1 · 0%
SWEET SPOT
Sweet-spot arc
3.0°
half-width 1.5°
Pick break speed
65
HP/s at max error
Worst-case break
1.5 s
from 100 pick HP
Finesse XP on success
50
0.8% of the dial
// pick degradation — runs every frame while tension is applied
damage = baseBreakSpeed × breakMultiplier × skillReduction × dt

breakMultiplier = lerp(0.3, 1.0, errorNormalised)   // 0.3 at the edge of the sweet spot
skillReduction  = lerp(1.0, 0.5, PlayerSkillLevel)  // halves at max Finesse

// cylinder rotation limit — quadratic falloff, so near-misses still turn
rotationError = clamp01((angularError − halfWidth) / (90 − halfWidth))
maxRotation   = (1 − rotationError²) × 90°
TierArc @ skill 0Arc @ skill 1Break speedWorst caseXP
Simple30°38°156.7 s10
Standard20°28°224.5 s15
Advanced12°20°323.1 s20
Expert14°452.2 s30
Master11°651.5 s50

Pick HP is a flat 100 at every tier. Difficulty scales through break speed, never durability — so a hard lock punishes imprecision rather than patience, and the player's relationship with a lock is always "am I on the spot", not "do I have enough resource".

The refusal

The skill cap is deliberately too small to matter

Finesse adds 4° per side. At maximum, a Master lock goes from a 3° arc to an 11° arc — 3% of the dial. A fully-levelled player still has to find the sweet spot by hand, still breaks picks, still feels the lock.

Because progression here is parameter-space, not permission. Stats make existing mechanics easier to execute; they never unlock a bypass. That's the line between an immersive sim and an RPG with a lockpicking skill.

Perception — Seeing versus Noticing§12.2

A guard's eye is two cones, not one. That single split is what produces the genre's signature moment: the cone sweeps toward you, and you freeze instead of running.

GUARD · eye 1.6 m INNER 60° / 15 m detects any player OUTER 120° / 8 m moving players only STILL → unseen MOVING → seen
Two zones. The inner cone detects a player whether or not they're moving. The outer cone only registers movement above a threshold — a stationary player in peripheral vision is invisible, and even when it does fire, peripheral detection fills suspicion at 40% of the normal rate. It's a vague sense, not a sighting. After a cone check passes, 1–3 raycasts to centre-mass, head and feet confirm line of sight; any clear ray counts, which stops a head poking over cover from being a false negative.

Shadow is a hard gate, not a modifier

Below a light level of 0.2 the guard cannot see the player at all, at any range. Above it, light is remapped to 0–1 and raised to an exponent of 2.0, so 50% light gives 25% detection speed. The quadratic is doing real work: it makes the difference between "dim" and "dark" feel like a cliff rather than a slope, which is what makes extinguishing a single candle a tactical act.

All five inputs — distance, angle, light, movement, posture — multiply into one combinedMult, which maps linearly onto a fill time between 0.5 s (worst case) and 5 s (best case). Every threshold lives on a GuardData ScriptableObject, so a Nervous Guard in the Catacombs is an asset, not a subclass.

ScenarioTime to Alert
Standing, full light, centre of cone, close range~0.5 s
Crouching, dim light (0.4), edge of cone, far range~5 s+
Moving, peripheral cone only~12 s
Still, below the light threshold, any rangenever

Sound — Four Terms, One Radius§17.5

Sound is global where vision is local, and that asymmetry is what makes the two systems reward different play. Every noise the player makes runs through the same four-term model before a guard ever hears it. Build a scenario below.

Live — sound propagation model Output mirrors the in-engine debug HUD, line for line.
Movement — raw radius
Surface underfoot — multiplies at source
Ambient masking — subtracts metres at the origin
Wall between you and the guard — multiplies suspicion
0 meffective radius12 m

Decision · masking

Subtract from radius, not suspicion

Masking removes metres from the sound before dispatch. A crouched player (1.5 m) in rain (−4 m) produces a radius of zero and the event is never dispatched at all. A running player still leaks 8 m. Falloff stays distance-based and consistent.

Decision · measurement point

Measured at the origin, not the listener

Masking is sampled where the sound is made. The player is rewarded for positioning themselves next to the organ before acting — not for the guard happening to stand near it.

The refusal

Walls muffle. Walls never block.

The cheap version of this system is a boolean: line of sight to the listener, or silence. It's faster, and it would have made stone walls into perfect soundproofing.

Instead every material is a multiplier — stone 0.2×, wood 0.5×, curtain 0.8× — and a pistol shot at a 50 m radius still punches meaningful suspicion through cathedral stone.

Because "firing a weapon is always a disaster" is a contract with the player. A hard block would make guards deaf behind cover and quietly turn the pistol into a safe option in the exact rooms where it should be the worst one.

The honest limitation: occlusion is a single raycast, so a thin pillar between source and listener blocks as if it were a wall. Multi-sampling to head, torso and feet fixes it at 3× the raycast cost, and it's deferred until vertical-slice tuning shows it actually matters on a Steam Deck.

What Feeds What§13

Read row → column. The point of the table isn't the individual cells; it's the two columns on the right. Almost every player action, whatever system it starts in, eventually expresses itself as a change in one guard's suspicion value.

CoverInteractSoundLightJournalInventoryProgressionAI SensesAI State
Player Movementemitsreadsemitsreads·····
Cover·blocksreads····blocks LOS·
Lockpicking··emits*··uses→ Finesse··
Interaction··emitstoggles·····
Documents / Obs.····writesreads→ Acuity··
Sound Propagation·······reads·
Guard Coordinator··shouts·····writes bb

* Lockpick noise is wired but not yet emitting — the OnPickBroken event exists and Sound Propagation will subscribe at ~6 m. Empty cells are either unwired or not meaningful; they're left blank rather than invented.

Risk Register§19

Kept because a solo project fails from the production side more often than the design side, and because a mitigation you wrote down before the risk landed is worth more than one you improvised after.

RiskSeverityMitigation
Solo dev burnoutHighSingle-doc workflow; aggressive asset reuse; cut ruthlessly
Player can't find clues to progressHighClue-density targets per area; always at least one golden-path clue per critical lock
Scope creep on narrativeHighVertical slice locked; other regions reuse its structural template
OnGUI → Canvas migration slipsMediumRendering decoupled in every system; can ship beta on placeholder UI
Sound propagation perf on Steam DeckMediumStart with single-ray occlusion; profile early
Save/load complexityMediumDefinitions already live on ScriptableObjects; only runtime instances need serialising
AI feels "solved" once routes are knownMediumCoordinator's dynamic role assignment reshuffles each encounter
Note The first mitigation is why the design documentation is one file instead of a GDD plus five FDDs plus an art bible. Maintaining cross-references between documents cost more than the structure returned, for a team of one.

Decision Log§27

A running record of what was decided and why. Reversals aren't deleted — they're struck through with the reason. Ten entries selected from the log; the rule is that every one has to name the pillar or constraint it serves.

2026-03
Health is punitive — 3 HP, no regeneration
Why Vulnerability pillar. Everything downstream assumes the player dies in two or three hits.
2026-03
Cover is automatic, not button-activated
Why Removes friction at the exact moment the player's attention is most expensive.
2026-03
Progression is use-based; no skill points, no allocation screen
Why Immersive-sim parameter-space rather than an RPG bypass. You never stop playing to spend.
2026-03
Clues are author-defined at asset-creation time, not parsed from prose at runtime
Why Narrative keeps total control over what counts as actionable. No ambiguity about what the player was supposed to catch.
2026-03
Wall occlusion is a partial muffle, never a hard block
Why Preserves the "weapons are always a disaster" contract. A hard block would make guards deaf behind cover.
2026-03
Masking subtracts from radius, not from suspicion
Why Keeps falloff consistently distance-based, and keeps loud things loud even inside a masking zone.
2026-03
Cinematic inspection mode is opt-in per observation, not the default
Why Most observations stay ambient and cheap to author. Cinematic attention has to be earned by the beat.
2026-04
Consolidated one GDD; retired the modular GDD + FDD + manual ecosystem
Why Maintenance cost of cross-referencing exceeded the value it returned for a single author.
2026-04-25
Kept the Informant archetype in vertical-slice scope, against the source plan's advice
Why More stealth-verb variety in the demo, at an accepted cost of roughly 2.5 extra days of engineering. Written down as a cost, not as an assumption.
2026-05-01
Player actions persist across scene re-entry; enemies always reset to patrol
Why Doors, taken items, snuffed candles and read documents all persist, so consequence stays on the player. EnemyController is deliberately excluded from save data so a reload never drops you back into an alert spiral.

Implementation Status§1.8

Mirrored from the repo's README.md, June 2026. Listed in full — including what isn't built — because "done" is only a useful word if it's used precisely.

18
Named systems
15
Complete
3
In progress
3
Not started
(outside the 18)
Complete In progress Not started
Done
Cover
Done
Lockpicking
Done
Single-guard AI
Done
Multi-guard coordination
Done
Light & shadow detection
Done
Sound propagation
Four-term model, surface types, wall occlusion, masking, debug HUD.
Done
Contextual interaction
Done
Inventory
Done
Health & damage
Done
Journal & observations
Includes RE4-style inspection mode.
Done
Character progression
Use-based stats.
Done
Extended player controller
Done
Steam integration
Done
Debug infrastructure — tier 1
Done
Debug infrastructure — tier 2
WIP
Distraction / throwables
Phases 1–2 shipped and verified; deliberately on hold until combat or the Canvas UI pass.
WIP
Save / load + scene transitions
6 of 10 phases. Pending: Steam Cloud routing, settings split, difficulty gates, migration table.
WIP
Analytics collection
Event plan locked; accumulator not built, so no events fire yet.
Not started
Combat
Dagger stealth-kill foundation fully specced, not yet in-engine.
Not started
NPC dialogue
Not started
Influence stat
No XP source wired yet.
Stated plainly All current UI is placeholder OnGUI; the Canvas pass is scheduled as Phase C. Every system above was built with rendering decoupled from logic specifically so that swap is a render-layer change and nothing else.

Takeaways

Rank the pillars, or they're decoration

Four pillars told me what the game was. The priority order told me what to cut. The second one did all the work, and it's the part most pillar lists skip.

Cap the stats below the point of trivialisation

Max Finesse moves a Master lock from 3° to 11° of a 360° dial. Designing the cap first and the curve second is what kept progression from eating the mechanic it rewards.

Write the limitation down when you make it

Single-ray occlusion, no corner wrap, no vaulting. Each was a known trade at the time, recorded with its reason. None of them turned into a surprise six months later.

Deeper This case study covers four systems. The full design document — pillars, boundaries, competitive analysis and the rest — is available as an interactive doc.