Unity Term Book
Animation & UI

Animator Controller

Animator Controller là state machine trực quan quản lý Animation Clip — quyết định khi nào và cách nào blend Idle, Walk, Run, Jump, Attack và mọi state khác.

Hãy tưởng tượng...

Animator Controller giống như bộ não phối hợp diễn xuất của nhân vật. Nó biết nhân vật đang ở trạng thái nào (đứng, chạy, nhảy) và khi điều kiện được thoả mãn (bấm nút nhảy, tốc độ > 0) thì chuyển sang animation tiếp theo một cách mượt mà. Bạn khai báo các "State" là các clip hoạt hình và vẽ "mũi tên" (Transition) giữa chúng với điều kiện — Animator Controller tự quyết định khi nào kích hoạt từng mũi tên.

Khái niệm chi tiết

Animator Controller là asset (.controller) chứa một hoặc nhiều Layer, mỗi layer là một state machine độc lập. Layer Base thường chứa locomotion (di chuyển), layer trên có thể blend override cho body upper (ném đạn trong khi chạy). Mỗi layer có Weight (0–1) và Blending Mode (Override/Additive).

Trong mỗi layer, có các State (hộp cam/xanh) đại diện cho Animation Clip, và Transition (mũi tên) định nghĩa điều kiện chuyển state. Transition có Has Exit Time (đợi animation xong rồi mới chuyển) và Transition Duration (thời gian blend giữa hai clip). Parameters (Int, Float, Bool, Trigger) là các biến Animator đọc để quyết định transition nào được kích hoạt.

Sub-State Machine là cách nhóm nhiều state phức tạp thành một node (ví dụ: nhóm tất cả attack variants vào “Combat” sub-state). Any State là state đặc biệt — transition từ Any State sẽ kích hoạt từ bất kỳ state hiện tại nào, dùng cho Death hoặc Hit animation cần interrupt.

Từ Unity 2022+, Animation Rigging package cho phép IK layer trên Animator Controller: điều chỉnh runtime vị trí tay/chân (VR controllers, leo tường) mà không cần Animation Clip riêng.

Sơ đồ State Machine nhân vật cơ bản

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

Hướng dẫn thực hành

1

Tạo Animator Controller

Project → Create → Animator Controller. Double-click để mở Animator window. Kéo Animation Clip vào để tạo State.

2

Thêm Parameters

Animator window → Parameters tab (+) → thêm Float "Speed", Bool "IsGrounded", Trigger "Jump". Đây là biến C# sẽ set.

3

Tạo Transition với điều kiện

Chuột phải State → Make Transition → click State đích. Chọn Transition → Inspector → Conditions → thêm điều kiện (Speed > 0.1).

4

Gán vào GameObject

Add Component → Animator → gán Controller vào slot "Controller". Đảm bảo Avatar đúng (Humanoid setup).

5

Drive từ script

animator.SetFloat("Speed", velocity.magnitude) trong Update(). Dùng Animator.StringToHash() để cache hash thay vì string.

Trình mô phỏng tương tác

Click vào state để chuyển trực tiếp, hoặc dùng nút điều khiển để trigger transition có điều kiện.

Current State:IdleSpeed: 0.0

Ví dụ Code

Cơ bản

Drive Animator parameters từ PlayerController — locomotion cơ bản với Speed và Jump trigger.

using UnityEngine;

public class AnimatorDriver : MonoBehaviour
{
  Animator anim;

  // Cache hash tại Awake — nhanh hơn string mỗi 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 tự reset sau 1 frame
  }

  public void Die()
  {
      anim.SetTrigger(DeathHash);
      anim.SetLayerWeight(1, 0f); // Tắt layer 1 khi chết
  }
}

Ví dụ Code

Nâng cao

PlayInFixedTime, CrossFade và đọc state hiện tại — kiểm soát chi tiết transition từ code.

using UnityEngine;

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

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

  // CrossFade: chuyển sang state theo tên với blend duration tuỳ chỉnh
  public void PlayAttack(string attackName)
  {
      anim.CrossFadeInFixedTime(attackName, 0.15f, BaseLayer);
  }

  // Kiểm tra nhân vật đang ở state cụ thể (tên state hoặc tag)
  bool IsInState(string stateName)
  {
      return anim.GetCurrentAnimatorStateInfo(BaseLayer)
                 .IsName(stateName);
  }

  // Điều chỉnh tốc độ phát animation theo gameplay
  public void SetAttackSpeed(float speedMultiplier)
  {
      anim.SetFloat("AttackSpeed", speedMultiplier);
  }

  // Đọc normalized time để biết animation đã phát được bao nhiêu %
  void Update()
  {
      var info = anim.GetCurrentAnimatorStateInfo(BaseLayer);
      if (info.normalizedTime >= 0.9f && info.IsTag("Attack"))
      {
          Debug.Log("Attack sắp kết thúc — có thể combo");
      }
  }
}

📌 Ghi nhớ nhanh

  • Trigger tự reset sau 1 frame (dùng cho Jump, Hit)
  • Animator.StringToHash() → cache ở Awake, tránh string GC
  • Any State → transition interrupt bất kỳ state nào
  • SetFloat(hash, value, damping, dt) để blend mượt hơn
  • normalizedTime: 0 = đầu clip, 1 = cuối clip

⚠️ Lỗi thường gặp

  • ❌ Trigger không hoạt động — state không chuyển

    Has Exit Time = true và animation chưa xong — trigger bị ignore

    ✅ Tắt Has Exit Time cho transition muốn trigger bất kỳ lúc nào

  • ❌ Animation không blend mượt — cắt đột ngột

    Transition Duration = 0

    ✅ Đặt Transition Duration = 0.1–0.25s để blend