Develop
Develop
Select your platform

Use Spatial Anchors

Updated: Sep 4, 2024

Set Up Project

  1. In Edit > Project Settings > OculusVR:
    1. In the General section, go to the XR API list, and then select OVRPlugin OpenXR.
    2. In the Mobile section, select Passthrough Enabled and Anchor Support.
  2. Follow the typical steps for adding a Passthrough layer to an Unreal project.

Asynchronous Anchor Events

Most Spatial Anchors functions are asynchronous. That is, the results of the functions can’t be processed immediately as part of the return value and instead the results are broadcast later. In a Blueprint, this is performed using latent actions. There are success and failure execution pins available for every asynchronous Spatial Anchors function. In code, this is performed with a callback delegate passed to the function call.

Call Asynchronous Blueprint Function

Using one of the latent Spatial Anchors actions, you can respond to success or failure events for an asynchronous call by connecting to the output execution pins. Keep in mind that the success and failure execution pins don’t execute in the same frame as the start of the function call. To execute logic that isn’t dependent on the asynchronous results of Spatial Anchors Blueprints you can continue the execution flow by way of the unnamed immediate execution pin.
example of async logic
After the asynchronous function is completed, the success or failure pins execute. If the request was successful you can use the output data members that are part of the latent action.

Call Asynchronous C++ Function

Executing an asynchronous function in C++ requires passing a delegate as a parameter to the function that starts the asynchronous work. This delegate is executed in the case of success or failure and has a parameter that determines success as well as other parameters you can use in the success case. For more information, see “Delegates” in the Unreal Engine documentation.
For example:
void CreateAnchorCallback(bool Success, UOculusAnchorComponent* Anchor)
{
    UE_LOG(LogTemp, Log, TEXT(“Success: %d”), Success);
}

// Somewhere else in the code
OculusAnchors::CreateSpatialAnchor(
    TargetActor->GetActorTransform(),
    TargetActor,
    FOculusSpatialAnchorCreateDelegate::CreateRaw(SomePointer, &SomeClass::CreateAnchorCallback);

Persist Anchors

Each anchor that a user wants to re-use over multiple sessions must have the anchor’s UUID saved to the headset’s local storage. Anchors saved to the cloud are not persistent indefinitely. Anchors saved using SaveAnchor or SaveAnchorList can be saved to the headset or to the cloud. This storage is accessible to a user and can be queried by way of QueryAnchors. You can load a specific set of anchors by providing a list of anchor UUIDs to the function call.
For locally persistent spatial anchors we recommend you save spatial anchors to an app-specific, persistent save game, and query anchors by way of a list of saved UUIDs. For more information, see “Saving and Loading Your Game” in the Unreal Engine documentation.

Use the Blueprint Interface

There’s no need to add any mandatory components or managers to a scene to use spatial anchors. You can access the Spatial Anchors system from Blueprints and we provide a set of Blueprint nodes that you can use. For more information, see Blueprint API Reference.

Create Spatial Anchor Example

  1. Spawn an actor.
    • This actor does not have to have a spatial anchor component attached.
  2. Call the Blueprint method Oculus Async Create Spatial Anchor and pass in the anchor transform and actor as parameters.
    • If the specified actor does not have a spatial anchor component attached, a new component is constructed for you.
  3. Execute logic using the Success and Failure pins.
    • The resulting Anchor component output is only valid in the case of success.
create spatial anchor

Erase Spatial Anchor Example

  1. Call the Blueprint method Oculus Async Erase Anchor.
  2. Pass in an actor that has a spatial anchor attached.
  3. Execute logic using the Success or Failure pins.
    • The resulting Actor and UUID output is only valid in the case of success.
    • The UUID of the destroyed anchor is returned for your bookkeeping.
Erase can also work as an “unsave” operation. It is up to you to decide whether you destroy the anchor actor representation as part of Erase.
Note: Cloud saved spatial anchors cannot be erased from storage.
erase spatial anchor

Save Spatial Anchor Example

You can save a specific spatial anchor or multiple anchors.
To save a specific spatial anchor:
  1. Call the Blueprint method Oculus Async Save Anchor.
    • Specify the target actor that has a spatial anchor attached.
    • Specify the storage location, this is either local or cloud.
  2. Execute logic using the Success or Failure pins. The output Anchor Component is only valid if success is executed.
save spatial anchor
To save multiple anchors:
  1. Call the Blueprint method Oculus Async Save Anchors.
    • Specify the target actors that have spatial anchors attached.
    • Specify the storage location, this is either Local or Cloud..
  2. Execute logic using the Success or Failure pins. The output Anchor Components array is only valid if success is executed.
save spatial anchor

Load Spatial Anchor Example

  1. Get a list of Anchor UUIDs to request loading.
    • This can be from a save game or anywhere else you are storing UUIDs.
  2. Call the Blueprint method Oculus Async Query Anchors.
    • Specify the location to save to, this can be local or cloud.
  3. If successful, iterate the query results and then construct new actors using the Spawn Oculus Anchor Actor function.
load spatial anchor

Using the Spatial Anchors C++ API

The Spatial Anchors C++ API is accessed through the OculusAnchors header file. Each asynchronous function that is part of the interface takes a delegate parameter as the last argument to the function. This delegate has a boolean parameter as its first argument which represents the success or failure state of that call. The following table lists each delegate and their associated parameters.
DelegateParameters
FOculusSpatialAnchorCreateDelegate
EOculusResult::Type Result
UOculusAnchorComponent* Anchor
FOculusAnchorEraseDelegate
EOculusResult::Type Result
FUUID AnchorUUID
FOculusAnchorSaveDelegate
EOculusResult::Type Result
UOculusAnchorComponent* Anchor
FOculusAnchorSaveListDelegate
bool Success
const TArray<UOculusAnchorComponent*> and SavedAnchors
FOculusAnchorQueryDelegate
EOculusResult::Type Result
const TArray<FOculusSpaceQueryResult> and Results
Note: The example code below makes use of lambda delegate bindings. This isn’t a requirement to use the API and is only used to shorten the code being shown.

Create Spatial Anchor Example

  1. Spawn an actor.
    • This actor does not have to have a spatial anchor component attached.
  2. Call the method OculusAnchors::CreateSpatialAnchor and pass the anchor transform, actor, and result delegate in as parameters.
    • If the specified actor does not have a spatial anchor component attached, a new component is constructed for you.
  3. Execute logic in the results delegate.
    • The resulting spatial anchor component is only valid in the case of success.
AActor* NewActor = GetWorld()->SpawnActor<AActor>();

OculusAnchors::FOculusAnchors::CreateSpatialAnchor(NewActor->GetActorTransform(),
NewActor, FOculusSpatialAnchorCreateDelegate::CreateLambda([](bool Success,
UOculusAnchorComponent* AnchorComponent)
    {
        // Post-create logic in here
    })
);

Erase Spatial Anchor Example

  1. Retrieve an anchor from an actor.
  2. Call the method OculusAnchors::EraseAnchor and pass the anchor component and result delegate in as parameters.
  3. Execute logic in the results delegate.
    • The resulting UUID is only valid in the case of success.
UOculusAnchorComponent* AnchorComponent = … // Retrieve anchor

OculusAnchors::FOculusAnchors::EraseAnchor(AnchorComponent,
    FOculusAnchorEraseDelegate::CreateLambda([](bool Success, FUUID UUID)
    {
        // Post-erase logic here
    })
);

Save Spatial Anchor Example

  1. Retrieve an anchor from an actor.
  2. Call the method OculusAnchors::SaveAnchor and pass the anchor component and result delegate in as parameters.
  3. Execute logic in the results delegate.
    • The resulting AnchorComponent is only valid in the case of success.
UOculusAnchorComponent* AnchorComponent = … // Retrieve anchor

OculusAnchors::FOculusAnchors::SaveAnchor(AnchorComponent,
EOculusSpaceStorageLocation::Local, FOculusAnchorSaveDelegate::CreateLambda([](bool
Success, UOculusAnchorComponent* AnchorComponent)
    {
        // Post-save logic here
    })
);

Save Spatial Anchor List Example

  1. Retrieve an anchor from an actor.
  2. Call the method OculusAnchors::SaveAnchor and pass the anchor component and result delegate in as parameters.
  3. Execute logic in the results delegate. The resulting AnchorComponent is only valid in the case of success.
UOculusAnchorComponent* AnchorComponent = … // Retrieve anchor

OculusAnchors::FOculusAnchors::SaveAnchor(AnchorComponent,
EOculusSpaceStorageLocation::Local, FOculusAnchorSaveDelegate::CreateLambda([](bool
Success, UOculusAnchorComponent* AnchorComponent)
    {
        // Post-save logic here
    })
);

Query Spatial Anchors Example

  1. Generate a list of UUIDs,
  2. Call the method OculusAnchors::QueryAnchors and pass the list of UUIDs, the query location, the maximum number of anchors to load, and the result delegate in as parameters.
  3. Execute logic in the results delegate.
    • Spawn an actor of your choice, there is a helper method you can use called SpawnActorWithAnchorQueryResults.
uint32 MaxAnchorsToLoad = 16;
EOculusSpaceStorageLocation QueryLocation = EOculusSpaceStorageLocation::Local;
TArray<FUUID> AnchorUUIDs = ...;

OculusAnchors::FOculusAnchors::QueryAnchors(AnchorUUIDs, QueryLocation, MaxAnchorsToLoad,
    FOculusAnchorQueryDelegate::CreateLambda([](bool Success, const
TArray<FOculusSpaceQueryResult>& Results)
    {
        if(Success)
        {
            for(auto& it : Results)
            {
                UOculusAnchorBPFuctionLibrary::SpawnActorWithAnchorQueryResults(
                    GetWorld(),
                    it,
                    AActor::StaticClass(), // Your class here
                    Owner,
                    Instigator,
                    CollisionHandlingMethod
                );
            }
        }
    })
);

Error Codes

We now expose error codes as the result of Spatial Anchors functions. These error codes are available as an output from the Blueprint API and the C++ API. Error codes are defined in the EOculusResult enum. Most enumeration results are general but there are specific enumerations available for spatial anchors.
UENUM(BlueprintType)
namespace EOculusResult
{
	enum Type
	{
		Success = 0,
		Success_EventUnavailable = 1,
		Success_Pending = 2,

		/// Failure
		Failure = -1000,
		Failure_InvalidParameter = -1001,
		Failure_NotInitialized = -1002,
		Failure_InvalidOperation = -1003,
		Failure_Unsupported = -1004,
		Failure_NotYetImplemented = -1005,
		Failure_OperationFailed = -1006,
		Failure_InsufficientSize = -1007,
		Failure_DataIsInvalid = -1008,
		Failure_DeprecatedOperation = -1009,
		Failure_ErrorLimitReached = -1010,
		Failure_ErrorInitializationFailed = -1011,

		/// Space error cases
		Failure_SpaceCloudStorageDisabled = -2000,
		Failure_SpaceMappingInsufficient = -2001,
		Failure_SpaceLocalizationFailed = -2002,
		Failure_SpaceNetworkTimeout = -2003,
		Failure_SpaceNetworkRequestFailed = -2004
	};
}

Explanation of error values

  • Failure_SpaceCloudStorageDisabled: Saving anchors to cloud storage is not permitted by the user.
  • Failure_SpaceMappingInsufficient: The user did not move around enough for a reliable localization. The user should walk and look around their space before calling save again. For example, the user could walk in a large circle around the center of their playspace.
  • Failure_SpaceLocalizationFailed: The system failed to localize the shared anchor. This could be because the shared anchor is not near the user when calling Query.
  • Failure_SpaceNetworkTimeout: Network operation timed out.
  • Failure_SpaceNetworkRequestFailed: Network operation failed.

Migrating from Legacy Spatial Anchors

The API for Spatial Anchors changed to an improved version in Oculus SDK v43. Any legacy versions of Spatial Anchors in existing projects need to be migrated to this current version.
In general, all legacy Spatial Anchors calls that relied on event listeners have now been changed to latent actions. This means you can fold the event logic into a single execution flow graph rather than relying on a bifurcated event response model.
For example, the legacy Spatial Anchors logic for creating a spatial anchor looked like the following diagram:
legacy spatial anchor
You would:
  1. Bind a callback, usually in begin play, to the Spatial Anchors event you are listening for.
  2. Execute a Spatial Anchors method, in this case “Create Spatial Anchor”.
  3. Wait for the bound callback to execute.
  4. Complete any work necessary in the callback.
In contrast, the current Spatial Anchors logic now resembles the following diagram:
current spatial anchor
  1. Execute a latent action Blueprint nodes.
  2. Execute Success of Failure pins based on results.
The following table lists the legacy Spatial Anchors calls and their new latent action names. For more information, see Spatial Anchors Blueprint Reference.
BindingLatent Action
Create Spatial Anchor
Oculus Async Create Spatial Anchor
Erase Spatial Anchor
Oculus Async Erase Anchor
Save Spatial Anchor
Oculus Async Save Anchor
Query All Local Spatial Anchors
Oculus Async Query Anchors
You can use the SpatialAnchorsSample sample project as an example of how to use Spatial Anchors after migrating.
Did you find this page helpful?
Thumbs up icon
Thumbs down icon