Meta XR Interaction SDK (com.meta.xr.sdk.interaction.ovr)
Use ISDK for interaction on Meta VR
The Core SDK (
) gives you the headset - camera rig, tracking,
.
It does not give you interaction.
ISDK is the interaction layer. Don't hand-roll grabbing on
, raycasts, or trigger colliders: ISDK already models hover/select state, pose
conforming, throw velocity, and interactor arbitration, and a hand-rolled version drifts from
platform behavior.
| Target | What to do |
|---|
| Meta VR / Horizon OS, or platform not stated | OVR backend (com.meta.xr.sdk.interaction.ovr
) - the rest of this skill |
| Cross-platform, still on ISDK | UnityXR backend (Essentials + Unity XR Hands). Same model, different entry points - look them up, don't assume the OVR ones |
| Cross-platform not using ISDK | Stop. This skill does not apply. |
Do not infer cross-platform intent - default to OVR, switch only if the user says so, and
state which backend you used.
Set interactions up through the Quick Actions APIs, not by hand.
builds the rig;
makes objects interactive and equips interactors. Reach for
manual prefab assembly only when a Quick Action cannot express what you need - and say so.
Meta's own guidance too: Quick Actions are "the fastest and recommended way" to add the rig
(
docs).
Quick start
csharp
using Oculus.Interaction.Editor.QuickActions; // QuickActionsAPI
using Oculus.Interaction.OVR.Editor.QuickActions; // OVRQuickActionsAPI
// Delete the default Main Camera first - ISDK brings its own camera rig.
// 1. Rig: comprehensive interaction rig (hands + controllers + every interactor + visuals),
// plus an OVRCameraRig if the scene has none.
OVRQuickActionsAPI.AddOVRInteractionRig();
// 2. Interactable: adds Rigidbody + Grabbable + a HandGrab/Grab interactable child.
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.transform.localScale = Vector3.one * 0.1f;
cube.transform.position = new Vector3(0f, 1.0f, 0.4f); // in reach at a FloorLevel origin
QuickActionsAPI.AddGrabInteraction(cube);
Every method returns the root GameObjects it created - not the target you passed. Use that
to find what to configure or rename.
Which call for which interaction
| Feature | Call |
|---|
| Interaction rig (+ camera rig) | OVRQuickActionsAPI.AddOVRInteractionRig()
|
| Hand grab (near) | QuickActionsAPI.AddGrabInteraction(go)
|
| Distance grab | QuickActionsAPI.AddDistanceGrabInteraction(go, mode)
|
| Ray grab | QuickActionsAPI.AddRayGrabInteraction(go)
|
| Gaze grab (eye tracking) | QuickActionsAPI.AddGazeInteraction(go)
|
| Teleport surface | QuickActionsAPI.AddTeleportInteraction(go, surfaceType)
|
| Poke / ray / gaze a Canvas | QuickActionsAPI.AddPokeCanvasInteraction(go)
· · |
| Interactors on a hand / controller | QuickActionsAPI.AddHandInteractors(go, types)
· · AddControllerHandInteractors
|
| Touch hand grab, plain poke, plain ray | no API - menu item only |
Every one of these takes a - never a component. Passing a
to a
Canvas call is a compile error (
cannot convert from 'UnityEngine.Canvas' to 'UnityEngine.GameObject'
);
pass
or the result of
. Likewise the interactor calls take the
GameObject that carries the
/
, not the component.
Signatures, parameters, and what each call builds:
quick-actions-api.md ·
ovr-quick-actions-api.md.
Per-feature Meta docs: documentation.md.
The interactor calls are only for a hand-built rig -
already ships every
interactor, so after it they are redundant.
When the rig isn't in your scene
An interaction needs a matching interactor at runtime, in the loaded scene set. It does not
need the rig present at author time.
| Quick Action | Needs a rig in the scene? |
|---|
| Interactables (grab, distance grab, ray grab, gaze, canvas, teleport) | No - everything they require is on the target itself |
| Interactors ( and siblings) | Yes - must carry the / |
| It creates the rig |
So all of these are fine: the rig is instantiated at runtime, lives in a bootstrap scene under
, arrives from an additive scene, or you are authoring a
prefab.
The consequence. An interactable Quick Action also tops up the rig with matching interactors
when it finds one - silently skipped when there is no rig. Nothing breaks, but nobody added
those interactors, so the rig that eventually loads must already carry the types your interactable
needs. A
DistanceHandGrabInteractable
with no
DistanceHandGrabInteractor
on the runtime rig is
inert and logs nothing. When you author against an absent rig, say which interactor types the
runtime rig must provide.
Baking interaction into a prefab
Verified: run the interactable call on a scene instance, then save. The wiring is self-contained -
the interactable's
resolves to the
inside the prefab, with no
leaked scene reference.
csharp
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
QuickActionsAPI.AddGrabInteraction(go); // no rig needed
PrefabUtility.SaveAsPrefabAsset(go, "Assets/Grabbable.prefab", out bool ok);
Object.DestroyImmediate(go);
- Don't verify a prefab asset via
interactable.PointableElement
- it is assigned in
, which never runs on an asset, so it is always null there even when the prefab is
correct. Read the serialized field:
new SerializedObject(interactable).FindProperty("_pointableElement").objectReferenceValue
.
- Canvas calls: pass
fixPointableCanvasModule: false
when the comes
from a prefab or an unloaded additive scene, or you get a second one in the open scene.
Running the calls - both APIs are Editor-only
They live in Editor assemblies and
cannot run from a
or anything in a build.
1. Live Editor via (preferred when one is open - drive it instead of editing
scene YAML):
bash
unity status --format json # look for state "ready"
unity command run_script --project-path <proj> --file /abs/path/DoTheThing.cs --entry MyClass.Run --format json
Use
with a
file rather than
: shell quoting mangles C# char literals
(
), the file is re-runnable, it can live outside
, and
awaits async
entry points.
2. A project Editor script under
(or an Editor-only asmdef referencing
Oculus.Interaction.Editor
and
Oculus.Interaction.OVR.Editor
), exposed as a
-
when a human should be able to repeat the setup.
3. MCP, if a Unity MCP server is connected. Its harness constraints (no
, no
overloads, package Editor assemblies not referenced) apply
there and
not to
's Roslyn
— see
unity-mcp-fallback.md.
Finish by saving the scene (
EditorSceneManager.MarkSceneDirty
+
), then verify.
Installing
Install com.meta.xr.sdk.interaction.ovr
- it pulls in Essentials and the Core SDK. Check
first; if it is listed, do not re-add. Installing only
com.meta.xr.sdk.interaction
is the classic misinstall: interaction model, no OVR binding, no
rig, no
.
csharp
using UnityEditor.PackageManager;
var request = Client.Add("com.meta.xr.sdk.interaction.ovr"); // async - poll request.IsCompleted
Or against a live Editor:
unity command package_add --identifier com.meta.xr.sdk.interaction.ovr --confirm true --wait true
.
A successful add forces a recompile and domain reload - never install a package and use its
API in the same script. Details: installation.md.
Finding the truth: source first, docs second
Don't answer ISDK questions from memory. The installed package is authoritative for
behavior; Meta's docs for intent and design guidance.
Search for the API sources by filename pattern - this works regardless of install
form (registry cache, embedded, or local):
**/com.meta.xr.sdk.interaction*/Editor/QuickActions/Scripts/QuickActionsAPI.cs
**/com.meta.xr.sdk.interaction.ovr*/Editor/QuickActions/Scripts/OVRQuickActionsAPI.cs
Then use the resolved package roots for all subsequent lookups.
Inside Unity,
paths work regardless of install form:
Packages/com.meta.xr.sdk.interaction.ovr/Runtime/Prefabs/...
.
For docs, query
-
vr_docs_search(query, scope="unity")
for the
, then
, and
vr_api_search(query, engine="unity")
for signatures.
Never
hand-construct a doc URL; the namespace varies. Curated map:
documentation.md.
The two APIs are the supported entry points; the machinery under them is not public API. If
you are reaching for reflection to invoke a wizard, you have probably missed a method - re-read
the two files first. The same actions exist under the
GameObject > Interaction SDK
menu as
blocking wizard windows; open one when you want to see what a call will create.
What the docs don't tell you
Each of these is visible only from the source or from a runtime failure.
- You get the optional components too. A call is equivalent to clicking Fix All and
Add All in the wizard.
AddDistanceGrabInteraction
additionally creates a root
that pulls the object back after it goes untouched - delete it to
opt out. Detail: quick-actions-api.md.
- Grab calls make the Rigidbody kinematic (
useGravity = false; isKinematic = true
). Objects
float where you put them; that is intended.
- Created names come from the wizard - , .
Rename afterwards if it matters; components track by reference, so renaming is safe.
- Interactor calls target the / object itself. -derived components on
child objects log
Could not execute HandInteractorWizard
; filter with
.Where(h => h.GetType() == typeof(Hand))
.
- Enum names don't match the components they select. vs. the Movement
Provider in the scene vs. Meta's doc label are three vocabularies - never guess the mapping.
- The wizards expose settings the APIs don't. If you need one that isn't a parameter, use the
menu item or post-process the result. Smooth Locomotion on the rig is the common case.
- Building Blocks are another route to the same result, not a better one. Prefer the APIs: no
block-tracking components, no project-setup side pass, readable names.
- No API means no API. If there is no method for what you want, use the menu item or build it
from components - don't reflect into the wizard.
Always verify in Play mode
ISDK asserts its wiring in
, so a scene that looks correct in the Inspector can still
throw. Edit-time component checks are
not sufficient.
- Clear the console, enter Play mode, read the console, exit.
- Zero errors and zero exceptions is the bar. Every names the exact
component and field - treat it as a real defect.
[MetaXRFeature]: ErrorFormFactorUnavailable xrGetSystem
only means no headset/XRSIM is
attached; OpenXR teardown warnings are benign.
- Some failures are silent - see verification.md.
- After Play mode is clean, confirm on a real headset: build an APK, , then stream
metavr adb logcat --follow --tag Unity
while exercising each interaction.
References
- quick-actions-api.md - signatures, enums, what each call builds
- ovr-quick-actions-api.md - the rig call and the rig it creates
- interaction-model.md - interactor/interactable model, rig anatomy, placement
- installation.md - install paths, dependency chain, assemblies
- verification.md - Play-mode loop, assertion table, silent failures
- documentation.md - curated Meta doc map and lookup recipe
- unity-mcp-fallback.md - running the calls over Unity MCP instead of
Related skills
- - camera rig, , tracking origin, hand tracking support,
Project Setup Tool, AndroidManifest
- - drive a live Editor
hz-xr-simulator-install-and-configure
/ - run and debug beyond Play mode