GAME 1303 Mecanim Wiring Guide


Companion documents


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)

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

  1. Make the controller. Add only Speed. Build Idle ↔ Walk. Play.
  2. Add IsGrounded, YVelocity, Jump. Build Jump/Fall. Play.
  3. 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.)
  4. Add Hurt/Dead when Health is wired.
  5. 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 Speed and two states. Turn off Has Exit Time. Your hazards don't need an Animator — they're already animated by code.