Implementing Spatial Anchors in Unity for HoloLens 2 with MRTK

The Mixed Reality Toolkit (MRTK) provides a cross-platform framework for building spatially aware applications targeting HoloLens, Windows Mixed Reality, and other XR devices. It includes input systems, UI components, and foundational utilities to accelerate development.

Project Setup

Begin with a new Unity project using Unity 2019.4.40—this version is required for compatibility with MRTK 2.5.3 and HoloLens 2 deployment via UWP.

Switch the build platform to Universal Windows Platform (UWP) via File > Build Settings. Configure the following:

  • Target Device: HoloLens
  • Architecture: ARM64
  • Build Type: D3D
  • Target SDK Version: Latest installed
  • Minimum Platform Version: 10.0.18362.0 or higher
  • Build Configuration: Release

Installing MRTK via Mixed Reality Feature Tool

Download and run the Mixed Reality Feature Tool. Point it to you're Unity project diretcory and select:

  • MixedRealityToolkit.Foundation (v2.5.3)

Note: OpenXR support is not available in Unity 2019; it requires Unity 2020.3+.

After importing, restart Unity when prompted.

Player Settings for UWP

In Project Settings > Player:

  • Under XR Settings, enable Virtual Reality Supported and add Windows Mixed Reality.
  • In Publishing Settings, generate or assign a signing certificate (required for device deployment).
  • Set Scripting Backend to IL2CPP and API Compatibility Level to .NET Standard 2.1.

Adding Spatial Anchor Functionality

Spatial anchors allow holograms to persist in physical space across sessions. The following script manages anchor creation, saving, loading, and deletion using WorldAnchorStore:

using System.Collections;
using UnityEngine;
using UnityEngine.XR.WSA.Persistence;
using UnityEngine.XR.WSA;
using TMPro;

public class SpatialAnchorManager : MonoBehaviour
{
    public GameObject anchorPrefab;
    public TextMeshPro statusText;
    public Transform sceneObject;

    private WorldAnchorStore anchorStore;
    private WorldAnchor currentAnchor;
    private GameObject anchorInstance;
    private const string AnchorKey = "PersistentAnchor";

    void Start()
    {
        WorldAnchorStore.GetAsync(OnStoreLoaded);
    }

    void OnStoreLoaded(WorldAnchorStore store)
    {
        anchorStore = store;
        LoadExistingAnchor();
    }

    public void CreateNewAnchor()
    {
        if (anchorInstance == null)
        {
            anchorInstance = Instantiate(anchorPrefab, Camera.main.transform.position + Camera.main.transform.forward * 2f, Quaternion.identity);
            anchorInstance.name = "PersistentAnchorObject";
        }

        currentAnchor = anchorInstance.GetComponent<WorldAnchor>() ?? anchorInstance.AddComponent<WorldAnchor>();

        if (currentAnchor.isLocated)
        {
            SaveAnchor();
        }
        else
        {
            currentAnchor.OnTrackingChanged += OnAnchorLocated;
        }
    }

    void OnAnchorLocated(WorldAnchor anchor, bool located)
    {
        if (located)
        {
            anchor.OnTrackingChanged -= OnAnchorLocated;
            SaveAnchor();
        }
    }

    void SaveAnchor()
    {
        if (anchorStore != null && currentAnchor != null)
        {
            anchorStore.Save(AnchorKey, currentAnchor);
            LogMessage("Anchor saved.");
        }
    }

    void LoadExistingAnchor()
    {
        if (anchorStore.AnchorExists(AnchorKey))
        {
            anchorInstance = Instantiate(anchorPrefab);
            currentAnchor = anchorStore.Load(AnchorKey, anchorInstance);
            if (currentAnchor != null)
            {
                LogMessage("Anchor loaded.");
                sceneObject.SetParent(anchorInstance.transform, false);
            }
        }
        else
        {
            LogMessage("No saved anchor found.");
        }
    }

    public void DeleteSavedAnchor()
    {
        if (anchorStore != null && anchorStore.Delete(AnchorKey))
        {
            if (currentAnchor != null) Destroy(currentAnchor);
            currentAnchor = null;
            LogMessage("Anchor deleted.");
        }
    }

    void LogMessage(string msg)
    {
        if (statusText != null)
            statusText.text += msg + "\n";
        Debug.Log(msg);
    }
}

Attach this script to a manager object in the scene. Assign a visual prefab (e.g., a cube) to anchorPrefab, a TMP text component for feedback, and the hologram you wish to anchor to sceneObject.

Building and Deploying to HoloLens 2

Use MRTK Build Window (Mixed Reality > Toolkit > Utilities > Build Window) to generate the UWP solution. Set the target device to HoloLens and build.

Open the generated .sln file in Visual Studio. Ensure:

  • Solution Platform: ARM64
  • Configuration: Release

If Visual Studio reports missing Windows SDKs, verify that the Windows 10 SDK (10.0.18362.0 or newer) is installed via the Visual Studio Installer. The SDK must reside in C:\Program Files (x86)\Windows Kits\10.

To deploy:

  1. Enable Developer Mode on both PC and HoloLens 2.
  2. Find the HoloLens IP under Settings > Network & Internet > Wi-Fi > Properties.
  3. Navigate to http://<HoloLens_IP> in a browser to access the Device Portal.
  4. Under Apps, upload the .msix package from the build output (AppPackages folder).
  5. Install and launch the app from the HoloLens start menu.

Once deployed, the app will save the anchor on first run and reposition the hologram at the same real-world location on subsequent launches.

Tags: unity HoloLens2 MRTK Spatial Anchors Mixed Reality

Posted on Thu, 16 Jul 2026 16:54:32 +0000 by jkatcherny