VR Body Tracking Part 3 | Legs

And now, for the final tutorial. This is the final tutorial is a series where I explain how to make my body tracking system. If you haven’t read the others, you can start from the beginning here. Anyway in this tutorial as you can probably tell from the title we are going to set up the legs, so let’s get right into it.

The Setup

The first thing we want to do is set up our legs with the IK asset we coded in the last tutorial. The setup is exactly the same as the arms so you can use the same instructions to set that up. For the target and pole, we need to create a new object. Create an empty game object as a child of the [CamerRig] and put two empty game objects inside it, move these objects so that they are at the same location as Robot Kyle’s ankles (The origin of your feet) and then name them according to which foot they will be the target for. Add another game object to both of these target game objects and position them so that they are about three feet or one meter in front of their respective knee. Ok so what you should have now should look something like this:

we can now use these as the target and pole for our leg IK.

The Code

Now that we’ve done all the setup we can start coding. First, we are going to want to create a new script to control our feet and attach it to the root of the game objects we created earlier.

using UnityEngine;

public class FootMovment : MonoBehaviour
{
    public float StrideLength;
    public float StepHeight;
    public Transform LeftFoot;//set these to the foot target game objects we created earlyer  
    public Transform RightFoot;

    public float HeightMultiplyer;

    private float StridePosition;
    private Vector3 FootOffsetL;
    private Vector3 FootOffsetR;
    private float Length;

    void Start()
    {
        //sets the offset of our feet
        FootOffsetL = LeftFoot.localPosition;
        FootOffsetR = RightFoot.localPosition;
    }

    // Update is called once per frame
    void Update()
    {
        PositionFoot(LeftFoot, FootOffsetL, false);
        PositionFoot(RightFoot, FootOffsetR, true);

        UpdateHeight();

    }
    void PositionFoot(Transform Foot, Vector3 FootOffset, bool side)
    {

        float height;
        if (side)
        {
            height = Mathf.Sin(StridePosition * 2 * Mathf.PI) * StepHeight;

            if (fixStride(1 - StridePosition) < .5f) Length = (StrideLength / 2) - (StridePosition * 2) * StrideLength + (StrideLength / 2) + StrideLength / 2;
            else Length = ((StridePosition - .5f) * 2) * StrideLength + (StrideLength / 2);
            if (height < 0) height = 0;
        }
        else
        {
            height = -Mathf.Sin(StridePosition * 2 * Mathf.PI) * StepHeight;
            if (fixStride(StridePosition) > .5f) Length = ((StridePosition - .5f) * 2) * StrideLength - (StrideLength / 2);
            else Length = -(StridePosition * 2) * StrideLength + StrideLength / 2;
            if (height < 0) height = 0;
        }
        Foot.localPosition = FootOffset + new Vector3(0, height*HeightMultiplyer, Length);
    }
    float fixStride(float stride)
    {
        if (StridePosition > 1)
        {
            return StridePosition -= 1;
        }
        else if (StridePosition < 0)
        {
            return StridePosition += 1;
        }
        else
        {
            return stride;
        }
    }
    public void WalkM(float Meeters)//a method called to move the legs when you move.
    {
        StridePosition += Meeters / (StrideLength * 2);
        while (true)
        {
            if (StridePosition > 1)
            {
                StridePosition--;
            }
            else if (StridePosition < 0)
            {
                StridePosition++;
            }
            else
            {
                break;
            }
        }
    }
    private void UpdateHeight()//this function keeps the height multiplyer within a certan value.
    {
        if (HeightMultiplyer > 1)
        {
            HeightMultiplyer = 1;
        }
        else if(HeightMultiplyer < 0)
        {
            HeightMultiplyer = 0;
        }
    }
}

What the above code does is position the feet using a sin wave controlled by the stride position variable. Now, this code won’t do anything right now as I designed it to be controlled by our body tracking script so let’s update that now.

//add these varibles 
public Transform FeetRoot;
private Vector3 PastPos;

//Add the following code right under this line of code:
BodyRoot.transform.position = new Vector3(Head.transform.position.x, CameraRig.transform.position.y, Head.transform.position.z);

//this code controlls the rotation of the hips, movment of the legs, and height of the feet.
if ((BodyRoot.transform.position - PastPos).magnitude > .005f)
        {//If we have moved far enough
            FeetRoot.GetComponent<FootMovment>().HeightMultiplyer += .01f;//update foot position
            
            if (Quaternion.Angle(Quaternion.LookRotation(BodyRoot.transform.position - PastPos), Quaternion.Euler(0, Head.transform.rotation.eulerAngles.y, 0)) < 100)
            {//if we move forward 
                Hips.transform.rotation =Quaternion.RotateTowards(Hips.transform.rotation, Quaternion.Euler(0, Quaternion.LookRotation(BodyRoot.transform.position - PastPos).eulerAngles.y, 0) * HipOffsetRot,3);
                FeetRoot.rotation = Quaternion.RotateTowards(FeetRoot.rotation, Quaternion.Euler(0, Quaternion.LookRotation(BodyRoot.transform.position - PastPos).eulerAngles.y, 0), 3) ;
                FeetRoot.GetComponent<FootMovment>().WalkM((BodyRoot.transform.position - PastPos).magnitude);
            }
            else
            {//if we move backwards 
                Hips.transform.rotation = Quaternion.RotateTowards(Hips.transform.rotation, Quaternion.Euler(0, Quaternion.LookRotation(-(BodyRoot.transform.position - PastPos)).eulerAngles.y, 0) * HipOffsetRot,3);
                FeetRoot.rotation = Quaternion.RotateTowards(FeetRoot.rotation, Quaternion.Euler(0, Quaternion.LookRotation(-(BodyRoot.transform.position - PastPos)).eulerAngles.y, 0),3);
                FeetRoot.GetComponent<FootMovment>().WalkM(-(BodyRoot.transform.position - PastPos).magnitude);
            }
        }
        else
        {
            Hips.transform.rotation =  Quaternion.RotateTowards(Hips.transform.rotation, Quaternion.Euler(0, BodyRoot.transform.rotation.eulerAngles.y, 0) * HipOffsetRot, 2.5f) ;
            FeetRoot.rotation = Quaternion.RotateTowards(FeetRoot.rotation, Quaternion.Euler(0, BodyRoot.transform.rotation.eulerAngles.y, 0), 2) ;

            FeetRoot.GetComponent<FootMovment>().HeightMultiplyer -= .01f;//lower feet when not moving
        }

This script is what controls our hip rotation, drops our feet when we stop moving, and tells our feet how far to walk. Fill in the new variable, (FeetRoot is the game object thing we created earlier. ) and you should have a fully working body tracking system!

Liked it? Take a second to support WireWhiz on Patreon!
Become a patron at Patreon!