“Distance Grab” is a feature that allows users to point to faraway objects, and have them leap into your hands when they squeeze the grip button.
There are many reasons you might add distance grab to your app. You might want to support players who have small play areas, or who would prefer to play seated. Objects on the floor can be inconvenient to grab. Distance grab can provide more convenience, comfort, and speed than navigating over to a distant object.
To help you add distance grab behavior to your app, we’ve added the DistanceGrab scene to version 1.18 of the Oculus Unity Sample Framework. The Oculus Unity Sample Framework project file is available in the Unity Asset Store, and from
our downloads page.
SAMPLE NOTE: Please recenter the app through the Universal Menu once you enter the DistanceGrab scene. A known issue with OVRPlayerController makes the scene difficult to navigate if you skip this step.
Distance Grab Behavior Overview
The best way to see the sample behavior is to download it. Here’s a quick overview of what's happening:
- You may distance grab a grabbable object if it is within a certain range of the player (details below). Any grabbable object within range is highlighted.
- If either of the player’s hands point at an object in range, it becomes the target. It displays a different color to indicate it’s the target. If multiple objects could be the target, the closest one is targeted.
- If the player points at an object and squeezes the grip button, the object will fly to the player’s hand and remain attached until the player lets go. (All highlighting is turned off at this time.)
- Objects that are within a small range of the player’s hand don’t fly towards the hand. Instead, they grab as usual. This makes it easier to do precision work such as lifting objects in a stack.
Quick Implementation
Distance Grab builds upon our basic Custom Hands sample for Unity. If you’re not yet familiar with our basic hand samples, you may wish to read
our latest post on implementing quality hands before attempting to implement the Distance Grab sample.
For a basic implementation, first, use the DistanceHandLeft/Right prefabs to create a hand with all the basics from our Custom Hands sample. Then replace OVRGrabber with a DistanceGrabber component.
From there, you can use the DistanceGrabCubePf or DistanceGrabCubeCrosshair prefabs as a starting point for your own Game Objects, or just add a DistanceGrabbable component to your existing objects. The trickiest part of this will be adapting the sample's outlines or crosshairs to something that works well with your app.
Implementation Details and Choices
Now we'll walk through some implementation details, with a focus on things you might wish to adjust when adding distance grab to your own app.
Grabbable Range
If you open up the Distance Grab scene in the Unity Editor, you’ll see a DetectGrabRange GameObject under the OVRPlayerController. It uses a sphere and the GrabManager component to notify grabbable objects (that is, objects with a DistanceGrabbable component) that they’re in range. Then the grabbable objects update their display to indicate grabbability. If you drive the player controller around the scene using the thumbsticks, you’ll see objects become outlined in white as they come into range, and then lose this outline when they’re out of range. A blue outline indicates an object is currently targeted by one of your hands (see the capture above for an illustration).
How to change this grabbable distance depends on how you select target objects, so read on through the next two sections where we’ll discuss that next.
Grab Volumes Versus Sphercast
By default, the Distance Grab sample attaches three trigger volumes to each hand: two small cylinders for short-range grabbing, and a cone for long-range grabbing. The two small cylinders are used in our standard grab samples as well, and we’ve found them to be good for short-range grabs.
You can switch to a spherecast using the menu, and you can adjust the radius of the spherecast using Spherecast Radius in the Distance Grabber component through the Editor.
Increasing Grab Range
The sphere trigger volume used by the GrabManager determines if an object should be grabbable. But it’s the trigger volumes or spherecasts on the hands that do the actual work of identifying targets. That means you have to change both places to change the player’s grab distance.
The biggest downside to making the grab volumes or spherecast ranges too large is performance—you don’t want to search the entire world. But don’t worry about making them slightly too big. In fact, you should make them larger than the radius of the GrabManager’s sphere, because the player’s hand might be on the other side of the player’s body from the object.
No-Fly Distance
Objects that are close enough to the hand already, and don't have a custom grab snap position, will not fly towards the hands. They will simply become attached where they are. This is crucial for fine manipulation, such as stacking a tower of blocks: if the block flies towards your hand, it will knock over the tower. You can change this snap distance in each DistanceGrabber’s Max Grab Distance.
Crosshairs and Outlines
It’s up to you to determine the best way to show in-range and targeted states on objects in your app. No single approach works well across all apps. This sample offers two potential starting points.
Some objects in the scene use the ToyCubeWithOutline shader to render a programmatically selected outline around grabbable or targeted objects. While some common outline shaders push vertices out along the vertex normal, that doesn’t work well with discontinuous normals like you’d find on a cube. So this shader pushes vertices out from the object center—which may not look appealing with some convex objects. This shader does add the performance cost of rendering all outlined objects a second time.
Other objects in the scene use a crosshair to indicate state. The crosshairs are simply additional Game Objects that are shown and hidden. Enabled crosshairs incur a small CPU cost in Update(1.18) by orienting themselves towards the player. In the CrosshairZCheat shader they move their z towards the player a little bit so that they always render on top of the object they’re attached to, but this hardcoded range won’t work for all object sizes.
Grabbing Objects Through Walls
The sample optionally prevents objects from being grabbed through world geometry (see menu in sample).
We accomplish this with a separate raycast after the target is identified, regardless of whether we’re using grab volumes or spherecasts to identify targets. We use a raycast rather than a spherecast for obstruction detection, because we want to be as permissive as possible when determining what’s grabbable.
Collision Layers
We’ve set up our layers to accomplish a couple things:
- Don’t grab objects through walls. “Walls” are any collider in the layer m_ObstructionLayer in DistanceGrabber.cs, which is the Default layer in the sample.
- Don’t try to grab everything. Only look to grab objects in layer m_GrabObjectsInLayer, also in DistanceGrabber.cs. This is the Toys layer in the sample. (Note that only objects with a Grabbable component will be grabbed, but this layer use prevents non-grabbable objects from triggering our trigger volumes.)
- Don’t collide hands with any objects, unless the hand is closed and not holding anything. We accomplish this by putting the hands in the Toys layer, and disabling their collision unless they're closed
Conclusion
Distance Grab can improve the comfort and usability of many types of apps. Now you should have enough information to adapt our Distance Grab sample to your app, or to start work on your own. While we've attempted to provide a variety of options in the sample, there are many other ways interaction at a distance can work. Keep the goals of your app in mind, and don't be afraid to experiment with unconventional approaches.