GAME 1303 Mecanim Wiring Guide
Companion documents
- 01.GAME 1303 — Pitch Analysis — the original 17 pitches and what they have in common
- 02.GAME 1303 — Scripts by Game_v2 — which of the 115 scripts each student needs
- 03.GAME 1303 — Scripts Source — Text listings of .cs scripts
- 04.GAME 1303 — Instructor Handbook — This Guide
- 05.GAME 1303 — Deployment Guide — How to start in Unity and deploy the scripts
- 06.GAME 1303 — Mecanim Wiring Guide — Mecanim rigs, parameters, state machines
The short answer
There is one Animator Controller per character, not one per system. Every script that animates writes into the same controller — PlayerAnimatorBridge sets the locomotion parameters, MeleeAttack fires an Attack trigger, Dash fires a Dash trigger, and so on. They don't conflict because each script owns different parameters.
Across all 110 scripts there are exactly 20 animator parameters, and any one character uses a subset. Nothing in the kit ever requires an Animator: every script checks whether the parameter exists (or whether an Animator is present at all) before writing to it, so a character with a plain SpriteRenderer and no Animator still works. That's deliberate — students can build and test gameplay before they have art.
There are four distinct rigs (parameter sets), and within the character rigs, two Animator layers if the student wants attacks that play over movement.
The four rigs
| Rig | Driven by | Used by |
|---|---|---|
| 1. Side-scroll character | PlayerAnimatorBridge + optional modules | 11 player characters and every walking enemy |
| 2. Top-down character | TopDownMover | Last Delivery robot, Apep's top-down mode, Cozy Critters pet |
| 3. Pet | PetBrain + PlayerAnimatorBridge | Cozy Critters only |
| 4. No rig | — | doors, hazards, crushers, spikes, projectiles |
Rig 4 matters more than it looks: most animated objects in these games don't need an Animator at all. A spinning saw is transform.Rotate. A crusher is a coroutine moving a transform. A pop-up spike is Vector3.MoveTowards. A bobbing collectible is a sine wave. Students reach for Mecanim reflexively; tell them the hazards are already animated by code, and they save themselves a lot of work.
Complete parameter reference
| Parameter | Type | Written by | Meaning |
|---|---|---|---|
Speed | Float | PlayerAnimatorBridge, TopDownMover | absolute movement speed, 0 when still |
IsGrounded | Bool | PlayerAnimatorBridge | feet on floor |
YVelocity | Float | PlayerAnimatorBridge | + rising, − falling |
Jump | Trigger | PlayerAnimatorBridge (from OnJumped) | a jump started |
Hurt | Trigger | PlayerAnimatorBridge (from Health.OnDamaged) | took damage |
Dead | Trigger | PlayerAnimatorBridge (from Health.OnDied) | died |
Attack | Trigger | MeleeAttack | swing started |
Shoot | Trigger | ProjectileShooter | fired |
Dash | Trigger | Dash | dash started |
DoubleJump | Trigger | DoubleJump | air jump |
Launch | Trigger | MomentumLauncher | Missile Man launched |
IsGliding | Bool | Glide | gliding |
IsWallSliding | Bool | WallSlideJump | sliding down a wall |
IsClimbing | Bool | LadderClimber | on a ladder |
ClimbSpeed | Float | LadderClimber | 0 when paused on the ladder |
IsRiding | Bool | RailRide | grinding a power line |
MoveX | Float | TopDownMover | last facing X (−1…1) |
MoveY | Float | TopDownMover | last facing Y (−1…1) |
IsSleeping | Bool | PetBrain | pet asleep |
IsHappy | Bool | PetBrain | happiness above 60% |
The trigger names on the four action scripts are Inspector fields, not hard-coded — MeleeAttack.animatorTrigger, ProjectileShooter.animatorTrigger, Dash.animatorTrigger, DoubleJump.animatorTrigger, MomentumLauncher.animatorTrigger. Clear the field and the script simply doesn't touch the Animator. Useful when a student wants two attacks driving Attack1 and Attack2.
The bools and floats are hard-coded, but every script that writes one checks anim.parameters at Awake and skips silently if it isn't there. So adding Glide to a character whose controller has no IsGliding parameter produces no error and no warning — it just doesn't animate. Worth mentioning, because "my glide animation doesn't play" is usually a missing parameter, not a broken script.
Rig 1 — Side-scroll character (the one everybody needs)
Minimum viable: 2 states, 1 parameter
Start here on day one. Idle and Walk, with Speed.
[Idle] ──── Speed > 0.1 ────▶ [Walk]
◀──────── Speed < 0.1 ─────────┘
Both transitions: Has Exit Time OFF, Transition Duration 0.
That "Has Exit Time off" is the single most important setting in this entire document. Left on (the default), Unity waits for the current clip to finish before transitioning, and the character feels like it's responding a quarter-second late. Every gameplay transition in a 2D game wants it off. Cutscene-ish transitions are the only exception.
Standard platformer: 5 states
Add Run, Jump and Fall.
┌──────────────────────────────┐
▼ │
[Idle] ◀──▶ [Walk] ◀──▶ [Run] │
│ │ │ │
└───────────┴──────────┘ │
│ │
Any State ── Jump (trigger) ──▶ [Jump] │
│ │
YVelocity < 0 │ │
▼ │
[Fall] │
│ │
IsGrounded ───┴──────┘
| From | To | Conditions |
|---|---|---|
| Idle | Walk | Speed > 0.1 |
| Walk | Idle | Speed < 0.1 |
| Walk | Run | Speed > 5 (set this to about 70% of your moveSpeed) |
| Run | Walk | Speed < 5 |
| Any State | Jump | Jump trigger |
| Jump | Fall | YVelocity < 0 |
| Any State | Fall | IsGrounded false and YVelocity < −0.1 |
| Fall | Idle | IsGrounded true |
Two notes on Any State: always untick Can Transition To Self (or a repeated trigger restarts the animation mid-play), and remember Any State transitions are checked every frame, so keep their conditions tight.
If a student only has one walk cycle, skip Run entirely and delete the parameter threshold — three states is a perfectly good platformer.
Adding hurt and death
Any State ── Hurt (trigger) ──▶ [Hurt] ──(exit time)──▶ Idle
Any State ── Dead (trigger) ──▶ [Dead] (no exit — it's the end)
Hurt and Dead are the two places where Has Exit Time ON is correct on the outbound transition: you want the hurt animation to actually finish. Give the Hurt state's transition-out an Exit Time of 1.
Blend Trees instead of Walk/Run
Once a student has three locomotion clips, a 1D Blend Tree on Speed is cleaner than three states and two thresholds:
[Locomotion] ← 1D Blend Tree on Speed
0.0 → Idle
2.0 → Walk
8.0 → Run
This is a good teaching moment about when to reach for a blend tree: for continuous variation (speed), not for discrete modes (grounded vs. airborne). Discrete states, discrete modes.
Layers — where the "attack system" actually lives
This is the part of the question worth the most attention. There are two ways to handle attacks, and the choice matters:
Option A — single layer, attack is a state (simpler)
Any State ── Attack (trigger) ──▶ [Attack] ──(Exit Time 1)──▶ Idle
The whole body plays the attack clip. Movement animation stops during the swing. This is right for Stardust, Spark of Corruption, MythoClash — games where attacking is a committed action. It's also what MeleeAttack.stopWhileAttacking assumes (it freezes horizontal movement during the swing by default).
Match the clip length to the script. MeleeAttack has windup + active + recovery (default 0.08 + 0.12 + 0.2 = 0.4s). If the animation is 0.9s, the character keeps swinging long after the hitbox is gone. Either trim the clip, adjust the three timings, or — better — switch the script to useAnimationEvents and put EnableHitbox() / DisableHitbox() calls directly on the animation frames. That's the professional approach and it's genuinely easier to tune.
Option B — two layers, attack overrides the upper body (fancier)
- Base Layer (weight 1): the locomotion machine above.
- Upper Body (weight 1, Blending: Override, with an Avatar Mask covering the arms/torso): Empty → Attack on trigger → back to Empty.
The character keeps walking while swinging. Right for a run-and-gun feel (The Magnet, Missile Man's sprint-launch). It requires an Avatar Mask, which for 2D means a mask over the relevant sprite-rig bones — so this only works if the student is using a bone-rigged sprite (Unity 2D Animation package), not frame-by-frame sprite swapping.
For a class of beginners on sprite sheets, Option A is the right default. Mention Option B exists; don't require it.
Rig 2 — Top-down character
TopDownMover writes Speed, plus MoveX and MoveY when Facing Mode is FourDirection. Two setups:
Simple (flip mode): identical to Rig 1's Idle/Walk pair with Speed. The script flips the sprite on X. Nothing more needed. This is right for Last Delivery if Chris's robot is symmetrical.
Four-direction (Zelda-style): two 2D Blend Trees on MoveX/MoveY.
[Idle] ← 2D Simple Directional (MoveX, MoveY)
(0,-1) IdleDown (0,1) IdleUp (-1,0) IdleLeft (1,0) IdleRight
[Walk] ← 2D Simple Directional (MoveX, MoveY)
(0,-1) WalkDown (0,1) WalkUp (-1,0) WalkLeft (1,0) WalkRight
[Idle] ── Speed > 0.1 ──▶ [Walk] ── Speed < 0.1 ──▶ [Idle]
The trick that makes this feel right: MoveX/MoveY hold the last direction rather than resetting to zero, so the idle blend tree keeps facing the way you were walking. That's already how the script behaves — worth pointing out because students often try to solve it themselves.
Four-direction costs 8 clips instead of 2. For a semester project, flip mode is usually the better trade.
Rig 3 — The pet (Cozy Critters)
PetBrain writes IsSleeping and IsHappy; PlayerAnimatorBridge writes Speed if Rhys adds it.
[Idle] ──── Speed > 0.1 ────▶ [Walk] ──── Speed < 0.1 ────▶ [Idle]
│ ▲
└── IsSleeping true ──▶ [Sleep] ── IsSleeping false ───────┘
Optional: IsHappy layered as a variant —
[Idle] and [IdleHappy], transition on IsHappy
Four states covers the whole game. If Rhys wants the pet's mood visible without doubling the clip count, PersonalityTracker can swap the entire RuntimeAnimatorController per personality — one controller for "playful", one for "sleepy" — which is how a game gets a lot of visible variety from a little art. That's already built into CharacterDefinition and PersonalityTracker.Apply().
Rig-by-game
| Game | Rig | States needed | Params used |
|---|---|---|---|
| Cutthroat Course | 1 | Idle, Walk, Jump, Fall, Dead | Speed, IsGrounded, YVelocity, Jump, Dead |
| Commission Witch | 1 | + Hurt | + Hurt |
| Hot Air | — | balloon needs no Animator (tilt is physics) | — |
| The Magnet | 1 | + Attack, Magnet-pull pose | + Attack |
| Missile Man | 1 | + Run, Launch | + Launch |
| Stardust | 1 | + Attack, Dash, WallSlide | + Attack, Dash, IsWallSliding |
| Zap Attack | 1 | + Dash, WallSlide, Ride | + Dash, IsWallSliding, IsRiding |
| Spark of Corruption | 1 × 4 controllers | one per party member | + Attack, Shoot, Dash, IsClimbing |
| Roach Invasion | 1 × 4 controllers | one per insect | + IsGliding, DoubleJump, IsClimbing |
| Apep's Trial | 1 + 2 | side-scroll set + top-down set | + Dash, DoubleJump, IsGliding, MoveX/Y |
| MythoClash | 1 | Idle, Attack, Hurt, Dead | Speed, Attack, Hurt, Dead |
| Detective Liz | 1 | + Climb | + IsClimbing, Hurt |
| Anabasis | 1 or 2 | Idle, Walk (a ghost barely needs more) | Speed |
| Last Delivery | 2 | Idle, Walk | Speed (+ MoveX/Y if 4-dir) |
| Weenie Maze | — | the dog can be a static sprite that rotates | — |
| Cozy Critters | 3 | Idle, Walk, Sleep | Speed, IsSleeping, IsHappy |
| Store Simulator | — | first-person: no player animator at all | — |
Character-swap games (Kathryn, Joshua) need one controller per character, all with the same parameter names. CharacterSwapper swaps anim.runtimeAnimatorController at runtime, which keeps parameter values only if the names match. Mismatched names silently reset to defaults on every swap — that's the bug to watch for in those two projects.
Enemies use the same rig
PlayerAnimatorBridge isn't player-specific. Put it on an enemy with EnemyPatrol/EnemyChase and leave its Controller field empty; the AI scripts call SetSpeed() directly. So a walking enemy needs exactly Idle + Walk + Speed, and optionally Hurt/Dead if it has a Health (the bridge hooks those automatically).
Three enemy states is enough for every enemy in every game in this class.
Practical setup order
- Make the controller. Add only
Speed. Build Idle ↔ Walk. Play. - Add
IsGrounded,YVelocity,Jump. Build Jump/Fall. Play. - Turn Has Exit Time OFF on all four transitions and set duration to 0. Play again and notice how much better it feels. (This comparison is worth doing live in class — it's the moment students understand why the setting exists.)
- Add
Hurt/Deadwhen Health is wired. - Add one module trigger at a time as Tier 3 scripts go on.
Troubleshooting
| Symptom | Cause |
|---|---|
| Animation lags behind input | Has Exit Time is on. Turn it off, duration 0. |
| Character stuck in one state | A transition condition can't be met — check the threshold against the actual Speed value in the Animator window at runtime |
| Jump animation restarts constantly | Any State → Jump with Can Transition To Self ticked |
| A bool animation never plays | The parameter doesn't exist in the controller. The script checks and skips silently — no error appears |
| Attack animation outlasts the hitbox | Clip length ≠ windup + active + recovery. Trim the clip or use useAnimationEvents |
| Sprite faces the wrong way | Scripts flip via transform.localScale.x. If the art faces left by default, untick artFacesRight on the enemy scripts, or flip the source sprite |
| Params reset on character swap | Controllers use different parameter names. Make them identical across all four |
| Everything works but nothing animates | No Animator Controller assigned, or no PlayerAnimatorBridge on the object |
What I'd say to the class in one slide
One Animator per character. Twenty parameters exist; you'll use six. Start with
Speedand two states. Turn off Has Exit Time. Your hazards don't need an Animator — they're already animated by code.