The question
How do the design theories taught in game design — MDA, game loops, internal economy, game feel — map onto the concrete scripting patterns used in Unity 2D? Where does a design decision become a line of code?
The event chain: design concepts in motion
A typical Unity 2D game processes each meaningful event through a chain of responsibilities. Each step corresponds to a design concept:
Input → Physics → Logic → Economy → Feedback
| Step | Design concept | Unity implementation |
|---|---|---|
| Input | Player agency, real-time control | Input.GetAxisRaw, Input.GetKeyDown |
| Physics | Simulated space (Swink’s second building block) | Rigidbody2D.linearVelocity, AddForce |
| Logic | MDA mechanics (rules, conditions, state) | OnTriggerEnter2D, CompareTag, method calls |
| Economy | Internal economy (scores, health, lives, drains, sources) | GameManager.AddScore(), TakeDamage() |
| Feedback | Game feel (audio, visual, animation) | AudioSource.PlayOneShot, ParticleSystem.Emit, Animator.SetTrigger |
The key insight from the mda-framework is that designers control the mechanics layer; the dynamics and aesthetics emerge from player interaction. In Unity terms: you write scripts for the Logic and Economy steps, then observe what players experience. Playtesting is how you close that gap.
Design concepts and their Unity counterparts
MDA mechanics → MonoBehaviour scripts
MDA defines mechanics as the rules, resources, and actions of a game. In Unity, mechanics are encoded in MonoBehaviour scripts:
- Rules — conditionals and guard clauses in methods (
if (currentState != GameState.Playing) return;) - Resources — private fields in
GameManager(private int score,private int lives) - Actions — public methods with clear verbs (
AddScore(int amount),TakeDamage(int amount)) - Triggers —
OnTriggerEnter2Dcallbacks that fire when the game world detects a rule-relevant event
The mechanics produce dynamics through the object communication chain. A coin’s OnTriggerEnter2D calls GameManager.Instance.AddScore(10), which changes state, which in turn drives the UI and audio feedback. The student/designer never directly “produces” a dynamic — they write mechanics and observe what emerges.
See unity-object-communication and unity-collider2d-and-triggers.
Game loops → GameManager + lifecycle
Sellers’ game loop taxonomy distinguishes inner loops (immediate action/feedback), core loops (session-level goals), and outer loops (progression). In Unity 2D:
- Inner loop —
Update()on PlayerMovement + Rigidbody2D physics + immediate audio/particle feedback - Core loop — the win/lose condition logic in
GameManager, triggered by cumulative state changes (all coins collected, health reaches zero) - Designer’s loop — the edit → playtest → adjust cycle enabled by Unity’s Play Mode
The GameManager owns the state that defines when the core loop ends (isGameOver, coin counts, lives remaining). It exposes a public API so other scripts can report events without needing to know the win/lose logic themselves.
See unity-gamemanager-pattern and monobehaviour-lifecycle.
Internal economy → GameManager public API
Adams’ internal economy describes sources (score pickups), drains (damage, lives lost), converters, and feedback loops. The GameManager pattern implements this directly:
// Source — adds to the economy
public void AddScore(int amount) { score = Mathf.Max(0, score + amount); }
// Drain — removes from the economy
public void TakeDamage(int amount) { health = Mathf.Max(0, health - amount); }Clamping with Mathf.Max(0, ...) and Mathf.Min(maxHealth, ...) enforces the economy’s valid range. Guard clauses (if (isGameOver) return;) prevent the economy from updating after the game ends — a common source of state corruption in student projects.
The GameManager never queries other scripts for their state. Other scripts call into it. This direction of dependency keeps the economy centralised and auditable.
See unity-gamemanager-pattern.
Game feel → the feedback step
Swink’s three building blocks are real-time control, simulated space, and polish. In Unity:
- Real-time control —
Input.GetAxisRawintoRigidbody2D.linearVelocitywithTime.deltaTimefor frame-rate independence - Simulated space — the physics simulation (gravity, collision response, Rigidbody2D body types)
- Polish — audio, particles, and animation driven from scripts in response to game events
Polish is the last step in the event chain. It should be triggered by the same game events that drive the economy, not separately. A coin pickup calls GameManager.AddScore(10) and then fires sfxSource.PlayOneShot(coinClip) and SpawnParticlePrefab(coinFX, worldPos) in the same method. This keeps feedback consistent with state.
See unity-audiosource, unity-particle-system-scripting, unity-animator-scripting.
Structural principles
Separation of concerns
Each script has one job. The fat class anti-pattern — a single script that handles input, physics, economy, and feedback simultaneously — is the most common structural failure in student Unity projects. It makes debugging difficult and playtesting feedback hard to act on (which mechanic caused this dynamic?).
The correct structure for a small Unity 2D game:
| Script | Responsibility |
|---|---|
PlayerMovement | Input → Physics |
CoinPickup | Detect collision → call GameManager |
GameManager | Own economy state; expose public API |
FeedbackManager | Audio, particles, screen effects |
UIManager | Read GameManager state; update display |
Scripts communicate by reference + method call (see unity-object-communication). The GameManager is the exception: it uses a singleton so any script can reach it without an Inspector reference.
Direction of dependency
Dependencies should point toward the GameManager, not outward from it. CoinPickup knows about GameManager; GameManager does not know about CoinPickup. This keeps the economy testable in isolation and prevents coupling where removing a feature breaks unrelated systems.
Prototype mechanics first
Bond’s iterative design loop and Swink’s prototyping advice both recommend building the inner loop first — the core movement/collision/feedback cycle — before layering in the economy and outer loops. In Unity terms: get PlayerMovement + Rigidbody2D + one collision callback working before building GameManager. This matches the CRE132 lab sequence (Labs 1–4 before Labs 5–9).
Where design decisions become bugs
Some student bugs are configuration issues (wrong body type, missing Collider2D, tag mismatch). But the subtler bugs are design-logic failures:
| Design error | Code symptom |
|---|---|
| No guard on win/lose state | Score increments after game over |
| Economy not clamped | Health goes negative; score wraps |
| Fat class | One script handles input + collision + sound → hard to playtest |
| Feedback wired to wrong event | Audio plays on wrong condition |
| No separation between physics and logic | Update() moves Rigidbody2D directly → jittery physics |
Understanding the event chain (Input → Physics → Logic → Economy → Feedback) helps diagnose these: find which step the failure occurs in, then check the corresponding script.
Open questions
- How does this architecture scale to multiplayer, or to games with AI-controlled agents? The
GameManagersingleton becomes a bottleneck. - At what point does a
GameManagerbecome too large and need splitting intoScoreManager,HealthManager, etc.? - How do Unity’s newer features (e.g. the Input System package, Scriptable Objects for data) change this architecture?
Related
- mda-framework — design theory this architecture implements
- game-loops — loop taxonomy mapped to Unity lifecycle
- internal-economy — the model
GameManagerencodes - game-feel — the feedback step and Swink’s three building blocks
- unity-object-communication — the reference + method call pattern
- unity-gamemanager-pattern — singleton, public API, economy state
- unity-collider2d-and-triggers — mechanics triggers in Unity
- unity-audiosource — feedback: audio
- unity-particle-system-scripting — feedback: particles
- unity-animator-scripting — feedback: animation + game state
- monobehaviour-lifecycle — how Unity’s execution order maps to the event chain
- prototyping — build inner loop first; iterate from mechanics outward
- overview-unity-csharp-cpp-programming — the broader programming learning path that feeds into this architecture view
- source-cre132-labs