Grabbing virtual hands in Unity

In this first post on virtual hands in Unity I will explain the basics of picking up objects. What do you need to include in your gameobjects, how to setup the colliders and make the objects follow your hands.

The solution I will provide here is quite straightforward: if your hand touches an object and you close your hand, it will pick up the object. If you then open you hand again, it will let go the object. It will not deal with collisions with static objects like walls or include physics like hinges. I will discuss this in a future post.

We start with a basic Collider on the hand. This will enable us to detect that the hand is touching an object. Moreover, even when not picking up the object, the Collider will enable go basic interaction with the environment, because the collisions it causes will move Rigidbodies around.

We will keep the hand Collider very simple: a sphere with a radius of 10 cm (0.1 units). We have to move it towards the fingers for about 10 cm, because the origin of the hand is actually at the wrist.

HandCollider

The we need to add a Rigidbody, because otherwise we cannot process the collision events. We add the Rigidbody to the root bone of the avatar. It defines the properties of a physics object as a whole, in this case the whole body. The most important fields here are Use Gravity, which is deselected, and Is Kinematic, which is selected. Gravity is deselected, because that is handled by the Character Motor. Kinematic is selected because the avatar is driven by the users input.

Rigidbody

Now we have to implement a script on the object with the Rigidbody which catches the collision event. We do this using a OnCollisionStay implemention which is called when a Collider within a Rigidbody collides with an object. In this function we check whether the hand is closing. This depends on the device, buttons and implementation you use. In this case we assume that the left mouse button is used for closing the hand.

If the hand is closed, we extract the object we want to grab from the collision information we received in the OnCollisionStay function. Then we convert the object’s Rigidbody to Kinematic: we hold it now and it no longer is influenced by the physics engine any more.

Then the most important step: we parent it to the hand. This actually means we make it a part of the avatar which moves together with the hand. However, before we do this we have to store the current parent of the object. When we will let go the object later, we have to restore the parent again.

    void OnCollisionStay(Collision collision) {
        if (Input.GetMouseButton(0) == true) { // Hand is closed
            // the object we grab
            grabbedObject = collision.gameObject;
            // Make it kinematic as we are holding it now
            grabbedObject.rigidbody.isKinematic = true;
            // Store the original parent to restore it when letting loose
            objectOrgParent = grabbedObject.transform.parent;
            // And parent it to the hand
            grabbedObject.transform.parent = hand.transform;
        }
    }

And that is it: we have grabbed the object and it will be moving wherever we move our hand!

Now dropping the object again. This is done in a FixedUpdate function. Within this function, when we are grabbing an object and the hand is open, we know that we have to drop that object. This is done quite simply by restoring the parent to the previously stored value, make it non-kinematic again and clear the grabbed object so that we know that we do not hold any object any more.

    void FixedUpdate() {
        if (grabbedObject != null) { // are we holding an object?
            if (Input.GetMouseButton(0) == false) { // Hand is open
                // unparent it from the thumb
                grabbedObject.transform.parent = objectOrgParent;
                // make it non-kinematic again
                grabbedObject.rigidbody.isKinematic = false;
                // and clear the grabbed object
                grabbedObject = null;
            }
        }
    }

And to be complete: here is the full code of the grabbing object function:

using UnityEngine;
using System.Collections;

public class GrabObject : MonoBehaviour {

    public GameObject hand;

    private GameObject grabbedObject = null;
    private Transform objectOrgParent = null;

    void OnCollisionStay(Collision collision) {
        if (Input.GetMouseButton(0) == true) { // Hand is closed
            // the object we grab
            grabbedObject = collision.gameObject;
            // Make it kinematic as we are holding it now
            grabbedObject.rigidbody.isKinematic = true;
            // Store the original parent to restore it when letting loose
            objectOrgParent = grabbedObject.transform.parent;
            // And parent it to the hand
            grabbedObject.transform.parent = hand.transform;
        }
    }

    void FixedUpdate() {
        if (grabbedObject != null) { // are we holding an object?
            if (Input.GetMouseButton(0) == false) { // Hand is open
                // unparent it from the thumb
                grabbedObject.transform.parent = objectOrgParent;
                // make it non-kinematic again
                grabbedObject.rigidbody.isKinematic = false;
                // and clear the grabbed object
                grabbedObject = null;
            }
        }
    }
}

LayerCollisionMatrixThere is one thing to watch when using a solution like this: your hand may collide with the capsule connected to the Character Controller. If that happens, you will not be able to walk any more. You can prevent this by creating two separate layers: one for the body and one for the hand. The Character Controller will use the body layer, while the hands use the hands layer. Now in the Project Settings -> Physics you can configure that the body layer does not collide with the hands layer in the Layer Collision Matrix:

Of course this will not be the most optimal of efficient implementation, but I hope this will help you forward in implementing grabbing hands.

I have made a free Unity3D Asset package which implements the techniques above.

Posted in HowTo Tagged with: , ,

Leave a Reply