Unity Term Book
アニメーションと UI

Animator Controller

Animator Controller は Animation Clip を管理する視覚的なステートマシンです。Idle・Walk・Run・Jump・Attack など各状態の切替とブレンドを決め、Parameters と Transition で滑らかに遷移します。図解とシミュレーターで学べます。

想像してみてください...

Animator Controller はキャラクターの舞台監督です。いまどの状態にいるか(idle、run、jump)を把握し、条件が満たされると(ジャンプボタン、speed > 0)次のクリップへ滑らかに遷移します。State をクリップとして置き、条件付きの「矢印」(Transition)を描くと、コントローラが自動で発火します。

概念の詳細

Animator Controller は (.controller) アセットで、1 つ以上の Layer を持ちます。各 Layer は独立したステートマシンです。Base Layer は通常ロコモーション、上位 Layer は胴体だけ上書き(走りながら投げる)などに使います。各 Layer には Weight(0–1)と Blending Mode (Override/Additive)があります。

Layer 内では State(オレンジ/緑の箱)が Animation Clip、 Transition(矢印)が切替条件です。Transition には Has Exit Time (クリップ終了を待つ)と Transition Duration (ブレンド時間)があります。Parameter(Int、Float、Bool、Trigger)は Animator が Transition を選ぶ変数です。

Sub-State Machine は複雑な状態を 1 ノードにまとめます(例: 攻撃バリエーションを “Combat” に)。Any State は特別で、現在どの状態からでも遷移でき、Death や Hit の割り込みに使います。

Unity 2022+ では Animation Rigging パッケージが Animator の上に IK を足し、専用クリップなしで実行時の手/足ポーズ(VR コントローラ、クライミング)を扱えます。

基本的なキャラクターステートマシン

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

ハンズオン手順

1

Animator Controller を作成

Project → Create → Animator Controller。ダブルクリックで Animator ウィンドウを開き、Animation Clip をドロップして State を作ります。

2

Parameter を追加

Animator ウィンドウ → Parameters タブ(+)→ Float "Speed"、Bool "IsGrounded"、Trigger "Jump" を追加。C# からセットする変数です。

3

条件付き Transition を作成

State を右クリック → Make Transition → 目標をクリック。Transition を選び → Inspector → Conditions → (Speed > 0.1)を追加。

4

GameObject に割り当て

Add Component → Animator → "Controller" にドロップ。Avatar が正しいか確認(Humanoid セットアップ)。

5

スクリプトから駆動

Update() で animator.SetFloat("Speed", velocity.magnitude)。文字列の代わりに Animator.StringToHash() でハッシュをキャッシュします。

インタラクティブシミュレーター

状態をクリックしてジャンプするか、コントロールボタンで条件付き Transition を発火してみましょう。

Current State:IdleSpeed: 0.0

コード例

基本

PlayerController から Animator Parameter を駆動——Speed と 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
  }
}

コード例

上級

PlayInFixedTime、CrossFade、現在状態の読み取り——コードから細かい遷移を制御。

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");
      }
  }
}

📌 要点

  • Trigger は 1 フレーム後に自動リセット(Jump、Hit)
  • Animator.StringToHash() → Awake でキャッシュし、文字列 GC を避ける
  • Any State → 現在のどの状態からでも割り込み
  • SetFloat(hash, value, damping, dt) でより滑らかなブレンド
  • normalizedTime: 0 = クリップ開始、1 = クリップ終了

⚠️ よくあるミス

  • ❌ Trigger をセットしても状態が変わらない

    Has Exit Time が true でクリップがまだ終わっていない——Trigger が無視される

    ✅ いつでも発火させたい Transition では Has Exit Time をオフにする

  • ❌ アニメーションがブレンドせず硬直切替になる

    Transition Duration = 0

    ✅ Transition Duration を 0.1–0.25 秒にしてブレンドする