Documentation
    Preparing search index...

    Interface MixerRootMotionOptions

    Root motion for one playback (§19, plan decision P7-5) — the MVP tier: translation only.

    The designated track is removed from normal per-track binding. Its path is never resolved against the mixer's target, its absolute values never write a bound property, and it needs no property to exist at all: "rootMotion" is a perfectly good path for a channel that exists only to be differenced. What the mixer does with it instead is add the change in its sampled value across each AnimationMixer.advance to target.transform.position.

    Let S(t) be the track sampled at clip-local time t, d the clip duration, and let one advance move elapsed time from a to b (a <= b). Inside a single iteration the answer is the plain difference S(l(b)) − S(l(a)), where l is the clip-local time of an elapsed time.

    A wrap is where naive differencing fails: l jumps from d back to 0, so S(l(b)) − S(l(a)) would subtract a whole stride and teleport the node backwards. The honest quantity is the distance travelled along the clip, so the crossing is split at the seam — the motion left in the outgoing iteration plus the motion already made in the incoming one:

    (S(d) − S(l(a)))  +  (S(l(b)) − S(0))
    

    An advance long enough to swallow whole iterations adds one full stride S(d) − S(0) for each of them, which generalizes both cases to

    w = iteration(b) − iteration(a)                       // wraps crossed
    w = 0:  S(l(b)) − S(l(a))
    w > 0:  (S(d) − S(l(a))) + (w − 1)·(S(d) − S(0)) + (S(l(b)) − S(0))
    

    iteration and l are the mixer's own loop bookkeeping (the same functions that place the pose), so root motion wraps exactly when playback does — and, because the last iteration's end reads as l = d rather than l = 0 of an iteration that does not exist, the advance that finishes a loop(n) playback contributes the full n-th stride.

    S(l(a)), S(d), and S(0) are all held from the previous advance or frozen at play, so a step costs exactly one new sample and no allocation.

    • AnimationMixer.seek applies no delta at all. It repositions the left endpoint so the next advance differences from the sought time, and nothing more: §16 makes evaluation a pure function of clip time, and a scrub that also moved the character would make scrubbing back and forth a source of displacement. A seek positions; only an advance locomotes.
    • A delta refused by §42 (see below) is dropped, not banked. The left endpoint still advances, so authority granted mid-playback resumes from the clip's current position rather than replaying the motion that was refused — the same "never depended on the writes that were refused" property the pose path has.

    The write lands on MixerRootMotionOptions.target's transform, so that node must be under "animation" authority — independently of whatever node gates the pose, which is often a different one (the pose animates a skeleton under a root the motion moves). A node that is not warns once (deduplicated by warnAuthorityConflict) and its deltas are skipped.

    A vector3 track extracts translation (transform.position.add). A quaternion track extracts rotation: the relative quaternion conjugate(previous) * sampled is multiplied onto transform.rotation (local composition, the same space the translation channel adds in). Other adapter kinds are still rejected — there is no defensible delta for a scalar or a colour.

    interface MixerRootMotionOptions {
        target: Node;
        trackPath: string;
    }
    Index
    target: Node

    The node the extracted translation or rotation moves. Written under §42 "animation" authority; needs no relationship to the mixer's target.

    trackPath: string

    Path of the clip track to difference, matched exactly against AnimationTrackLike.path. The clip must contain exactly one track with this path, and it must be a vector3 or quaternion track.