Keeping character grounded when moving with Root Motion

I have a bunch of NPC characters that move with their animation’s root motion, i.e. I control them by setting the animation and letting it run. They are simple enemies that essentially move forward or turn left/right when hitting something.

I also have kinematic rigidbodies attached to them because I need to catch them colliding with various triggers in my scene.

I want to keep them on the ground. I thought this would be trivial:

        void FixedUpdate() {
                RaycastHit hit;
                if (Physics.Raycast(transform.position + Vector3.up, Vector3.down, out hit, 4f, groundLayer)) {
                    m_rigidbody.MovePosition(transform.position + Vector3.down * (hit.distance - 1f));
                }
        }

But root motion and rigidbody MovePosition don’t seem to mix, i.e. depending on how I set things, they are either moving, but flying in the air, or grounded but not moving.

I’ve also tried:

  • animation set to animate physics or not
  • using rigidbody.MovePosition or transform.Translate

What is the proper solution to mix root motion with also controlling the character’s position through script?

MovePosition on a kinematic rigidbody will explicitly set the position, definitely, so it’ll overwrite all of your animation driven movement. I imagine you just need to use a non-kinematic rigidbody with Animate Physics turned on OR only call MovePosition when the animation isn’t playing if that’s possible, but I truthfully don’t have a lot of experience with root motion in general.

Problem is that I do want kinematic (i.e. I don’t want the physics engine to move my NPC) and I specifically want to ground it WHEN it is moving (when it’s not moving, it doesn’t need to check if it stepped off a cliff, right?).

This is just a stab in the dark, but what if you MovePosition after the animation is applied. Maybe instead of setting it in FixedUpdate, maybe OnAnimatorIK?

You can see in this diagram that this event fires after the animation is applied, where as fixed update is before.