UnityのTimeline(タイムライン)は、カットシーンの演出やキャラクターのアニメーションシーケンスを直感的に構築するための強力なツールです。しかし、歩行や走行などの同じアニメーションクリップをタイムライン上でループ再生させたり、複数のクリップを繋ぎ合わせて連続再生させたりした際、「ループを繰り返すたびに、キャラクターの足の位置や体の中心が徐々にズレていき、最終的に本来のルートから大きく逸脱してしまう」という現象(アニメーション・ドリフト)に悩まされたことはないでしょうか。本記事では、この位置ズレバグの技術的な背景と、初心者にも分かりやすい例え話、そして完全にズレを解消するための設定フローと具体的なC#制御コードを徹底解説します。

1. 例え話で理解する:『ランニングマシンの緩み』と『微小な歩幅の差』

ループ再生時の位置ズレ(ドリフト)を直感的に理解するために、「ランニングマシン(トレッドミル)で走るランナー」を例えに考えてみましょう。

ランナー(キャラクター)は、ランニングマシンの上で、全く同じ歩幅とタイミングで走り続けています。マシンのベルトコンベアが後ろに下がる速度と、ランナーが前に進む速度が100%完全に一致していれば、ランナーの位置は1ミリもズレることなく、常にマシンの中心に留まり続けます。これが正しいループアニメーションの状態です。

しかし、もしマシンのベルトがループ(1周)するたびに、「ほんの0.1ミリだけベルトの滑りが甘く、ランナーが前に進みすぎてしまう」という小さなバグ(計算の端数切り捨てや、初期位置への戻し忘れ)があったとします。1歩走るだけなら、0.1ミリのズレは目に見えません。しかし、このループを100回、1000回と繰り返していくと、どうなるでしょうか。最初は0.1ミリだったズレが、「1センチ」「10センチ」「1メートル」と累積していき、最終的にはランナーがマシンの前方に衝突するか、はみ出して転倒してしまいます。

Timelineにおけるアニメーション・ドリフトもこれと全く同じです。アニメーションクリップが終了して次のループ(または次のクリップ)に切り替わる瞬間、キャラクターの基準点(Root)の位置座標を完全に「補正・リセット」しなければ、フレーム間の微小なズレや外挿計算の誤差がループのたびに積み重なり、キャラクターの位置が画面上で目に見えてスライドしていくことになります。

2. 根本的な原因:Root Motion の蓄積と外挿(Extrapolation)

技術的に、このバグを引き起こす要因は主に以下の2点に集約されます。

  1. Root Motion(ルートモーション)の累積とリセット不全: ルートモーションとは、アニメーション内の移動量(重心の変位)を、Unityの Transform 座標に動的に足し合わせて移動させる機能です。Timeline上でクリップがループする際、Animatorは「アニメーションが1周したので、重心位置をクリップの初期位置に戻す」必要がありますが、Timelineの評価システムとAnimatorの更新が正しく噛み合っていないと、戻るべき初期位置がループ前の最終位置のまま加算され続け、変位が累積してしまいます。
  2. Post-Extrapolation(後方外挿)の設定不備: クリップの終了後から次のクリップが始まるまでの隙間、あるいはループの継ぎ目の極小のフレームにおいて、キャラクターのTransformがどのように挙動するかを定義するのが外挿設定です。これが不適切だと、クリップ外の暗黙のフレームでTransformが引き伸ばされ、微小なドリフトが発生します。

3. 実務で役立つ解決ステップと設定手順

このアニメーション・ドリフトを解消するための標準的な設定手順と、スクリプトによる完全な制御手法です。

ステップ1:Track Offsets の適切な設定(最も重要)

Timelineエディタ上で、対象の Animation Track のヘッダーを右クリック、またはインスペクターを展開し、Track Offsets プロパティを設定します。

  • Apply Scene Offsets: キャラクターの初期位置をシーン内に配置された現在のTransform座標を基準として開始させます。手動配置されたキャラクターにループをかける場合に最適です。
  • Apply Transform Offsets: タイムライン側で開始座標と角度を厳密に固定指定します。カットシーンなどで開始位置を毎回一定にクランプさせ、蓄積エラーを防止したい場合に強力です。
  • Auto (デフォルト): Unityが自動判別しますが、ループ処理時は判別が狂うことがあるため、上記のいずれかを明示的に指定することを強く推奨します。

ステップ2:Clip Extrapolation の最適化

ループさせるアニメーションクリップをTimeline上で選択し、インスペクターの Animation Playable Asset > Clip Extrapolation を調整します。

  • Pre-Extrapolation(再生開始前の挙動): `Hold` または `None` に設定します。
  • Post-Extrapolation(再生終了後の挙動): `Hold` または `None` に設定します。

※これを `Continue`(ループなしのままパラメータの移動速度を維持して補間し続ける)などに設定していると、クリップの終端を跨ぐたびに余計な移動慣性がTransformに加算され、滑るようなドリフトの原因になります。

ステップ3:C#スクリプトによる移動制御の切り離し(確実な回避策)

ルートモーションとTimelineの連携は、複雑なゲーム進行状況によって破綻しやすいため、「アニメーションはただの再生(Transform移動なし)に専念させ、座標移動はC#スクリプトから完全に制御する」設計に切り離すのが実務上最も頑健です。Animatorの Apply Root Motion をOFFにし、以下のスクリプトをアタッチして物理挙動を完全に制御します。

using UnityEngine;
using UnityEngine.Playables;

[RequireComponent(typeof(Animator))]
public class TimelineMovementController : MonoBehaviour
{
    public PlayableDirector director;
    public float moveSpeed = 3.0f;
    private Animator animator;
    private bool isPlayingTimeline = false;

    void Start()
    {
        animator = GetComponent<Animator>();
        
        // Timelineの再生・停止イベントを購読
        if (director != null)
        {
            director.played += OnTimelinePlayed;
            director.stopped += OnTimelineStopped;
        }
    }

    void OnTimelinePlayed(PlayableDirector pd)
    {
        isPlayingTimeline = true;
        // ルートモーションをOFFにし、スクリプト制御に切り替える
        animator.applyRootMotion = false;
    }

    void OnTimelineStopped(PlayableDirector pd)
    {
        isPlayingTimeline = false;
    }

    void Update()
    {
        // タイムライン再生中のみ、物理的に一定の速度で前方に移動させる
        if (isPlayingTimeline)
        {
            // キャラクターの前方ベクトルに向かって、フレームに依存しない一定速度で移動
            transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
        }
    }

    void OnDestroy()
    {
        if (director != null)
        {
            director.played -= OnTimelinePlayed;
            director.stopped -= OnTimelineStopped;
        }
    }
}

4. トラブルシューティング・チェックリスト

設定を見直してもキャラクターがスライドしてズレる場合は、以下の項目を確認してください。

  • アニメーションクリップのLoop Pose設定: Projectウィンドウでアセット(.fbx)を選択し、インスペクターの「Rig / Animation」タブで「Loop Time」および 「Loop Pose」 チェックボックスがONになっているか。Loop PoseがOFFだと、アニメーションの最初と最後のフレームのポーズが一致せず、ループの瞬間にカクつきやズレが生じます。
  • Bake Into Pose の適用: Animationインスペクターの「Root Transform Position (Y)」および「Root Transform Position (XZ)」の 「Bake Into Pose」 をONにして、重心の高さや前後左右の物理座標への反映を強制的にアニメーション内に焼き込み(固定化)します。