AnimationScriptPlayable cannot strip the velocity of its input

I tried to input an AnimationClipPlayable into an AnimationScriptPlayable and set the AnimationStream.velocity to Vector3.zero in the AnimationScriptPlayable.ProcessRootMotion method to remove the displacement in the AnimationClip. However, this approach did not work as expected. Although AnimationStream.velocity did become Vector3.zero, the character still moved, and in the OnAnimatorMove method, Animator.velocity was also not Vector3.zero.

Why are AnimationStream.velocity and Animator.velocity not equal (without any animation blending)?

What is the relationship between the RootMotion generated in the AnimationClip and AnimationStream.velocity ? Is it that the RootMotion in the clip is converted to velocity and carried by AnimationStream to be passed up the Playable tree? Or is there additional data that caused me to change AnimationStream.velocity but not affect the final RootMotion?

If the approach of handling RootMotion with AnimationScriptPlayable does not work, do I need to handle RootMotion in OnAnimatorMove instead? In this case, should I directly extract Animator.velocity , multiply it by deltaTime , and add it to the Transform? If our character uses Rigidbody , should we modify Rigidbody.velocity instead of modifying Transform?

Here is the test code (also attached by file):

using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
using UnityEngine.UI;

// AnimationScriptPlayable cannot strip the velocity and angularVelocity of its input
// See comment in `AnimJobTest.OnAnimatorMove`

[RequireComponent(typeof(Animator))]
public class AnimJobTest : MonoBehaviour
{
    public AnimationClip clip;
    public Text text;

    private Animator _animator;
    private PlayableGraph _graph;
    private Vector3 _prevPos;


    // Window/Analysis/PlayableGraph Monitor
    // https://github.com/SolarianZ/UnityPlayableGraphMonitorTool

    private void Start()
    {
        _animator = GetComponent<Animator>();
        _graph = PlayableGraph.Create(nameof(AnimJobTest));
        _graph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);

        var animClipPlayable = AnimationClipPlayable.Create(_graph, clip);

        var animScriptPlayableA = AnimationScriptPlayable.Create(_graph, new MyAnimJobA());
        animScriptPlayableA.AddInput(animClipPlayable, 0, 1f);

        var animScriptPlayableB = AnimationScriptPlayable.Create(_graph, new MyAnimJobB());
        animScriptPlayableB.AddInput(animScriptPlayableA, 0, 1f);

        var animOutput = AnimationPlayableOutput.Create(_graph, "AnimOutput", _animator);
        animOutput.SetSourcePlayable(animScriptPlayableB);

        _graph.Play();
    }

    // private void OnAnimatorMove()
    // {
    //     // Theoretically, the `FINAL_VEL` should be zero, because it has been stripped in `MyAnimJobA.ProcessRootMotion`,
    //     // and in `MyAnimJobB.ProcessRootMotion` , the `JOB_B_VEL_0` is indeed zero.
    //     // However, the `FINAL_VEL` is actually a non-zero value, close to the `JOB_A_VEL_1`.
    //
    //     // Only used to view values during breakpoint debugging
    //     var velocity = _animator.velocity; // FINAL_VEL
    //     var deltaPosition = _animator.deltaPosition;
    //     var rootPosition = _animator.rootPosition;
    //     var targetPosition = _animator.targetPosition;
    //     var pivotPosition = _animator.pivotPosition;
    //     // var bodyPosition = _animator.bodyPosition;
    //
    //     _animator.ApplyBuiltinRootMotion();
    // }

    private void LateUpdate()
    {
        var currPos = transform.position;
        var posChanged = (_prevPos - currPos).sqrMagnitude > Mathf.Epsilon;
        _prevPos = currPos;

        text.text = posChanged ? "Has Motion" : "No Motion";
        text.color = posChanged ? Color.red : Color.green;
    }

    private void OnDestroy()
    {
        if (_graph.IsValid()) _graph.Destroy();
    }
}

public struct MyAnimJobA : IAnimationJob
{
    public void ProcessRootMotion(AnimationStream stream)
    {
        // var input = stream.GetInputStream(0);
        // var inputVelocity = input.velocity;
        // var inputPosition = input.rootMotionPosition;

        // Only used to view values during breakpoint debugging
        var velocity = stream.velocity; // JOB_A_VEL_0
        var position = stream.rootMotionPosition;

        stream.velocity = Vector3.zero;
        stream.angularVelocity = Vector3.zero;

        // Only used to view values during breakpoint debugging
        var newVelocity = stream.velocity; // JOB_A_VEL_1
        var newPosition = stream.rootMotionPosition;
    }

    public void ProcessAnimation(AnimationStream stream)
    {
    }
}

public struct MyAnimJobB : IAnimationJob
{
    public void ProcessRootMotion(AnimationStream stream)
    {
        // var input = stream.GetInputStream(0);
        // var inputVelocity = input.velocity;
        // var inputPosition = input.rootMotionPosition;

        // Only used to view values during breakpoint debugging
        var velocity = stream.velocity; // JOB_B_VEL_0
        var position = stream.rootMotionPosition;
    }

    public void ProcessAnimation(AnimationStream stream)
    {
    }
}

8866611--1210188--AnimJobTest.cs (3.89 KB)

The topology of the PlayableGraph:

8866614--1210194--upload_2023-3-10_20-20-23.png

A possable solution: https://discussions.unity.com/t/911262

Unity has confirmed that this is a bug: https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-36098