Unity's Timeline is a powerful tool for intuitively building cutscenes and character animation sequences. However, when playing the same animation clip, such as walking or running, in a loop on the timeline, or when multiple clips are connected and played continuously, have you ever experienced the phenomenon (animation drift) in which the position of the character's feet and the center of the body gradually shift each time the loop is repeated, and the character deviates significantly from the original route? In this article, we will thoroughly explain the technical background of this position misalignment bug, an example that is easy to understand even for beginners, and the setting flow and specific C# control code to completely eliminate the misalignment.
1. Understand with an analogy: ``Looseness of a treadmill'' and ``minor difference in stride length''
In order to intuitively understand positional deviation (drift) during loop playback, let's use the analogy of ``a runner running on a treadmill''.
The runner (character) continues to run on the treadmill with exactly the same stride length and timing. If the speed at which the machine's belt conveyor moves backward and the speed at which the runner moves forward are exactly the same, the runner's position will not shift even one millimeter and will always remain in the center of the machine. This is the correct loop animation state.
However, suppose that every time the machine's belt loops (one revolution), there is a small bug (such as rounding off fractions in calculations or forgetting to return to the initial position), such as ``The belt slips just 0.1 mm, causing the runner to move too far forward.'' If you just take one step, a difference of 0.1 mm will not be visible to the naked eye. But what happens if we repeat this loop 100, 1000 times? Initially, the deviation is 0.1 mm, but it accumulates to 1 cm, then 10 cm, then 1 meter, and eventually the runner either collides with the front of the machine or ends up falling over.
Animation drift in Timeline is exactly the same. If the position coordinates of the character's reference point (Root) are not completely "corrected/reset" at the moment when the animation clip ends and the next loop (or the next clip) is switched, minute discrepancies between frames and errors in extrapolation calculations will accumulate with each loop, and the character's position will visibly slide on the screen.
2. Root cause: Root Motion accumulation and extrapolation
Technically, the factors that cause this bug can be mainly summarized into the following two points.
- Root Motion accumulation and failure to reset: Root motion is a function that dynamically adds the amount of movement within the animation (displacement of the center of gravity) to Unity's
Transformcoordinates. When a clip loops on the Timeline, the Animator needs to ``return the center of gravity to the initial position of the clip because the animation has completed one rotation.'' However, if the Timeline's evaluation system and the Animator's updates do not mesh correctly, the initial position to be returned to will continue to be added to the final position before the loop, and the displacement will accumulate. - Incorrect Post-Extrapolation settings: Extrapolation settings define how the character's Transform behaves in the gap between the end of a clip and the start of the next clip, or the smallest frame at the seam of a loop. If this is incorrect, the Transform will be stretched at implicit frames outside the clip, causing small drifts.
3. Practical solution steps and configuration steps
Standard configuration steps and complete scripting control method to eliminate animation drift.
Step 1: Set the Track Offsets appropriately (most important)
In the Timeline editor, right-click the header of the target Animation Track or expand the Inspector and set the Track Offsets property.
- Apply Scene Offsets: Starts the character's initial position relative to the current Transform coordinates placed in the scene. Ideal for looping manually placed characters.
- Apply Transform Offsets: Specify the starting coordinates and angle strictly on the timeline side. This is powerful when you want to clamp the starting position to the same value each time in cutscenes, etc., to prevent accumulation errors.
- Auto (default): Unity will automatically determine this, but the determination may be incorrect during loop processing, so it is strongly recommended to explicitly specify one of the above.
Step 2: Optimize Clip Extrapolation
Select the animation clip you want to loop on the Timeline and adjust the Animation Playable Asset > Clip Extrapolation in the Inspector.
- Pre-Extrapolation: Set to `Hold` or `None`.
- Post-Extrapolation: Set to `Hold` or `None`.
*If this is set to `Continue` (keep interpolating while maintaining the moving speed of the parameter without looping), extra movement inertia will be added to the Transform each time the end of the clip is crossed, causing a slipping drift.
Step 3: Separate movement control using C# script (reliable workaround)
Since the link between root motion and Timeline is likely to break down due to complex game progress, it is practically the most robust to separate the design by "Animation is solely for playback (no Transform movement), and coordinate movement is completely controlled from C# script." Turn off Apply Root Motion in Animator and attach the script below to fully control the physics behavior.
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>();
// Subscribe to Timeline play/stop events
if (director != null)
{
director.played += OnTimelinePlayed;
director.stopped += OnTimelineStopped;
}
}
void OnTimelinePlayed(PlayableDirector pd)
{
isPlayingTimeline = true;
// Turn off root motion and switch to script control
animator.applyRootMotion = false;
}
void OnTimelineStopped(PlayableDirector pd)
{
isPlayingTimeline = false;
}
void Update()
{
// Physically move forward at a constant speed only during timeline playback
if (isPlayingTimeline)
{
// Move towards the character's forward vector at a constant frame-independent speed
transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);
}
}
void OnDestroy()
{
if (director != null)
{
director.played -= OnTimelinePlayed;
director.stopped -= OnTimelineStopped;
}
}
}
4. Troubleshooting Checklist
If the character still slides out of alignment even after reviewing the settings, please check the following items.
- Loop Pose settings for animation clips: Select the asset (.fbx) in the Project window and check the "Loop Time" and "Loop Pose" checkboxes in the "Rig/Animation" tab of the inspector. If Loop Pose is OFF, the poses of the first and last frames of the animation will not match, causing stuttering and misalignment at the moment of the loop.
- Applying Bake Into Pose: Turn on "Bake Into Pose" for "Root Transform Position (Y)" and "Root Transform Position (XZ)" in the Animation Inspector to forcibly bake (fix) the height of the center of gravity and the reflection in the front, back, left, and right physical coordinates into the animation.