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

Blend Tree

Blend Tree は Animator Controller の特殊ノードで、1〜2 個の Float Parameter から複数の Animation Clip を混ぜます。速度や方向に応じて滑らかなロコモーションを作れ、継ぎ目のない移動表現ができます。図解とシミュレーターで重みの変化を体感できます。

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

Blend Tree はミキサーです。「ゆっくり歩き」「ジョギング」「スプリント」の 3 トラックがあるとき、硬直切替ではなく速度に応じてクロスフェードします——2 m/s なら歩き 70% + ジョグ 30%。8 m/s ならスプリント 100%。結果、速度が連続的に変わっても継ぎ目のないロコモーションになります。

概念の詳細

Blend Tree は Animator Controller 内の State を置き換えます——1 クリップではなく、パラメータから複数をブレンドします。主な 3 タイプ: 1D(float 1 つ、通常は速度)、2D Simple Directional(X/Z 速度など float 2 つ、8 方向移動)、 2D Freeform Directional/Cartesian(より柔軟な影響領域)。

1D Blend Tree では各クリップに Threshold があります——パラメータが 2 つの Threshold の間にあるとき、Unity が重みを補間します。例: Idle=0、Walk=2、Run=6——Speed=1 なら Idle 50%/Walk 50%。 Automate Thresholds を有効にするとクリップを均等間隔にできます。

2D Blend Tree はより豊かなロコモーション向け: Walk Forward/Back/Left/Right、Idle、Run Forward… を水平+垂直速度からブレンドします。Unity は Gradient Band Interpolation(Simple)または Triangulation(Freeform)を使います。各クリップはパラメータ空間の (X, Y) 座標を持ちます。

Normalized time が重要です: クリップ長が違うとき、Unity は時間を 正規化 して位相を揃えます——歩きが 1 秒、走りが 0.6 秒でも右足のステップが揃います。モーションごとに Time Scale を有効にして再生速度を制御できます。

1D と 2D の Blend Tree

1D Blend (Speed)

Idle

0

Walk

2

Walk

4

Run

6

Threshold: Idle=0 · Walk=2 · Run=6

2D Blend (Velocity X/Z)

↑ Fwd
↓ Bwd
←
→
I
F
B
L
R

ハンズオン手順

1

State に Blend Tree を作成

Animator ウィンドウ → 右クリック → Create State → From New Blend Tree。または State をダブルクリック → Add Motion → New Blend Tree。

2

Blend Tree のタイプを選ぶ

Inspector → Blend Type: 単純な速度なら 1D、8 方向移動なら 2D Simple Directional。

3

Parameter を割り当てて Motion を追加

1D: Float Parameter(Speed)を選ぶ。(+) → Add Motion → Animation Clip をドロップ。クリップごとに Threshold を設定。

4

Animator ウィンドウでプレビュー

再生中: Animator ウィンドウの Parameter スライダーをドラッグし、モデル上でライブブレンドを確認。

5

スクリプトから駆動

animator.SetFloat("Speed", velocity.magnitude)、2D なら animator.SetFloat("VelocityX", vel.x) もセット。

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

Speed と Direction を調整し、Blend Tree が各クリップにどう重みを付けるか確認しましょう。

Blend weights loading...

コード例

基本

Rigidbody 速度から 2D Blend Tree を駆動——多方向ロコモーション。

using UnityEngine;

public class BlendTreeDriver : MonoBehaviour
{
  Animator anim;
  Rigidbody rb;

  static readonly int VelXHash = Animator.StringToHash("VelocityX");
  static readonly int VelZHash = Animator.StringToHash("VelocityZ");
  static readonly int SpeedHash = Animator.StringToHash("Speed");

  void Awake()
  {
      anim = GetComponent<Animator>();
      rb   = GetComponent<Rigidbody>();
  }

  void Update()
  {
      // World velocity → character local space
      Vector3 localVel = transform.InverseTransformDirection(rb.velocity);

      // 0.1s damping so the blend does not jump
      const float damp = 0.1f;
      anim.SetFloat(VelXHash,  localVel.x, damp, Time.deltaTime);
      anim.SetFloat(VelZHash,  localVel.z, damp, Time.deltaTime);
      anim.SetFloat(SpeedHash, localVel.magnitude, damp, Time.deltaTime);
  }
}

コード例

上級

現在の Blend Tree 重みを読み取り、どのクリップが優位かを知る——足音オーディオに便利。

using UnityEngine;

public class BlendTreeReader : MonoBehaviour
{
  Animator anim;

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

  // List clips currently blending and their weights
  void Update()
  {
      AnimatorClipInfo[] clips = anim.GetCurrentAnimatorClipInfo(0);

      foreach (var info in clips)
      {
          Debug.Log($"{info.clip.name}: {info.weight:F2}");
      }

      // Dominant clip (weight > 0.5) → pick a footstep volume
      string dominant = "Idle";
      float maxW = 0f;
      foreach (var info in clips)
      {
          if (info.weight > maxW)
          {
              maxW = info.weight;
              dominant = info.clip.name;
          }
      }

      float footVol = dominant.Contains("Run") ? 1f :
                      dominant.Contains("Walk") ? 0.6f : 0f;
  }
}

📌 要点

  • ▸1D: float 1 つ(速度)でブレンド、2D: float 2 つ(X, Z)
  • ▸Threshold: クリップの weight=1 になるパラメータ値
  • ▸SetFloat に damping を付けてブレンドのジッタを防ぐ
  • ▸InverseTransformDirection でローカル空間へ変換
  • ▸GetCurrentAnimatorClipInfo でライブ重みを読む

⚠️ よくあるミス

  • ❌ Walk/Run ブレンド中に足が滑る

    Walk と Run クリップのルートモーション速度が違う

    ✅ Blend Tree で "Adjust Time Scale" を有効にして速度を正規化

  • ❌ 2D Blend Tree が斜め方向でおかしい

    45° 方向用のクリップがない

    ✅ 斜めクリップを追加するか、"2D Freeform Cartesian" を使う