OVRSpatialAnchor component and poll the creation status until the runtime assigns a UUIDAssets/StarterSamples/Usage/, open the SpatialAnchor.unity scene, and build and deploy to your Quest device following the repository’s build instructions. When the app launches, you see passthrough enabled with a menu attached to your right controller, starting in Select mode.| File/Scene | What it demonstrates |
|---|---|
SpatialAnchor.unity | Scene setup with passthrough enabled, controller anchors, and UI components for two-mode interaction (Create/Select) |
Scripts/Anchor.cs | Individual anchor behavior: creation lifecycle polling, save/erase/hide operations, per-anchor UI menu, visual feedback, and billboard rendering |
Scripts/AnchorUIManager.cs | Mode management (Create/Select), raycasting for anchor selection, anchor placement, main menu navigation, and delegate-based input handling |
Scripts/SpatialAnchorLoader.cs | Batch loading of saved anchors by UUID, asynchronous localization, and binding to instantiated GameObjects |
Scripts/AnchorUuidStore.cs | PlayerPrefs-based UUID persistence for saving and retrieving anchor identifiers across sessions |
Editor/SpatialAnchorLoaderEditor.cs | Custom Inspector button and menu item for clearing all saved anchor UUIDs during development |
OVRSpatialAnchor component, which is added automatically to the anchor prefab via [RequireComponent(typeof(OVRSpatialAnchor))] in Anchor.cs. When a new anchor is instantiated, the runtime begins creation asynchronously. The sample polls the PendingCreation property in a coroutine until the anchor is fully created, then accesses the runtime-assigned Uuid and displays tracking status. This pattern is shown in Anchor.cs:while (_spatialAnchor && _spatialAnchor.PendingCreation)
yield return null;
ContinueWith with a state parameter, as seen in Anchor.cs:_spatialAnchor.SaveAnchorAsync().ContinueWith((result, anchor) =>
{
if (result.Success) anchor.OnSave();
}, this);
SpatialAnchorLoader.cs:var result = await OVRSpatialAnchor.LoadUnboundAnchorsAsync(uuidBatch, _unboundAnchors);
AnchorUuidStore class provides a simple PlayerPrefs-based storage layer for anchor UUIDs. It stores a count at key “numUuids” and individual UUIDs at keys “uuid0”, “uuid1”, etc. When anchors are saved, their UUIDs are added to this store. When the app restarts, the loader reads all stored UUIDs and passes them to LoadUnboundAnchorsAsync to restore the anchors. The code comments explicitly note this as a sample simplification; production apps should use more robust storage.SpatialAnchorLoader.cs. For each returned UnboundAnchor, the sample checks Localized; if not localized, it calls LocalizeAsync(). Once localized, the sample retrieves the world-space pose with TryGetPose(out pose), instantiates the anchor prefab at that position, and calls BindTo to connect the loaded data to the runtime component:unboundAnchor.TryGetPose(out var pose); var spatialAnchor = Instantiate(_anchorPrefab, pose.position, pose.rotation); unboundAnchor.BindTo(spatialAnchor);
AnchorUIManager.cs, the raycast originates from a tracked device transform and checks for Anchor components on hit colliders. The Physics.Raycast uses infinite range to detect distant anchors, while the line renderer visualizes only the first 10 meters for clarity. When hovering an anchor, the sample calls OnHoverStart() to change the anchor’s emission color to yellow. Selection is triggered by pressing the A button, which invokes a delegate that swaps between PlaceAnchor and SelectAnchor functions depending on the current mode:if (OVRInput.GetDown(OVRInput.RawButton.A))
_primaryPressDelegate?.Invoke();
OVRSpatialAnchor