[Solved] How to handle leg IK?

I’ve been trying to implement a foot inverse kinematics system for my character. However, when using the Two-Bone IK node, the walking animations are completely overridden.

I’m using a RayCast that shoots from a socket at the foot’s position. If it hits something, the IK’s target Y position will be set to the collision point. However, this creates a self-sustaining situation that prevents the feet from lifting off the ground.

Current result:
Game 2022-07-27 11-50-24

Expected result:
Game 2022-07-27 11-51-29

How do I prevent the IK from completely overriding the walking animations? If only there was some way to do the main animations, then trace, then apply IK. In Godot, I could simply animate the IK weight overtime on the animations themselves, but there doesn’t seem like an easy way to do the same thing in Flax Engine.

I’m not a master in animations area but I think that you should take the foot position from animation and apply IK only if places foot closer to the body (eg. player stepped on the stairs step). This might depend o the motion too (eg. more IK influence when Idle).

Thanks. That sounds like a good solution.

I managed to figure out another solution that works pretty well, that involves a bit of manual translations, but now I’m having the same issue with hip translation. I wish there was some way to access socket locations before the final animation output. I need the RayCasts to cast from where the feet would be with no IK, not where they are after the IK. Is there a way to access a bone/node’s translation inside the animation tree that can be exposed to scripts? Unreal Engine has Control Rig, which allows physics traces to be performed straight inside the animation graph.

I think I might’ve finally figured it out.


I have to adjust the weight of the hip offset when walking to prevent the character from bouncing up and down, but I have a working IK solution.

1 Like

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.

image

6 Likes