Unity Term Book
Animation & UI

Animator Controller

Animator Controller is a visual state machine that owns Animation Clips — it decides when and how to blend Idle, Walk, Run, Jump, Attack, and every other state.

Imagine...

An Animator Controller is the character’s stage director. It knows which state they are in (idle, run, jump) and, when a condition is met (jump button, speed > 0), transitions to the next clip smoothly. You declare States as clips and draw “arrows” (Transitions) with conditions — the controller fires each arrow on its own.

The concept in detail

An Animator Controller is an asset (.controller) with one or more Layers, each an independent state machine. The Base layer usually holds locomotion; upper layers can override the torso (throw while running). Each layer has a Weight (0–1) and a Blending Mode (Override/Additive).

Inside a layer, States (orange/green boxes) are Animation Clips, and Transitions (arrows) define when to switch. A transition has Has Exit Time (wait for the clip to finish) and Transition Duration (blend time). Parameters (Int, Float, Bool, Trigger) are the variables the Animator reads to pick a transition.

A Sub-State Machine groups complex states into one node (e.g. all attack variants under “Combat”). Any State is special — a transition from Any State can fire from whatever the current state is, used for Death or Hit interrupts.

From Unity 2022+, the Animation Rigging package adds IK on top of the Animator: runtime hand/foot poses (VR controllers, climbing) without a dedicated clip.

A basic character state machine

Entry
Idle
Walk
Run
Jump
Any State
Death
speed>0
speed=0
speed>4
onDeath

Hands-on steps

1

Create an Animator Controller

Project → Create → Animator Controller. Double-click to open the Animator window. Drop Animation Clips in to create States.

2

Add Parameters

Animator window → Parameters tab (+) → add Float "Speed", Bool "IsGrounded", Trigger "Jump". These are the variables C# will set.

3

Create a Transition with a condition

Right-click a State → Make Transition → click the target. Select the Transition → Inspector → Conditions → add (Speed > 0.1).

4

Assign it to a GameObject

Add Component → Animator → drop the Controller into "Controller". Make sure the Avatar is correct (Humanoid setup).

5

Drive it from script

animator.SetFloat("Speed", velocity.magnitude) in Update(). Cache hashes with Animator.StringToHash() instead of strings.

Interactive simulator

Click a state to jump there, or use the control buttons to fire conditional transitions.

Current State:IdleSpeed: 0.0

Code example

Basic

Drive Animator parameters from a PlayerController — basic locomotion with Speed and a Jump trigger.

using UnityEngine;

public class AnimatorDriver : MonoBehaviour
{
  Animator anim;

  // Cache hashes in Awake — faster than a string every frame
  static readonly int SpeedHash  = Animator.StringToHash("Speed");
  static readonly int JumpHash   = Animator.StringToHash("Jump");
  static readonly int GroundHash = Animator.StringToHash("IsGrounded");
  static readonly int DeathHash  = Animator.StringToHash("Death");

  void Awake() => anim = GetComponent<Animator>();

  void Update()
  {
      Vector3 vel = GetComponent<Rigidbody>().velocity;
      anim.SetFloat(SpeedHash, vel.magnitude, 0.1f, Time.deltaTime);
      anim.SetBool(GroundHash, isGrounded);

      if (Input.GetKeyDown(KeyCode.Space))
          anim.SetTrigger(JumpHash); // Trigger auto-resets after 1 frame
  }

  public void Die()
  {
      anim.SetTrigger(DeathHash);
      anim.SetLayerWeight(1, 0f); // Disable layer 1 on death
  }
}

Code example

Advanced

PlayInFixedTime, CrossFade, and reading the current state — fine-grained transitions from code.

using UnityEngine;

public class AdvancedAnimator : MonoBehaviour
{
  Animator anim;
  static readonly int BaseLayer = 0;

  void Awake() => anim = GetComponent<Animator>();

  // CrossFade: switch to a named state with a custom blend duration
  public void PlayAttack(string attackName)
  {
      anim.CrossFadeInFixedTime(attackName, 0.15f, BaseLayer);
  }

  // Is the character in a given state (name or tag)
  bool IsInState(string stateName)
  {
      return anim.GetCurrentAnimatorStateInfo(BaseLayer)
                 .IsName(stateName);
  }

  // Scale animation playback from gameplay
  public void SetAttackSpeed(float speedMultiplier)
  {
      anim.SetFloat("AttackSpeed", speedMultiplier);
  }

  // Read normalized time to know how far the clip has played
  void Update()
  {
      var info = anim.GetCurrentAnimatorStateInfo(BaseLayer);
      if (info.normalizedTime >= 0.9f && info.IsTag("Attack"))
      {
          Debug.Log("Attack almost done — combo window");
      }
  }
}

📌 Quick recap

  • A Trigger auto-resets after 1 frame (Jump, Hit)
  • Animator.StringToHash() → cache in Awake, avoid string GC
  • Any State → interrupt from any current state
  • SetFloat(hash, value, damping, dt) for smoother blends
  • normalizedTime: 0 = start of clip, 1 = end of clip

⚠️ Common mistakes

  • ❌ Trigger does nothing — the state never changes

    Has Exit Time is true and the clip has not finished — the trigger is ignored

    ✅ Turn off Has Exit Time for transitions that should fire anytime

  • ❌ Animation does not blend — it hard-cuts

    Transition Duration = 0

    ✅ Set Transition Duration to 0.1–0.25s to blend