[Solved] How to handle leg IK?

For anyone wondering how it works, 95% of the IK logic is in the Animation Graph. Here is the script that takes an impact normal and converts it into a rotation for the foot:

using FlaxEngine;

namespace Game
{
    /// <summary>
    /// FootIK Script. Can be used to make a character's feet move with uneven ground.
    /// </summary>
    public class FootIK : Script
    {
	    [Serialize, ShowInEditor, EditorOrder(0), EditorDisplay(name: "Left Foot IK")]
        private BoneSocket leftFootIK;
        [Serialize, ShowInEditor, EditorOrder(1), EditorDisplay(name: "Right Foot IK")]
        private BoneSocket rightFootIK;
        [Serialize, ShowInEditor, EditorOrder(2), EditorDisplay(name: "Collision Mask")]
        private LayersMask collisionMask;

        private AnimGraphParameter _leftFootRot;
        private AnimGraphParameter _rightFootRot;
        private AnimGraphParameter _leftFootIK;
        private AnimGraphParameter _rightFootIK;

        public override void OnStart()
        {
            // Cache parameters
            _leftFootRot = Actor.As<AnimatedModel>().GetParameter("LeftFootRot");
            _rightFootRot = Actor.As<AnimatedModel>().GetParameter("RightFootRot");
            _leftFootIK = Actor.As<AnimatedModel>().GetParameter("LeftFootIK");
            _rightFootIK = Actor.As<AnimatedModel>().GetParameter("RightFootIK");
        }

        public override void OnFixedUpdate()
        {
            if (Physics.RayCast(new Vector3(leftFootIK.Position.X, Actor.Position.Y + 10, leftFootIK.Position.Z), Vector3.Down, out RayCastHit leftFootHit, 50, collisionMask))
            {
                // Get a forward-facing direction relative to the ground noraml
                Vector3 aimDirection = Vector3.Cross(leftFootHit.Normal, Transform.Right);
                // Create a rotation from the direction
                Quaternion footRot = Quaternion.LookRotation(aimDirection, leftFootHit.Normal);

                _leftFootRot.Value = Quaternion.Euler(footRot.EulerAngles.X, 0, footRot.EulerAngles.Z);
                _leftFootIK.Value = leftFootHit.Point.Y - Actor.Position.Y;

            }
            else
            {
                _leftFootRot.Value = Quaternion.Euler(0, 0, 0);
                _leftFootIK.Value = 0;
            }

            if (Physics.RayCast(new Vector3(rightFootIK.Position.X, Actor.Position.Y + 10, rightFootIK.Position.Z), Vector3.Down, out RayCastHit rightFootHit, 50, collisionMask))
            {
                // Get a forward-facing direction relative to the ground noraml
                Vector3 aimDirection = Vector3.Cross(rightFootHit.Normal, Transform.Right);
                // Create a rotation from the direction
                Quaternion footRot = Quaternion.LookRotation(aimDirection, rightFootHit.Normal);

                _rightFootRot.Value = Quaternion.Euler(footRot.EulerAngles.X, 0, footRot.EulerAngles.Z);
                _rightFootIK.Value = rightFootHit.Point.Y - Actor.Position.Y;

            }
            else
            {
                _rightFootRot.Value = Quaternion.Euler(0, 0, 0);
                _rightFootIK.Value = 0;
            }
        }
    }
}

And here are some screenshots of the monstrous Animation Graph:



![image|690x239](upload://htIrU5aagP4fTEh6aZJCy


IK68n5.jpeg)

From the testing I’ve done so far, this IK system yields excellent results.

4 Likes