In my VR business simulation Shopkeeper Simulator VR, hand interaction is generally achieved through the SteamVR Interaction System. Let's take a look at the 3D scene in Unity to visualize what we are going to talk about:
Fridge door in closed state
Fridge door in opened state
Object hierarchy
To move the fridge door independently from the fridge body, the door was modeled as a separate mesh in Blender:
Object hierarchy in Blender
After we exported the file as FBX, the imported hierarchy on the Unity side looked like this:
Object hierarchy in Unity (irrelevant children obfuscated)
Handle explained
The "Handle" GameObject is the one which should react to the player's hand. Therefore, it was also separated from the door itself. The handle needs two components: The Interactable component which is part of the interaction system, a collider and the script OpenDoor which is explained further below.
"Handle" GameObject in the inspector
Script OpenDoor.cs
using UnityEngine;
using Valve.VR.InteractionSystem;
/*
* This class is attached to a door handle. The door handle is child of a door.
*/
public class OpenDoor : MonoBehaviour
{
private Vector3 force;
private Vector3 cross;
private bool holdingHandle;
private float angle;
private const float forceMultiplier = 150f;
private void HandHoverUpdate(Hand hand)
{
if (hand.GetStandardInteractionButton())
{
holdingHandle = true;
// Direction vector from the door's pivot point to the hand's current position
Vector3 doorPivotToHand = hand.transform.position - transform.parent.position;
// Ignore the y axis of the direction vector
doorPivotToHand.y = 0;
// Direction vector from door handle to hand's current position
force = hand.transform.position - transform.position;
// Cross product between force and direction.
cross = Vector3.Cross(doorPivotToHand, force);
angle = Vector3.Angle(doorPivotToHand, force);
}
else if (hand.GetStandardInteractionButtonUp())
{
holdingHandle = false;
}
}
void Update()
{
if (holdingHandle)
{
// Apply cross product and calculated angle to
GetComponentInParent<Rigidbody>().angularVelocity = cross * angle * forceMultiplier;
}
}
private void OnHandHoverEnd()
{
// Set angular velocity to zero if the hand stops hovering
GetComponentInParent<Rigidbody>().angularVelocity = Vector3.zero;
}
}
The two methods HandHoverUpdate
and OnHandHoverEnd
are specific to the SteamVR interaction system. They are called by the interaction system every frame the hand hovers over an interactable object and when the hovering ends respectively.
Basically, we are using the cross product of the force vector (pulling force of the hand) and the vector that points from the door's hinge to the transform where the hand is holding the handle (called "doorPivotToHand" vector). Together with the angle between the force vector and the "doorPivotToHand" vector, the angularVelocity is calculated and applied in FixedUpdate(). Note that FixedUpdate should be used and not Update, since we are changing a RigidBody's angular velocity.
Notice that in order to work, the door must have a Rigidbody, a collider and a Hinge Joint component to limit the door's angle. The HingeJoint's limits were set to 0 min and 90 max:
The GameObject "FridgeBody" needs a rigidbody with enabled isKinematic property and gravity equal false because we don't want the body to be controlled by the physics system and influenced by gravity. It is mandatory for the HingeJoint to work though.
I hope you found this little insight into our game Shopkeeper Simulator VR helpful for your own projects. If you found this useful, please leave a comment below and/or share the article! Also post any questions you might have.
Thanks for posting this article about Unity, as I am a Unity Fan :)
This comment is currently awaiting admin approval, join now to view.
This comment is currently awaiting admin approval, join now to view.