Custom VR hand Animations Using blend shapes for SteamVR

My other hand Animation Tutorial is important to me since it was my first tutorial, It has also consistently been one of my most popular tutorials. Though everyone always says that I should have used blend states instead of animation states. While I think that animation states work better, I decided I’d give people what they want.

To get started we need to create a hand model that has some blend states, you can download mine here. But I’ll still teach you how I made them:

Modeling

To create the hand model for this tutorial I just imported the SteamVR hand model into Blender and added the blend shapes. To do this I posed the hand with the armature, duplicated the armature modifier with the Copy button, and then converted the original modifier into a shape key with the Apply As Shape Key button:

We need to create three: One for when the trigger is pressed, one for when the grip is pressed, and one for when both are pressed:

Now that we have that put together, export it to unity as an FBX and let’s get to coding:

The Code

What we need to do is really simple, It’s basically the same script as the one in the last tutorial with some smoothing code put in:

using UnityEngine;
using Valve.VR;

public class HandShaper : MonoBehaviour
{
    public SkinnedMeshRenderer HandModel;//set to the renderer of the hand model
    public SteamVR_Input_Sources Hand;
    public SteamVR_Action_Boolean Trigger;
    public SteamVR_Action_Boolean Grip;
    public float SlerpSpeed;//speed that we change poses;

    private bool TriggerDown;
    private bool GripDown;

    void Update()
    {

        
        if (Trigger.GetStateDown(Hand))//Update our inputs 
        {
            TriggerDown = true;
        }else if (Trigger.GetStateUp(Hand))
        {
            TriggerDown = false;
        }

        if (Grip.GetStateDown(Hand))
        {
            GripDown = true;
        }else if (Grip.GetStateUp(Hand))
        {
            GripDown = false;
        }


        if (TriggerDown && GripDown)//check if we are making a fist
        {
            HandModel.SetBlendShapeWeight(0, limit(HandModel.GetBlendShapeWeight(0) - SlerpSpeed));//slowly increment towards 100 to give the apperance of movment
            HandModel.SetBlendShapeWeight(1, limit(HandModel.GetBlendShapeWeight(1) - SlerpSpeed));
            HandModel.SetBlendShapeWeight(2, limit(HandModel.GetBlendShapeWeight(2) + SlerpSpeed));
        }
        else if (TriggerDown)//are we making the ok sign
        {
            HandModel.SetBlendShapeWeight(0, limit(HandModel.GetBlendShapeWeight(0) + SlerpSpeed));
            HandModel.SetBlendShapeWeight(1, limit(HandModel.GetBlendShapeWeight(1) - SlerpSpeed));
            HandModel.SetBlendShapeWeight(2, limit(HandModel.GetBlendShapeWeight(2) - SlerpSpeed));
        }
        else if (GripDown)//are we pointing
        {
            HandModel.SetBlendShapeWeight(0, limit(HandModel.GetBlendShapeWeight(0) - SlerpSpeed));
            HandModel.SetBlendShapeWeight(1, limit(HandModel.GetBlendShapeWeight(1) + SlerpSpeed));
            HandModel.SetBlendShapeWeight(2, limit(HandModel.GetBlendShapeWeight(2) - SlerpSpeed));
        }else//reset position if we arn't doing anything
        {
            HandModel.SetBlendShapeWeight(0, limit(HandModel.GetBlendShapeWeight(0) - SlerpSpeed));
            HandModel.SetBlendShapeWeight(1, limit(HandModel.GetBlendShapeWeight(1) - SlerpSpeed));
            HandModel.SetBlendShapeWeight(2, limit(HandModel.GetBlendShapeWeight(2) - SlerpSpeed));
        }
        
    }
    
    private float limit(float value)//we need to limit the values to 0 through 100 or we get some disturbing results
    {
        if (value > 100)
        {
            return 100;
        }else if (value < 0)
        {
            return 0;
        }
        else
        {
            return value;
        }
    }
}

And it’s really that simple, just add the script to your hands, set the values, and you are ready to go!

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