Animator Layerとは?

Animator Layerは、Animator Controllerの中で複数のステートマシンを重ねて管理する仕組みです。

このAnimator Layerと、後述するAvatar Maskを組み合わせると、

「下半身は走るアニメーション、上半身は武器を構えるアニメーション」のように、

体の部位ごとに異なるアニメーションを同時に再生できるようになります。

なぜLayerが必要なのか

「走りながら武器を構える」「歩きながら手を振る」といった、複数の動作を同時に再生したい場面は多いと思います。

Layerを使わない場合、Animator Controllerは1つのステートマシンしか持てません。

そのため、「走るアニメーション」の再生中に「武器を構えるアニメーション」を再生すると、走りが完全に上書きされて構えポーズになってしまいます。

Layerを使えば、ステートマシンを複数持てるようになるので、それぞれが並列に動作します。

  • Base Layer(レイヤー0): 走る / 歩く / 止まるなどの下半身アニメーション
  • UpperBody Layer(レイヤー1): 武器を構える / 素手の上半身アニメーション

こうすることで、下半身の走りを維持したまま上半身だけ構えポーズに切り替える、といった制御が可能になります。

Layerの作成と設定

Layerの追加手順

  1. Animator ウィンドウ左上の Layers タブを選択
  2. + ボタンをクリックして新しいLayerを追加
  3. Layer名を入力(例:UpperBody
  4. 歯車アイコンをクリックして詳細設定を開く

Layer設定項目

Weight(重み)

LayerがBase Layerに対してどれだけの影響を与えるかを 01 で設定します。

  • 0: アニメーションが適用されない
  • 1: アニメーションが完全に適用される

新しく追加したLayerは初期値が 0 になっているので、アニメーションを適用したい場合は 1.0 に変更しておきましょう。

Blending(ブレンドモード)

アニメーションをどのように混合するかを設定します。

モード説明用途
Override下のLayerのアニメーションを上書きする通常の上半身/下半身の分離
Additive下のLayerのアニメーションに加算する呼吸や震えなどの微細な動き

Sync(同期)

他のLayerとステートを同期させる設定です。特定のLayerのステートマシンの構造を複製したい場合に使用します。

IK Pass

IK(インバースキネマティクス)を使用する場合にONにします。OnAnimatorIK() コールバックがこのLayer用に呼ばれるようになります。

Avatar Maskとは?

Avatar Maskは、アニメーションを体のどの部位に適用するかを定義するアセットです。

Layerと組み合わせることで、「上半身だけ」「頭だけ」といった形で、特定の部位にだけアニメーションを適用できるようになります。

ヒューマノイド用Avatar Mask

ヒューマノイドリグを持つキャラクターでは、体のパーツ単位でマスクを設定できます。

利用可能なパーツ:

  • Body(胴体)
  • Head(頭・顔)
  • Left Arm / Right Arm(左右の腕)
  • Left Hand / Right Hand(左右の手)
  • Left Leg / Right Leg(左右の脚)
  • Left Foot / Right Foot(左右の足)

Transform用Avatar Mask

ジェネリックリグ(非ヒューマノイド)では、Transformベースでマスクを設定します。特定のボーンを選択して適用範囲を指定します。

Avatar Maskの作成手順

  1. Project ウィンドウで右クリック
  2. Create > Avatar Mask を選択
  3. ファイル名を入力(例:UpperBodyMask
  4. Inspector で適用したい部位を選択

ヒューマノイドの設定例(上半身のみ):

  • Body、Head、Left Arm、Right Arm、Left Hand、Right Hand → 緑色(有効)
  • Left Leg、Right Leg、Left Foot、Right Foot → 赤色(無効)

LayerへのAvatar Maskの適用

  1. Layers タブで対象Layerの歯車アイコンをクリック
  2. Mask フィールドに作成した Avatar Mask をドラッグ&ドロップ

これで、このLayerで再生されるアニメーションは指定した部位にのみ適用されます。

Layer Weightのスクリプト制御

基本的な重みの設定

using UnityEngine;

public class LayerWeightController : MonoBehaviour
{
    private Animator _animator;

    // レイヤーインデックス(Base Layerが0)
    private const int UpperBodyLayerIndex = 1;

    private void Start()
    {
        _animator = GetComponent<Animator>();
    }

    // 上半身レイヤーの重みを即時変更
    public void SetUpperBodyWeight(float weight)
    {
        _animator.SetLayerWeight(UpperBodyLayerIndex, weight);
    }

    // 現在の重みを取得
    public float GetUpperBodyWeight()
    {
        return _animator.GetLayerWeight(UpperBodyLayerIndex);
    }
}

重みを徐々に変化させる(フェードイン/アウト)

武器を構えるときにアニメーションを滑らかに切り替えるには、重みを徐々に変化させます。

using System.Collections;
using UnityEngine;

public class WeaponLayerController : MonoBehaviour
{
    private Animator _animator;
    private const int UpperBodyLayerIndex = 1;

    private void Start()
    {
        _animator = GetComponent<Animator>();
        // 初期状態:上半身レイヤーを無効化
        _animator.SetLayerWeight(UpperBodyLayerIndex, 0f);
    }

    // 武器を構える(上半身レイヤーをフェードイン)
    public void AimWeapon()
    {
        StartCoroutine(FadeLayerWeight(UpperBodyLayerIndex, 1.0f, 0.3f));
    }

    // 武器をしまう(上半身レイヤーをフェードアウト)
    public void LowerWeapon()
    {
        StartCoroutine(FadeLayerWeight(UpperBodyLayerIndex, 0f, 0.3f));
    }

    private IEnumerator FadeLayerWeight(int layerIndex, float targetWeight, float duration)
    {
        float startWeight = _animator.GetLayerWeight(layerIndex);
        float elapsed = 0f;

        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            float t = Mathf.Clamp01(elapsed / duration);
            _animator.SetLayerWeight(layerIndex, Mathf.Lerp(startWeight, targetWeight, t));
            yield return null;
        }

        // 最終値を正確に設定
        _animator.SetLayerWeight(layerIndex, targetWeight);
    }
}

実践例:上半身・下半身の分離

走りながら武器を構える動作の完全な実装例です。

構成

Layer構成:

  • Base Layer(重み:1.0): Idle / Walk / Run
  • UpperBody Layer(重み:0〜1): Unarmed / Aim

Avatar MaskUpperBodyMaskを使い、頭・胴体・両腕のみを有効にしています。

Animator Controllerの設定

Base Layerのステートマシンは次のようになります。

Base Layer:

Idle ←→ Walk ←→ Run
[Speed < 0.1] [Speed > 0.1] [Speed > 5.0]

UpperBody Layer(Avatar Mask: UpperBodyMask)のステートマシンはこちらです。

Unarmed → Aim
[IsAiming = true]

Aim → Unarmed
[IsAiming = false]

実装スクリプト

using System.Collections;
using UnityEngine;

public class CharacterCombatAnimator : MonoBehaviour
{
    [SerializeField] private float _walkSpeed = 3f;
    [SerializeField] private float _runSpeed = 8f;

    private Animator _animator;
    private Rigidbody _rigidbody;

    private const int UpperBodyLayerIndex = 1;
    private const float LayerFadeDuration = 0.2f;

    // Animatorパラメータのハッシュ(パフォーマンス最適化)
    private readonly int _speedHash = Animator.StringToHash("Speed");
    private readonly int _isAimingHash = Animator.StringToHash("IsAiming");

    private bool _isAiming = false;

    private void Start()
    {
        _animator = GetComponent<Animator>();
        _rigidbody = GetComponent<Rigidbody>();

        // 初期状態では上半身レイヤーを無効化
        _animator.SetLayerWeight(UpperBodyLayerIndex, 0f);
    }

    private void Update()
    {
        HandleMovement();
        HandleAiming();
    }

    private void HandleMovement()
    {
        // 入力から移動方向を算出
        float horizontal = Input.GetAxis("Horizontal");
        float vertical = Input.GetAxis("Vertical");
        Vector3 direction = new Vector3(horizontal, 0f, vertical).normalized;

        float speed = 0f;
        if (direction.magnitude > 0.1f)
        {
            // Shiftキーでダッシュ
            speed = Input.GetKey(KeyCode.LeftShift) ? _runSpeed : _walkSpeed;
            // Rigidbodyで移動させる
            _rigidbody.MovePosition(transform.position + direction * speed * Time.deltaTime);
        }

        // 速度をAnimatorに渡してアニメーションを切り替え
        _animator.SetFloat(_speedHash, speed);
    }

    private void HandleAiming()
    {
        // 右クリックで構え切り替え
        if (Input.GetMouseButtonDown(1))
        {
            _isAiming = !_isAiming;
            _animator.SetBool(_isAimingHash, _isAiming);

            float targetWeight = _isAiming ? 1.0f : 0f;
            StartCoroutine(FadeLayerWeight(UpperBodyLayerIndex, targetWeight, LayerFadeDuration));
        }
    }

    private IEnumerator FadeLayerWeight(int layerIndex, float targetWeight, float duration)
    {
        float startWeight = _animator.GetLayerWeight(layerIndex);
        float elapsed = 0f;

        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            float t = Mathf.Clamp01(elapsed / duration);
            _animator.SetLayerWeight(layerIndex, Mathf.Lerp(startWeight, targetWeight, t));
            yield return null;
        }

        _animator.SetLayerWeight(layerIndex, targetWeight);
    }
}

AdditiveブレンドでLayerを使う

Additive モードでは、下のLayerのアニメーションに加算する形でアニメーションを合成します。

呼吸で胸が上下する動き、銃を持った手の揺れ、怪我による動きの歪みなど、

ベースのアニメーションに「ちょっとした味付け」を足したいときに向いています。

OverrideAdditive
動作下のLayerを上書き下のLayerに加算
用途上半身/下半身の分離微細な動きの追加
基準なし元のポーズからの差分
using System.Collections;
using UnityEngine;

public class BreathingLayerController : MonoBehaviour
{
    private Animator _animator;

    // 呼吸レイヤーのインデックス
    private const int BreathingLayerIndex = 2;

    private void Start()
    {
        _animator = GetComponent<Animator>();
        // 通常時は軽い呼吸を適用
        _animator.SetLayerWeight(BreathingLayerIndex, 0.3f);
    }

    // 疲労時に呼吸の重みを上げる
    public void SetExhausted(bool isExhausted)
    {
        float targetWeight = isExhausted ? 1.0f : 0.3f;
        StartCoroutine(FadeLayerWeight(BreathingLayerIndex, targetWeight, 0.5f));
    }

    private IEnumerator FadeLayerWeight(int layerIndex, float targetWeight, float duration)
    {
        float startWeight = _animator.GetLayerWeight(layerIndex);
        float elapsed = 0f;

        while (elapsed < duration)
        {
            elapsed += Time.deltaTime;
            float t = Mathf.Clamp01(elapsed / duration);
            _animator.SetLayerWeight(layerIndex, Mathf.Lerp(startWeight, targetWeight, t));
            yield return null;
        }

        _animator.SetLayerWeight(layerIndex, targetWeight);
    }
}

気をつける点

1. Layerの順序と優先度

Layerは上から下の順に評価され、番号が大きいLayerが優先されます。

  • Layer 0(Base Layer): 最も低い優先度
  • Layer 1: Layer 0より優先
  • Layer 2: Layer 1より優先

設計する際は、より細かい制御をしたいLayerを上位(番号が大きい)に配置します。

2. Avatar MaskとIKの関係

IK Pass をONにしているLayerで Avatar Mask を使う場合、マスクで除外した部位にはIKが適用されない点に注意してください。

3. Layer Weightの初期値

新しく追加したLayerのWeightデフォルト値は 0 です。実行時にスクリプトから設定する場合でも、Inspectorの初期値を 1.0 にしておくと意図しない挙動を防げます。

あるいは Start() でスクリプトから明示的に初期化することも有効です。

private void Start()
{
    _animator = GetComponent<Animator>();

    // 必要なLayerの初期Weightを明示的に設定
    _animator.SetLayerWeight(UpperBodyLayerIndex, 0f); // 最初は無効
}

4. パフォーマンスへの影響

Layerが増えるとアニメーション計算コストが増加します。

  • 必要最小限のLayerに抑える
  • アニメーション適用が不要なときはWeightを 0 に設定してLayerを無効化する

5. パラメータはLayerをまたいで共有される

Animator Controller内のパラメータは全Layerで共通です。別Layerで同じパラメータ名を使い回せます。

まとめ

Animator LayerAvatar Maskを組み合わせることで、体の部位ごとに独立したアニメーション制御が実現できます。

Layerはステートマシンを複数持てる仕組みなので、下半身の移動と上半身のアクションを並列に再生できます。

ブレンドモードはOverride(上書き)とAdditive(加算)の2種類があり、用途に応じて使い分けましょう。

Avatar Maskでアニメーションの適用部位を絞り、Weightで影響度を動的にコントロールするのが基本的な流れです。

よくある活用パターンとしては、以下のような構成が挙げられます。

典型的な活用パターン

LayerAvatar Mask用途
Base Layerなし移動(Idle / Walk / Run)
UpperBody上半身のみ武器構え・攻撃
Face頭のみ表情アニメーション
Breathing胴体(Additive)呼吸の微細な動き

まずは上半身と下半身の分離から試してみてください。Layerの仕組みが体感で掴めるはずです。