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!