|
Team
Solo
Engine
Unity
Plugin
SteamVR
Distance Grab
I remember how surprised I was when started my first Unity (Steam VR) project and did not find distance grab functionality in the library. I expected it to have some kind of Half-Life: Alyx alike mechanic for pulling objects from distance. That is why I decided to create my own version of it. My focus was not on direct copying it from the Valve's game, it simply would not work well for the project I was prototyping at that moment. However, I still wanted it to be as intuitive and simple as there.

In case you are interested in their implementation of the mechanic, I would highly recommend you to check the Half-Life: Alyx Developer Commentary, where they cover many corners of the game's development, including the interaction system.
Finding the object
For my implementation, I started with a simple raycast system. A script DistanceGrabHand.cs should be attached to the hands. Sending a ray towards the hand's transform.forward position would work for some types of the library's included hand models. However, to make the direction easily adjustable for any model I added an empty object DistanceGrabPointer inside the Hand object. By sending a raycast from the pointer's position towards its forward vector, I was able to identify objects the hand is pointing at. However, not all objects should be hit by the raycast, so I created a DistanceGrabbable layer and used it for raycasting.
Visual feedback
The system needed to have some kind of visual feedback, so the player can see what object they are currently pointing at. It can be simply done by creating a child object inside grabbable objects and attaching a mesh renderer to it with a HoverHighlight material included with the Steam VR library. When the object is hit by a ray, it should activate the mesh renderer for one frame and then disable it. For this purpose, I created the DistanceGrabbableObject.cs script.
Pulling the object
Now I find the object and highlight it, but still cannot grab it. The easy way of doing that would be to just instantly attach it to the hand, however, doing so would destroy the realism of the game. That is why I start the object's position linear interpolation (lerp) towards the player's hand when a player makes a grab motion. When the object's position gets close enough to the hand, I attach the object to the hand.
Spherecast vs Raycast
At this stage, distance grab is already working. However, it is very hard to point your hand at objects so that rays hit them, especially at small ones. That's why I decided to switch raycast to sphere cast for the objects' searching. It gives up some performance, but greatly increases the intuitiveness of the interaction. Distance Grab is done.
Adjustable public variables
DistanceGrabHand.cs:
· Grabbable objects' layer mask
· Speed of pull for linear interpolation
· Maximum distance of the grab
· Sphere radius for sphere-casting
· The distance at which pulled object gets attached to the hand
Distance Linear Motion
Distance grab can be used inside many games. But sometimes you just want to move the object around from the distance without pulling it into your hand. Imagine just dragging out a cover from the ground in a VR FPS game, or lifting up a fallen bridge to cross the river in an adventure game. Since I've already implemented distance grab, I decided to use the same DistanceGrabHand.cs script to add distance linear motion functionality to it.
Linear Drive
In the standard implementation of the linear drive (from Steam VR library), all update calls for LinearDrive.cs are coming from the hand while the object is attached to it. In case of a distant linear movement, the object can not be attached to the hand directly, since attachment would instantly teleport it to the player's hand position. So, it would be impossible to use the LinearDrive.cs script.

However, we still can take the main idea from there, and rework it for our needs. I created a DistanceLinearDrive.cs script, where added public OnAttachement(), OnDetachment, and DistanceHandUpdate() functions. The first function calculates the initial linear mapping offset. Two others calculate the linear mapping value based on the change in the hand's position and apply it to the object's position in the form of lerp.
Applying Linear Motion
In distance grab, the DistanceGrabbableObject.cs script was used only for activation of the object's highlight mesh renderer. Now it also contains a boolean, saying if the object is grabbable (true), or if it is used for Linear Motion (false).

Now we can simply call DistanceLinearDrive.cs functions from the DistanceGrabHand.cs script, which already has all functionality for starting the grab and updating the object's data every frame.
Feel free to use it for your projects!
Virtual Reality
Unity
Unreal
Augmented Reality
C#
C++
Agile
Version Control
AI