public class SomeController : MonoBehaviour { // // Private members // private GameObject anchorParent; private bool mTriedSpatialAnchor; // // Public methods // public void OnBroadcastLevelButtonPressed() { ResetSpatialAnchor(); OnBroadcastLevelButtonPressedImpl(); } private ResetSpatialAnchor() { if ( anchorParent.GetComponent() != null ) { Destroy( anchorParent.GetComponent() ); } anchorParent.transform.position = Vector3.zero; anchorParent.transform.localEulerAngles = Vector3.zero; } private void OnBroadcastLevelButtonPressedImpl() { // First, create a new local anchor UpdateProgressText( "Creating spatial anchor..." ); var spatialAnchor = anchorParent.GetComponent(); if ( spatialAnchor == null || !spatialAnchor.Created || !spatialAnchor.Localized ) { if ( mTriedSpatialAnchor ) { UpdateProgressText( "Spatial anchor could not be created" ); if ( UnityEngine.Application.platform == RuntimePlatform.WindowsEditor ) { StartCoroutine( CleanupExistingAnchorsAndSaveNewAnchor() ); } return; } mTriedSpatialAnchor = true; anchorParent.AddComponent(); // Creation can take a few seconds, in which case retry LeanTween.delayedCall( 2, OnBroadcastLevelButtonPressedImpl ); return; } // Next, clean up previous cloud anchor StartCoroutine( CleanupExistingAnchorsAndSaveNewAnchor() ); } private System.Collections.IEnumerator CleanupExistingAnchorsAndSaveNewAnchor() { // Existing spatial anchors appear to be cleaned up in the cloud after 24 hours: // https://communityforums.atmeta.com/t5/Unity-VR-Development/Spatial-Anchors-how-to-view-delete-in-profile-privacy/m-p/1097297 UpdateProgressText( "Cleaning up existing spatial anchors..." ); using ( var webRequest = new UnityWebRequest( SERVER_URL + "/rest/v1/deleteAll" ) ) { webRequest.SetRequestHeader( "Content-Type", "application/json" ); webRequest.downloadHandler = new DownloadHandlerBuffer(); yield return webRequest.SendWebRequest(); if ( webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError ) { UpdateProgressText( "Unable to connect: " + ToNiceErrorMessage( webRequest ) ); yield break; } // Save our new anchor var spatialAnchor = anchorParent.GetComponent(); Action saveNewAnchor = () => { UpdateProgressText( "Saving spatial anchor (takes a minute)..." ); OVRSpatialAnchor.SaveOptions saveOptions; saveOptions.Storage = OVRSpace.StorageLocation.Cloud; if ( spatialAnchor == null ) { if ( UnityEngine.Application.platform == RuntimePlatform.WindowsEditor ) { UpdateProgressText( "Broadcasting locally" ); return; } UpdateProgressText( "Unable to save spatial anchor" ); return; } if ( !spatialAnchor.Created ) { UpdateProgressText( "Failed to create spatial anchor" ); return; } if ( !spatialAnchor.Localized ) { UpdateProgressText( "Failed to localize spatial anchor" ); return; } var saveAsyncTask = spatialAnchor.SaveAsync( saveOptions ); saveAsyncTask.ContinueWith( ( isSuccessful ) => { if ( isSuccessful ) { UpdateProgressText( "Success!" ); return; } if ( saveAsyncTask.IsFaulted ) { UpdateProgressText( saveAsyncTask.GetException().ToString() ); return; } // Retry if timeout UpdateProgressText( "Retrying to save spatial anchor..." ); var saveAsyncTask2 = spatialAnchor.SaveAsync( saveOptions ); saveAsyncTask2.ContinueWith( ( isSuccessful2 ) => { if ( isSuccessful2 ) { UpdateProgressText( "Success! Now click 'Watch' in other VR headset" ); return; } if ( saveAsyncTask2.IsFaulted ) { UpdateProgressText( saveAsyncTask2.GetException().ToString() ); return; } UpdateProgressText( "Unable to save spatial anchor\n\nMake sure 'Share Point Cloud Data' is on in Meta Settings, and that you have a fast Internet connection (mobile hotspot may be insufficient)" ); } ); } ); }; var itemsToDelete = JsonConvert.DeserializeObject( webRequest.downloadHandler.text ); if ( itemsToDelete.Count == 0 ) { saveNewAnchor(); yield break; } // Clean up previous cloud anchors (if any) var loadOptions = new OVRSpatialAnchor.LoadOptions(); loadOptions.Uuids = new Guid[] { }; foreach ( var itemToDelete in itemsToDelete ) { var idString = (string) itemToDelete["id"]; Debug.LogWarning( "Deleting spatial anchor " + idString ); loadOptions.Uuids.Append( Guid.Parse( idString ) ); } loadOptions.StorageLocation = OVRSpace.StorageLocation.Cloud; OVRSpatialAnchor.LoadUnboundAnchorsAsync( loadOptions ).ContinueWith( ( unboundAnchors ) => { // Just-in-case cleanup var temporarySpatialAnchor = spatialAnchorDump.GetComponent(); if ( temporarySpatialAnchor != null ) { Destroy( temporarySpatialAnchor ); } if ( unboundAnchors == null ) { saveNewAnchor(); return; } // Create temporary spatial anchor temporarySpatialAnchor = spatialAnchorDump.AddComponent(); var anchorsToErase = unboundAnchors.ToList(); // Erase all existing anchors Action eraseAnchor = null; eraseAnchor = () => { while ( anchorsToErase.Count > 0 ) { var anchorToErase = anchorsToErase[0]; anchorsToErase.RemoveAt( 0 ); anchorToErase.BindTo( temporarySpatialAnchor ); var eraseOptions = new OVRSpatialAnchor.EraseOptions(); eraseOptions.Storage = OVRSpace.StorageLocation.Cloud; temporarySpatialAnchor.EraseAsync( eraseOptions ).ContinueWith( ( isSuccessful ) => { eraseAnchor(); return; } ); } // Cleanup temporary spatial anchor Destroy( temporarySpatialAnchor ); saveNewAnchor(); }; eraseAnchor(); } ); } } public void OnMonitorLevelButtonPressed() { ResetSpatialAnchor(); StartCoroutine( OnMonitorLevelButtonPressedImpl( true ) ); } private System.Collections.IEnumerator OnMonitorLevelButtonPressedImpl() { using ( var webRequest = new UnityWebRequest( SERVER_URL + "/rest/v1/receive" ) ) { webRequest.SetRequestHeader( "Content-Type", "application/json" ); webRequest.downloadHandler = new DownloadHandlerBuffer(); yield return webRequest.SendWebRequest(); if ( webRequest.result == UnityWebRequest.Result.ConnectionError || webRequest.result == UnityWebRequest.Result.ProtocolError ) { UpdateProgressText( "Unable to connect: " + ArenaUtils.ToNiceErrorMessage( webRequest ) ); yield break; } var receivedItem = JsonConvert.DeserializeObject( webRequest.downloadHandler.text ); if ( receivedItem == null || receivedItem["id"] == null ) { UpdateProgressText( "Click 'Broadcast' in other VR headset first" ); yield break; } // Load previous cloud anchor UpdateProgressText( "Connecting to spatial anchor..." ); try { var loadOptions = new OVRSpatialAnchor.LoadOptions(); loadOptions.Uuids = new Guid[] { Guid.Parse( (string) receivedItem["id"] ) }; loadOptions.StorageLocation = OVRSpace.StorageLocation.Cloud; OVRSpatialAnchor.LoadUnboundAnchorsAsync( loadOptions ).ContinueWith( ( unboundAnchors ) => { if ( unboundAnchors == null ) { if ( UnityEngine.Application.platform == RuntimePlatform.WindowsEditor ) { UpdateProgressText( "Monitoring locally" ); anchorParent.transform.position = new Vector3( 1, anchorParent.transform.position.y, 1 ); anchorParent.transform.eulerAngles = new Vector3( 0, 45, 0 ); return; } UpdateProgressText( "No spatial anchor found" ); return; } // Localize... if ( unboundAnchors[0].Localized ) { UpdateProgressText( "Success!" ); } else { UpdateProgressText( "Localizing spatial anchor..." ); unboundAnchors[0].LocalizeAsync().ContinueWith( ( localizedAnchor ) => { UpdateProgressText( "Success!" ); } ); } } ); } catch ( Exception e ) { UpdateProgressText( "Could not connect to spatial anchor: " + e.Message ); } } } }