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!