unity-animation について
Mecanim、Animator Controller、Blend Tree、Timeline、アニメーションスクリプトを扱うUnity開発者向けのスキルです。設定手順とコード例で、各コンポーネントとワークフローを説明します。
主な機能
- 01Mecanimの構成要素とアニメーション種別
- 02Animatorの状態・遷移・パラメーター
- 03Blend Treeの設定
- 04Avatarの設定とリターゲティング
- 05Root Motionとアニメーションイベント
- 06Timelineとアニメーションスクリプト
活用シーン
- 01キャラクターの移動と状態遷移の構築
- 02Humanoidリグ間でのアニメーション転送
- 03再生中のアニメーションイベント発火
- 04Timelineによるシネマティックシーケンス作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nice-wolf-studio/ unity-claude-skills --skill unity-animationインストール後、Claude Code で/unity-animationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/animation-clips.mdanimator-controller.mdtimeline.md詳細
Unity のアニメーションシステム
アニメーションシステムの概要
Unity の Mecanim アニメーションシステムは、相互に連携する次の 3 つのコンポーネントを基盤としています。
- アニメーションクリップ -- 動作の単位(待機、歩行、走行)
- Animator Controller -- クリップを状態と遷移のフローチャートに整理するステートマシン
- Avatar システム -- リターゲティング用に、ヒューマノイドキャラクターのスケルトンを共通の内部形式にマッピングします
Animator コンポーネントは GameObject にアタッチされ、再生に必要な Animator Controller と Avatar アセットの両方を参照します。
アニメーションの種類
- Humanoid -- Avatar の設定が必要です。異なるキャラクターリグ間のリターゲティングをサポートします。Generic より CPU 負荷が 15~20% 高くなります
- Generic -- 特定の階層にある Transform または MonoBehaviour のプロパティをアニメーション化します。異なる階層間では転用できません
- Legacy -- 古い Animation コンポーネントです。シンプルな単発アニメーションや UI アニメーションに使用します
Animator Controller
Animator Controller アセットは、キャラクターまたはアニメーション化された GameObject 用の Animation Clip と Transition をまとめます。
作成方法: Project ウィンドウで右クリック > Create > Animator Controller
主なコンポーネント
- State -- 各ステートは、関連付けられた Animation Clip または Blend Tree を再生します
- Transition -- ステートマシンがステート間を切り替える方法とタイミングを定義します
- Parameter -- スクリプトから設定して遷移を制御する変数(Float、Int、Bool、Trigger)です
- Layer -- 異なる身体部位やアニメーションの目的に応じて、ステートマシンを分けます
- Sub-State Machine -- 階層的に整理するための、ネストされたステートマシンです
パラメーター
使用できる種類は 4 つです。
| 種類 | 説明 | スクリプトメソッド |
|---|---|---|
| Float | 小数 | SetFloat() / GetFloat() |
| Int | 整数 | SetInteger() / GetInteger() |
| Bool | 真/偽 | SetBool() / GetBool() |
| Trigger | 自動リセットされるブール値 | SetTrigger() / ResetTrigger() |
using UnityEngine;
public class PlayerAnimController : MonoBehaviour
{
Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
// Note: Uses legacy Input Manager for simplicity. See unity-input for the new Input System.
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
bool fire = Input.GetButtonDown("Fire1");
animator.SetFloat("Forward", v);
animator.SetFloat("Strafe", h);
animator.SetBool("Fire", fire);
}
void OnCollisionEnter(Collision col)
{
if (col.gameObject.CompareTag("Enemy"))
{
animator.SetTrigger("Die");
}
}
}
Animator Override Controller
Animator Controller の構造、パラメーター、ロジックを維持したまま、アニメーションクリップを置き換えます。同じステートマシンを共有しながら、異なるクリップを使用する複数のキャラクターに便利です。
重要: Override Controller を使用する場合は、遷移の終了時間を秒ではなく正規化時間で設定してください。オーバーライドするクリップの長さが異なると、終了時間が無視されることがあります。
レイヤー
- Override モード -- 前のレイヤーのアニメーションを置き換えます
- Additive モード -- 前のレイヤーのアニメーションに重ねてアニメーションを追加します
- Avatar Mask -- レイヤーの適用対象を特定の身体部位(例:上半身のみ)に制限します
- Synced Layers -- 異なるクリップでステートマシンの構造を再利用します
ステートマシンと遷移
ステート
Animator Controller の各ステートは、個別の動作を表します。特殊なステートには、次のものがあります。
- Entry -- デフォルトの開始点
- Any State -- 現在のステートにかかわらず遷移します
- Exit -- 現在のステートマシンまたはサブステートマシンを終了します
遷移
遷移は、ステート間のブレンド方法を定義します。
| 設定 | 説明 |
|---|---|
| Has Exit Time | 正規化時間(例:0.75 = 75% 完了)で遷移を開始します |
| Transition Duration | ブレンド期間です。秒単位(Fixed Duration)または遷移元ステートに対する割合で指定します |
| Transition Offset | 遷移先ステートの再生開始位置です(0.5 = 中間地点) |
| Conditions | パラメーターに基づくルールです。すべてを同時に満たす必要があります |
| Interruption Source | 遷移を中断できる対象です。None、Current State、Next State、またはそれらの組み合わせを指定します |
| Ordered Interruption | 遷移の判定を現在の遷移で停止するか、有効な遷移が見つかるまで続けるかを指定します |
Has Exit Time と Conditions の両方が設定されている場合、Unity は終了時間に達した後にのみ条件を確認します。
ステートマシンの動作
StateMachineBehaviour を継承するスクリプトをステートにアタッチします。コールバック:OnStateEnter、OnStateUpdate、OnStateExit、OnStateMove、OnStateIK。すべて (Animator animator, AnimatorStateInfo stateInfo, int layerIndex) を受け取ります。
public class AttackState : StateMachineBehaviour
{
public AudioClip attackSound;
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
AudioSource.PlayClipAtPoint(attackSound, animator.transform.position);
}
override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
// Cleanup when leaving state
}
}
ブレンドツリー
ブレンドツリーは、パラメーター値に基づいて複数の類似したアニメーションを滑らかにブレンドします。時間をかけて個別のステート間を切り替えるトランジションとは異なります。
重要な要件: アニメーションの性質とタイミングが類似している必要があります。接地する足の位置が正規化時間上で揃っている必要があります(例:左足が 0.0、右足が 0.5)。
ブレンドタイプ
- 1D -- 1 つのパラメーターでブレンドを制御します(例:歩行/走行の速度)
- 2D Simple Directional -- 2 つのパラメーターを使い、各方向に 1 つのモーションを設定します
- 2D Freeform Directional -- 2 つのパラメーターを使い、各方向に複数のモーションを設定します
- 2D Freeform Cartesian -- 2 つのパラメーターを使い、方向を表さないモーションを設定します
- Direct -- 各モーションに独自のウェイトパラメーターを設定します(表情アニメーション)
作成方法: Animator Controller 内で右クリックし、Create State > From New Blend Tree を選択します。ダブルクリックしてグラフを開きます。Inspector から子モーションを追加します。
// Driving a locomotion blend tree from script
void Update()
{
float speed = new Vector3(rb.velocity.x, 0, rb.velocity.z).magnitude;
float direction = Vector3.SignedAngle(transform.forward,
rb.velocity.normalized, Vector3.up);
animator.SetFloat("Speed", speed, 0.1f, Time.deltaTime);
animator.SetFloat("Direction", direction, 0.1f, Time.deltaTime);
}
アバターとヒューマノイドリグ
Avatar システムは、モデルをヒューマノイドとして認識し、アニメーションのリターゲティング用に身体の各部位をマッピングします。
主な概念
- Avatar -- 骨格構造を Unity 内部のヒューマノイド形式にマッピングします
- リターゲティング -- 同じ Avatar マッピングを共有する異なるヒューマノイドリグ間でアニメーションを転送します
- 筋肉の定義 -- ボーン空間ではなく筋肉空間で直感的に制御します
設定
- Project ウィンドウでモデルを選択します
- Import Settings の Rig タブで、Animation Type を Humanoid に設定します
- Avatar のマッピングを設定します(可能な場合、Unity が自動でマッピングします)
- Avatar Configuration ウィンドウでボーンの割り当てを確認し、調整します
ルートモーション
ルートモーションは、アニメーションによる移動を GameObject の Transform に反映します。
アーキテクチャ
- Body Transform -- キャラクターの重心で、ワールド空間のカーブを保持します
- Root Transform -- Body Transform を Y 平面に投影したもので、実行時に各フレームで計算されます
クリップの Inspector 設定
| 設定 | 目的 |
|---|---|
| Bake Into Pose (Rotation) | 向きをボディに保持し、GameObject に回転を適用しません |
| Bake Into Pose (Y) | 垂直方向の動きをボディに保持します。ジャンプ以外では有効にします |
| Bake Into Pose (XZ) | 水平方向の動きをボディに保持します。アイドルクリップではドリフト防止のため有効にします |
| Based Upon | Body Orientation(モーションキャプチャ)、Original(キーフレーム)、Feet(浮き上がりを防止) |
Animator.gravityWeight は Bake Into Pose Position Y によって制御されます。有効の場合は 1、無効の場合は 0 です。
// Custom root motion handling
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class RootMotionController : MonoBehaviour
{
Animator animator;
CharacterController controller;
void Start()
{
animator = GetComponent<Animator>();
controller = GetComponent<CharacterController>();
animator.applyRootMotion = false; // We handle it manually
}
void OnAnimatorMove()
{
// Apply root motion through CharacterController
Vector3 deltaPosition = animator.deltaPosition;
deltaPosition.y -= 9.81f * Time.deltaTime; // Add gravity
controller.Move(deltaPosition);
transform.rotation *= animator.deltaRotation;
}
}
アニメーションイベント
アニメーションイベントは、アニメーションタイムライン上の指定した位置で関数を呼び出します。
パラメーターの種類
- Float -- 数値(例:音量)
- Int -- 整数値
- String -- テキストデータ
- Object -- GameObject または Prefab の参照
設定
- Animation タブで Events セクションを展開します
- 再生ヘッドを目的のフレームに移動します
- Add Event をクリックします
- アタッチされたスクリプトのメソッド名と一致する Function 名を設定します
using UnityEngine;
public class FootstepHandler : MonoBehaviour
{
public AudioClip[] footstepSounds;
public GameObject dustPrefab;
// Called by animation event -- function name must match event
public void PlayFootstep(int footIndex)
{
if (footstepSounds.Length > 0)
{
int clipIndex = Random.Range(0, footstepSounds.Length);
AudioSource.PlayClipAtPoint(footstepSounds[clipIndex], transform.position);
}
}
// Called by animation event passing an Object parameter
public void SpawnEffect(Object effectPrefab)
{
Instantiate((GameObject)effectPrefab, transform.position, Quaternion.identity);
}
}
Timeline
Timeline は、シネマティックコンテンツ、ゲームプレイシーケンス、オーディオシーケンス、パーティクルエフェクトを作成します。パッケージ:com.unity.timeline(v1.8.11、Unity 6.3 対応)。
主なコンポーネント
- Timeline Asset -- トラック、クリップ、およびその配置を定義します
- Timeline Instance -- 特定のシーンオブジェクトにバインドされた実行時インスタンスです
- Playable Director -- Timeline アセットを再生し、トラックをシーンオブジェクトにバインドするコンポーネントです
トラックの種類
| トラック | 目的 |
|---|---|
| Animation | バインドされた GameObject の Animator を制御します |
| Audio | バインドされた AudioSource で AudioClip を再生します |
| Activation | バインドされた GameObject を有効化/無効化します |
| Signal | SignalReceiver を介して特定の時刻にイベントを発生させます |
| Control | サブ Timeline、パーティクルシステム、または他の Playable Director を起動します |
| Playable | Playables API を使用するカスタムトラックです |
機能
- タイムラインでのアニメーションの直接記録
- ヒューマノイドアニメーションのサポート
- アニメーションオーバーライドトラック
- モジュール式の構成に対応したサブタイムライン
アニメーションのスクリプト制御
逆運動学(IK)
要件:ヒューマノイドアバター、レイヤーで IK Pass が有効になっていること。
using UnityEngine;
public class IKController : MonoBehaviour
{
Animator animator;
public bool ikActive = true;
public Transform rightHandTarget;
public Transform lookTarget;
void Start()
{
animator = GetComponent<Animator>();
}
void OnAnimatorIK()
{
if (animator == null || !ikActive) return;
// Look at target
animator.SetLookAtWeight(1f);
animator.SetLookAtPosition(lookTarget.position);
// Right hand IK
animator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1f);
animator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1f);
animator.SetIKPosition(AvatarIKGoal.RightHand, rightHandTarget.position);
animator.SetIKRotation(AvatarIKGoal.RightHand, rightHandTarget.rotation);
}
}
ステートの直接再生
// Play state immediately
animator.Play("Attack");
// Play with normalized time offset (start at 50%)
animator.Play("Attack", 0, 0.5f);
// Crossfade into state over 0.25 seconds
animator.CrossFadeInFixedTime("Run", 0.25f);
// Crossfade using normalized time
animator.CrossFade("Run", 0.1f);
ステート情報の照会
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
if (stateInfo.IsName("Attack"))
{
Debug.Log("Currently attacking");
}
if (stateInfo.normalizedTime >= 1.0f && !animator.IsInTransition(0))
{
Debug.Log("Animation finished");
}
// Use hash for performance
int runHash = Animator.StringToHash("Run");
if (animator.HasState(0, runHash))
{
animator.Play(runHash);
}
ターゲットマッチング
// Match character's hand to a ledge position during climb animation
animator.MatchTarget(ledgePosition, Quaternion.identity, AvatarTarget.RightHand,
new MatchTargetWeightMask(Vector3.one, 0f), 0.1f, 0.4f);
よく使われるパターン
キャラクターの移動コントローラー
using UnityEngine;
[RequireComponent(typeof(Animator))]
public class LocomotionController : MonoBehaviour
{
Animator animator;
static readonly int SpeedHash = Animator.StringToHash("Speed");
static readonly int GroundedHash = Animator.StringToHash("Grounded");
static readonly int JumpHash = Animator.StringToHash("Jump");
void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
float speed = Input.GetAxis("Vertical");
animator.SetFloat(SpeedHash, Mathf.Abs(speed), 0.1f, Time.deltaTime);
animator.SetBool(GroundedHash, IsGrounded());
if (Input.GetButtonDown("Jump") && IsGrounded())
{
animator.SetTrigger(JumpHash);
}
}
bool IsGrounded()
{
return Physics.Raycast(transform.position + Vector3.up * 0.1f, Vector3.down, 0.2f);
}
}
実行時のオーバーライドコントローラーの切り替え
using UnityEngine;
public class WeaponAnimSwap : MonoBehaviour
{
public AnimatorOverrideController swordOverride;
public AnimatorOverrideController bowOverride;
Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
public void EquipSword()
{
animator.runtimeAnimatorController = swordOverride;
}
public void EquipBow()
{
animator.runtimeAnimatorController = bowOverride;
}
}
アンチパターン
| アンチパターン | 問題 | 解決策 |
|---|---|---|
| 毎フレーム文字列パラメーターを使用する | GC の割り当てが発生し、検索が遅くなります | Animator.StringToHash() でハッシュをキャッシュします |
| 非ヒューマノイドオブジェクトにヒューマノイドリグを使用する | CPU に 15~20% の不要な負荷がかかります | 小道具、動物、エフェクトには Generic アニメーションタイプを使用します |
| 共有ルートの下に複数の Animator を配置する | 階層ごとの単一スレッド書き込み制限により、並列処理が妨げられます | 各 Animator に独自のルート GameObject を割り当てます |
| StateMachineBehaviour を多用する | 同期ポイントが発生し、並列評価が妨げられます | SMB コールバックを最小限にし、スクリプト側のロジックを優先します |
| Animator 経由で RectTransform をアニメーション化する | 書き込みの決定性に関する問題が発生します | UI アニメーションには旧式の Animation コンポーネントを使用します |
| 単発アニメーションに複雑なステートマシンを使用する | アイドル中も遷移評価が継続します | 代わりに Animation コンポーネントまたは Playables API を使用します |
Animator.Update() を手動で呼び出す | 並列実行が回避されます | 並列処理を維持したまま手動制御するには PlayableGraph を使用します |
| 遷移の終了時間を正規化された値に設定しない | Override Controller のクリップで遷移がスキップされることがあります | 終了時間には必ず正規化時間を使用します |
コントローラーの切り替え後に Rebind() を呼び出し忘れる | バインディングが古いままになり、再生が正しく行われません | 実行時にコントローラーを変更する際は Rebind() を呼び出します |
| アイドルクリップで Bake Into Pose が無効(XZ)になっている | アイドルアニメーションでドリフトが蓄積します | 静止クリップでは Bake Into Pose XZ を有効にします |
主要 API クイックリファレンス
Animator -- 主要プロパティ
applyRootMotion、speed、updateMode(Normal/AnimatePhysics/UnscaledTime)、cullingMode、deltaPosition、deltaRotation、velocity、isHuman、layerCount、bodyPosition、gravityWeight
Animator -- 主要メソッド
- 再生:
Play(state, layer, time)、CrossFade(state, duration)、CrossFadeInFixedTime(state, duration) - パラメーター:
SetFloat/Int/Bool/Trigger(name, value)、GetFloat/Int/Bool(name)、ResetTrigger(name) - ステートの照会:
GetCurrentAnimatorStateInfo(layer)、GetNextAnimatorStateInfo(layer)、IsInTransition(layer) - IK:
SetIKPosition/Rotation(goal, value)、SetIKPositionWeight/RotationWeight(goal, weight)、SetLookAtPosition(pos) - ユーティリティ:
MatchTarget(...)、GetBoneTransform(bone)、Rebind()、StringToHash(name)、SetLayerWeight(layer, weight)
関連スキル
- unity-foundations -- Unity の基本概念、GameObject、コンポーネント、プロジェクト構成
- unity-scripting -- MonoBehaviour のライフサイクル、C# のパターン、イベントシステム
- unity-graphics -- レンダリング、マテリアル、シェーダー(アニメーション化されたマテリアルプロパティに関連)