Issues with applying root motion

I’m trying to apply root motion for attack animations, but not other animations.

Doing debug logs, my anim.deltaPosition is always 0,0,0, and my root motion is always off, and I can’t figure out why.

Here is my StateManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using InControl;

namespace KM
{
    public class StateManager : MonoBehaviour
    {
        [Header("Init")]
        public GameObject activeModel;

        [Header("Inputs")]
        public float vertical;
        public float horizontal;
        public float moveAmount;
        public Vector3 moveDir;
        public bool l1, l2, r1, r2;
        public bool twoHanded;

        [Header("Stats")]
        public float moveSpeed = 2f;
        public float runSpeed = 3.5f;
        public float rotateSpeed = 8f;
        public float toGround = 0.5f;

        [Header("States")]
        public bool onGround;
        public bool run;
        public bool lockOn;
        public bool inAction;
        public bool canMove;
        public bool isTwoHanded;

        [HideInInspector]
        public AnimatorHook a_hook;
        [HideInInspector]
        public Animator anim;
        [HideInInspector]
        public Rigidbody rigid;
        [HideInInspector]
        public float delta;
        [HideInInspector]
        public LayerMask ignoreLayers;

        float _actionDelay;

        public void Init()
        {
            SetupAnimator();

            rigid = GetComponent<Rigidbody>();
            rigid.angularDrag = 999;
            rigid.drag = 4;
            rigid.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;

            a_hook = activeModel.AddComponent<AnimatorHook>();
            a_hook.Init(this);


            gameObject.layer = 8;
            ignoreLayers = ~(1 << 9);

            anim.SetBool("onGround", true);
        }

        void SetupAnimator()
        {
            if (activeModel == null)
            {
                anim = GetComponentInChildren<Animator>();
                if (anim == null)
                {
                    Debug.Log("You're a dingus, there's no model");
                }
                else
                {
                    activeModel = anim.gameObject;
                }
            }

            if (anim == null)
                anim = activeModel.GetComponent<Animator>();


            anim.applyRootMotion = false;
        }

        public void FixedTick(float d)
        {
            delta = d;

            DetectAction();

            if (inAction)
            {
                _actionDelay += delta;
                if(_actionDelay > 0.3f)
                {
                    inAction = false;
                    _actionDelay = 0;
                }
                else
                {
                    return;
                }
            }

            canMove = anim.GetBool("can_move");

            if (!canMove)
            {
                return;
            }

            anim.applyRootMotion = false;

            rigid.drag = (moveAmount > 0 || onGround == false) ? 0 : 4;

            float targetSpeed = moveSpeed;
            if (run)
                targetSpeed = runSpeed;

            if(onGround)
            rigid.velocity = moveDir * (targetSpeed * moveAmount);

            if (run)
                lockOn = false;

            if (!lockOn)
            {
                Vector3 targetDir = moveDir;
                targetDir.y = 0;
                if (targetDir == Vector3.zero)
                    targetDir = transform.forward;
                Quaternion tr = Quaternion.LookRotation(targetDir);
                Quaternion targetRotation = Quaternion.Slerp(transform.rotation, tr, delta * moveAmount * rotateSpeed);
                transform.rotation = targetRotation;
            }

            HandleMovementAnimations();
        }

        public void DetectAction()
        {
            if (!canMove)
                return;

            if (!r1 && !r2 && !l1 && !l2)
                return;

            string targetAnim = null;

            if (r1)
                targetAnim = "oh_attack_1";
            if (r2)
                targetAnim = "oh_attack_3";
            if (l1)
                targetAnim = "oh_attack_1";
            if (l2)
                targetAnim = "oh_attack_2";

            canMove = false;
            inAction = true;
            anim.CrossFade(targetAnim, 0.2f);
        }

        public void Tick(float d)
        {
            delta = d;
            onGround = OnGround();

            anim.SetBool("onGround", onGround);
        }

        void HandleMovementAnimations()
        {
            anim.SetBool("run", run);
            anim.SetFloat("vertical", moveAmount, 0.4f, delta); //Only vertical because no lock on mode
        }

        public bool OnGround()
        {
            bool r = false;

            Vector3 origin = transform.position + (Vector3.up * toGround);
            Vector3 dir = -Vector2.up;
            float dis = toGround + 0.3f;

            RaycastHit hit;

            if(Physics.Raycast(origin, dir, out hit, dis, ignoreLayers))
            {
                r = true;
                Vector3 targetPosition = hit.point;
                transform.position = targetPosition;
            }

            Debug.DrawRay(origin, dir, Color.red);

            return r;
        }

        public void HandleTwoHanded()
        {
            anim.SetBool("two_handed", isTwoHanded);
        }
    }
}

Here is my AnimatorHook:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace KM
{
    public class AnimatorHook : MonoBehaviour
    {
        Animator anim;
        StateManager states;

        public void Init(StateManager st)
        {
            states = st;
            anim = st.anim;
        }

        void OnAnimatorMove()
        {
            Vector3 DeltaPosition = anim.deltaPosition;
            Vector3 OriginalVelocity = states.rigid.velocity;

            Vector3 SetVelocity = DeltaPosition / Time.deltaTime;

            //Add new velocity
            states.rigid.velocity = SetVelocity;
        }

        void OpenDamageColliders()
        {

        }

        void CloseDamageColliders()
        {

        }
    }
}

Any insight appreciated. I see that some things with OnAnimatorMove() seem to have changed, but I can’t for the life of me get this working.

OnAnimatorMove ignores the applyRootMotion flag, so if you want it to stop applying movement then its up to you to set the velocity to zero.

But if you are never getting any delta position, then the problem is probably one of the options in the animation import settings.

1 Like

Here’s an example of the settings all the attack anims use. If I check root transform position for XZ, they do use root motion during the attack, but then after the attack they slide back to their original position, which is what the animator hook was to account for based on my understanding of it, I want it to end with the rigidbody and capsule collider where the animation ended.

Additionally, my set up of GOs is
PlayerController parent with rigidbody, capsule collider, statecontroller

Character itself with skinned mesh renderer and animator

1 Like

I’m not sure then. That looks like the settings I would use, though I don’t use root motion much.

I know this an old post but did you ever find the solution for this?

1 Like

Late response but these are the settings that worked for me when facing a similar issue.