docs.unity3d.com
Search Results for

    Show / Hide Table of Contents

    Persistent anchors

    AR Foundation 6 introduces an API for persistent anchors that enables you to save anchors in an AR session and load them in subsequent AR sessions. You can use this API to persist the state of your app across multiple sessions in the same physical space. The following sections explain how to use the persistent anchors API.

    A screenshot from a Meta Quest 3 device shows a room with two anchors placed. One anchor has been saved, and a user interface shows buttons to erase that anchor or save the other one.
    The Anchors sample scene shows you how to use the persistent anchor API on supported devices

    Important

    The AR Foundation persistent anchor API is a wrapper for persistent anchor APIs on multiple platforms. Due to the limitations of all supported platforms, you can't save an anchor on one platform and load it on another platform. Refer to the provider plug-in documentation for your target platforms to understand any additional platform-specific details.

    Check support

    The AR Foundation persistent anchor API consists of five optional features of the anchor subsystem:

    • Save anchor
    • Load anchor
    • Erase anchor
    • Get saved anchor IDs
    • Async cancellation

    Refer to the Optional features support table to learn which provider plug-ins support these features, and how to query for this information in C# scripts. The following sections explain each optional feature in more detail, including example code.

    Save anchor

    The save operation takes an anchor as input, saves that anchor to persistent storage, and returns a persistent anchor GUID that you can use later to load or erase the anchor.

    Note

    Some AR platforms may save anchors to the device's local disk, while others may save them to a cloud storage location associated with your app. Refer to the provider plug-in documentation for your target platforms to understand the implementation details of how anchors are saved.

    To save an anchor, use ARAnchorManager.TrySaveAnchorAsync as shown in the following code example:

    async void SaveAnchorAsync(ARAnchorManager manager, ARAnchor anchor)
    {
        var result = await manager.TrySaveAnchorAsync(anchor);
        if (result.status.IsError())
        {
            // handle error
            return;
        }
    
        // Save this value, then use it as an input parameter
        // to TryLoadAnchorAsync or TryEraseAnchorAsync
        SerializableGuid guid = result.value;
    }
    
    Important

    Keep track of the persistent anchor GUIDs returned to you by TrySaveAnchorAsync. Not all platforms support the ability to get your saved persistent anchor GUIDs if you lose them.

    Batch save anchors

    You can save a batch of anchors with ARAnchorManager.TrySaveAnchorsAsync as shown in the following code example:

    async void SaveAnchorsAsync(
        ARAnchorManager manager,
        IEnumerable<ARAnchor> anchors)
    {
        var results = new List<ARSaveOrLoadAnchorResult>();
        await manager.TrySaveAnchorsAsync(anchors, results);
    
        foreach (var saveAnchorResult in results)
        {
            if (saveAnchorResult.resultStatus.IsSuccess())
            {
                // Save the `savedAnchorGuid`s stored in `saveAnchorResults`,
                // then use them as input for TryLoadAnchorsAsync or
                // `TryEraseAnchorsAsync`.
            }
            else
            {
                // Anchor failed to save. Handle error.
            }
        }
    }
    
    Important

    Keep track of the persistent anchor GUIDs that are populated in saveAnchorResults.savedAnchorGuid. Not all platforms support the ability to get your saved persistent anchor GUIDs if you lose them. The AR Foundation Samples GitHub repository contains example code that you can use to save your persistent anchor GUIDs to Unity's Application.persistentDataPath, allowing you to quit your app and then load or erase your saved anchors in subsequent sessions. Any files created by your app and saved to Application.persistentDataPath will be lost if your app is uninstalled.

    By default, batch save anchors sequentially calls ARAnchorManager.TrySaveAnchorAsync on the list of anchors passed in. Platforms can override this behavior with custom implementations for batch save anchors. Refer to your platform's documentation to understand platform specific implementation details.

    Load anchor

    The load operation takes a persistent anchor GUID returned by Save anchor as input, retrieves the associated anchor from persistent storage, and returns a newly created anchor. On the AR Anchor Manager component's next Update step, that anchor will be reported as added.

    To load an anchor, use ARAnchorManager.TryLoadAnchorAsync as shown in the following code example:

    async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid)
    {
        var result = await manager.TryLoadAnchorAsync(guid);
        if (result.status.IsError())
        {
            // handle error
            return;
        }
    
        // You can use this anchor as soon as it's returned to you.
        ARAnchor anchor = result.value;
    }
    

    Batch load anchors

    You can load a batch of anchors with ARAnchorManager.TryLoadAnchorsAsync as shown in the following code example:

    async void LoadAnchorsAsync(
        ARAnchorManager manager,
        IEnumerable<SerializableGuid> savedAnchorGuids)
    {
        var results = new List<ARSaveOrLoadAnchorResult>();
        await manager.TryLoadAnchorsAsync(
            savedAnchorGuids,
            results,
            OnIncrementalResultsAvailable);
    
        foreach (var loadAnchorResult in results)
        {
            if (loadAnchorResult.resultStatus.IsSuccess())
            {
                // Anchor with results.savedAnchorGuid was successfully loaded.
            }
            else
            {
                // Anchor with results.savedAnchorGuid failed to load.
            }
        }
    }
    
    void OnIncrementalResultsAvailable(ReadOnlyListSpan<ARSaveOrLoadAnchorResult> loadAnchorResults)
    {
        foreach (var loadAnchorResult in loadAnchorResults)
        {
            // You can use these anchors immediately without waiting for the
            // entire batch to finish loading.
            // loadAnchorResult.resultStatus.IsSuccess() will always be true
            // for anchors passed to the incremental results callback.
            ARAnchor loadedAnchor = loadAnchorResult.anchor;
        }
    }
    

    The order in which anchors are loaded isn't guaranteed to match the order they were requested in. You can find the associated persistent anchor GUID of an anchor with LoadAnchorResult.savedAnchorGuid.

    By default, ARAnchorManager.TryLoadAnchorsAsync sequentially calls ARAnchorManager.TryLoadAnchorAsync on the list of saved persistent anchor GUIDs. Platforms can override this behavior with custom implementations for batch load anchors. Refer to your platform's documentation to understand platform specific implementation details.

    Incremental load results

    ARAnchorManager.TryLoadAnchorsAsync accepts a callback that will be invoked each time a subset of requested anchors is loaded. This enables you to work with anchors as soon as they become available without waiting for the entire load request to complete. You should use the incremental results callback to ensure you're notified when an anchor has loaded before ARAnchorManager.trackablesChanged is raised. The final result from a batch load request isn't guaranteed to complete before ARAnchorManager.trackablesChanged. To ignore the incremental results, pass null for the callback.

    By default, an incremental result is returned for each anchor as it's loaded. Platforms may override the default behavior and load groups of anchors incrementally allowing them to load some anchors immediately and some anchors more slowly, still guaranteeing to pass through the incremental results callback before ARAnchorManager.trackablesChanged. Refer to your platform's documentation to understand platform specific implementation details.

    The incremental results callback passes a ReadOnlyListSpan<ARSaveOrLoadAnchorResult> when invoked that provides a read-only slice of the List<ARSaveOrLoadAnchorResult> output list passed into ARAnchorManager.TryLoadAnchorsAsync.

    When a request is made to load a batch of anchors, some anchors can fail to load. However, results reported during the incremental results callback will always have a success XRResultStatus. Only the final results stored in the List<ARSaveOrLoadAnchorResult> output list after the load request completes can contain a result with an error StatusCode. Therefore, when working with the final results, you should check the resultStatus of each result before using the anchor with resultStatus.IsSuccess() or resultStatus.IsError().

    Erase anchor

    The erase operation takes a persistent anchor GUID returned by Save anchor as input, erases that anchor from persistent storage, and returns a status indicating if the operation was successful.

    Note

    The save and erase operations only modify the persistent storage associated with an anchor, not the tracking state of the anchor. For instance, if you create an anchor, save it, then immediately erase it, the persistent storage associated with the anchor will be erased, but the anchor itself will not be removed from the scene.

    To erase an anchor, use ARAnchorManager.TryEraseAnchorAsync as shown in the following code example:

    async void EraseAnchorAsync(ARAnchorManager manager, SerializableGuid guid)
    {
        var status = await manager.TryEraseAnchorAsync(guid);
        if (status.IsError())
        {
            // handle error
            return;
        }
    
        // The anchor was successfully erased.
    }
    

    Batch erase anchors

    You can erase a batch of anchors with ARAnchorManager.TryEraseAnchorsAsync as shown in the following code example:

    async void EraseAnchorsAsync(
        ARAnchorManager manager,
        IEnumerable<SerializableGuid> savedAnchorGuids)
    {
        var eraseAnchorResults = new List<XREraseAnchorResult>();
        await manager.TryEraseAnchorsAsync(savedAnchorGuids, eraseAnchorResults);
        foreach (var eraseAnchorResult in eraseAnchorResults)
        {
            if (eraseAnchorResult.resultStatus.IsSuccess())
            {
                // anchor was successfully erased.
            }
            else
            {
                // anchor failed to erase
            }
        }
    }
    

    By default, batch erase anchors sequentially calls ARAnchorManager.TryEraseAnchorAsync on the list of saved persistent anchor GUIDs. Platforms can override this behavior with custom implementations for batch erase anchors. Refer to your platform's documentation to understand platform specific implementation details.

    Get saved anchor IDs

    Some platforms support the ability to get a list of your currently saved anchors. If your app has successfully kept track of its state across usages of save anchor and erase anchor, you have no reason to use this API. However, if you lose track of your currently saved anchors for any reason, this API is a useful way to recover them, allowing you to subsequently load or erase your saved anchors.

    The following example code demonstrates how to get saved anchor IDs:

    async void GetSavedAnchorIdsAsync(ARAnchorManager manager)
    {
        // If you need to keep the saved anchor IDs longer than a frame, use
        // Allocator.Persistent instead, then remember to Dispose the array.
        var result = await manager.TryGetSavedAnchorIdsAsync(Allocator.Temp);
    
        if (result.status.IsError())
        {
            // handle error
            return;
        }
    
        // Do something with the saved anchor IDs
        NativeArray<SerializableGuid> anchorIds = result.value;
    }
    

    Async cancellation

    AR Foundation's persistent anchors API is entirely asynchronous. If your target platform supports the ability to cancel async operations in progress, you can use the CancellationToken input parameter of the other persistent anchor methods. Otherwise, this input parameter is ignored on platforms that do not support cancellation.

    The following example code demonstrates how to cancel an async operation:

    void AsyncCancellation(ARAnchorManager manager)
    {
        // Create a CancellationTokenSource to serve our CancellationToken
        var cts = new CancellationTokenSource();
    
        // Use one of the other methods in the persistent anchor API
        var awaitable = manager.TryGetSavedAnchorIdsAsync(Allocator.Temp, cts.Token);
    
        // Cancel the async operation before it completes
        cts.Cancel();
    }
    
    In This Article
    Back to top
    Copyright © 2025 Unity Technologies — Trademarks and terms of use
    • Legal
    • Privacy Policy
    • Cookie Policy
    • Do Not Sell or Share My Personal Information
    • Your Privacy Choices (Cookie Settings)