Android XR

使用 Unity 在 Android XR 上建構空間內容

喇叭

  • Levana Chen

    軟體工程師

  • Jennifer Stanton

    資深軟體工程師

關於

講座簡介

瞭解如何使用 Unity 和 Android XR,在真實世界中錨定及保留 3D 體驗。本課程將介紹 Spatial Entities 架構、Cloud Anchors 和 Geospatial API,示範如何建構可記住實體位置,並在不同工作階段同步多使用者空間資料的情境感知應用程式。

我們將探討空間實體的架構,瞭解如何將數位資產錨定至實體物件、表面和地理位置。您將學會實用的 Unity 工作流程,包括對應空間、在不同工作階段中保留內容,以及建立共用、可辨識位置的 AR/XR 體驗。

摘要

  • AR Foundation 使用 OpenXR 擴充功能,提供跨 XR 平台的統一指令碼 API。可讓您呈現及查詢平面和網格等真實世界特徵。
  • 永久錨點可在工作階段和裝置重新啟動期間,穩定數位內容。您可以使用 TrySaveAnchorAsync 保留這些資料,並使用序列化 GUID 重新載入。
  • Spatial Entities 架構取代了可追蹤的擴充功能,提供跨平台支援,並提升追蹤定界框和標記等項目的效能。
  • 在授予 Android XR 權限之前,應停用管理員元件。避免因敏感環境資料存取受限,導致子系統初始化失敗。
  • Geospatial API 會使用 Google Cloud ARCore 服務和 VPS,將內容放置在真實世界的戶外位置。

資源

Gemini Notebook

主題

  • android-xr
  • 團結
  • ar-foundation
  • openxr
  • spatial-entities
  • 錨定廣告
  • geospatial-api

轉錄稿

簡介和 AR Foundation 總覽

  • 現在我們來做個小測驗

  • 大家好,歡迎各位的加入。

  • 本課程是「使用 Unity 在 Android XR 上建構空間內容」。

  • 我是 Jennifer Stanton。

  • 我在 Unity 擔任資深軟體工程師。

  • 我任職於 XR 功能團隊,主要負責 AR Foundation 和相關套件,將擴增實境和混合實境導入 Unity。

  • 我是 Google XR 遊戲引擎團隊的 Levana Chen。

  • 我們的團隊致力於提供外掛程式和套件,以強化 XR 功能,並與遊戲引擎完美整合。

  • 我們常聽開發人員說,要用同一個程式碼集支援多個 XR 平台,是一大難題。

  • AR Foundation 是 Unity 的高階延展實境 API 層。

  • 您可以指定多個 XR 平台,不必大幅重寫程式碼或管理多個程式碼集,而且只要使用一個指令碼 API,就能在任何平台上使用 AR 功能。

  • 這樣一來,您就能專注於打造應用程式、遊戲或體驗,而 Unity 會為您處理平台細節。

  • AR Foundation 實現這點的方法之一,就是透過 OpenXR 擴充功能。

  • 這是一組由 Khronos Group 定義的開放式業界標準 API。

  • OpenXR 也建立了空間實體。

  • 這項架構可讓數位內容呈現實體世界,並與之互動。

  • 空間實體提供統一模型,可將真實世界元素呈現為數位物件。

  • 很高興能與各位分享 AR Foundation 的最新資訊,協助大家開始打造 XR 體驗。

  • 接下來,我會介紹 AR Foundation 的功能,讓數位內容與現實世界互動。

  • 接著,我會說明專案設定,包括套件和其他依附元件,方便您在專案中實作。

  • 最後,我會介紹 Android XR 獨有的功能,為這場演講畫下句點。

AR Foundation 的主要功能

  • AR Foundation 以數位方式呈現真實世界的方法之一,就是平面。

  • 這些是實體環境中的平面,例如地板、牆壁、天花板,甚至是桌子和沙發。

  • 網格是代表實體世界幾何圖形的 3D 結構。

  • 我們還能透過光線投射與現實世界互動。

  • 這些點會決定光線與數位內容的交點。

  • 就像雷射筆一樣,你可以取得雷射筆照射位置的資訊。

  • 我們也提供錨點,可協助穩定及重新定位空間中的點。

  • 稍後會深入說明錨定標記。

  • 因此,這四項功能都屬於 AR Foundation 功能。

  • 還有一些功能未列在這張投影片中。

  • 平面和錨點會擴充 ARTrackable 類別,因此特別視為可追蹤項目,其管理工具會追蹤執行階段的更新。

  • 接著來看看平面。

  • 平面:我們有 ARPlane 元件,可即時辨識平面。

  • 這些平面具有語意標籤,可供查詢,因此您能知道平面是地板、桌子、牆壁等。

  • 平面也有動態邊界,因此當使用者在真實世界中移動,並能看到更多表面時,平面邊界會即時動態擴展、合併及調整。

  • 我們也可以將網格碰撞器附加到平面,處理物理效果。

  • 以下是平面偵測的範例。

  • 這是 AR Foundation 範例光線投射場景,在 Android XR 裝置上執行。

  • 可即時偵測動態 2D 多邊形。

  • 您可以按照專案設定指南,或使用我們的範例或範本,建構具備這項功能的應用程式。

  • 接下來是網格。

  • 網格是一項 AR 功能,可提供環境的 3D 網格。

  • 我們可以將網格碰撞器附加到這些生成的網格,讓數位內容與真實世界中的物體互動。

  • 網格可讓物件遮蔽效果更逼真。

  • 因此,虛擬物件可以顯示在實體物件後方。

  • 如果數位物件可以在空間中移動,您可以使用相同的幾何圖形,找出避開現實世界物件的路徑。

  • 接下來是光線投射。

  • 光線投射會將虛擬光線投射到 3D 場景中,尋找交點,您不必擔心複雜的數學問題。

  • 投射光線可能會與環境中的多個 3D 物件交會。

  • 您可以設定光線投射,只擊中特定類型的數位內容。

  • 舉例來說,您可以將其設為不與平面相交,只與網格相交。

設定套件和依附元件

  • 現在來看看相關套件。

  • 這些套件會相互通訊,共同打造完整的 XR 體驗。

  • 如前所述,AR Foundation 會處理平面和錨點等功能。

  • OpenXR 外掛程式會處理各種 XR 平台上的原生 API,因此如果裝置使用 OpenXR,建議您使用這個套件。

  • OpenXR 外掛程式可視需要與 OpenXR Spatial Entities 套件搭配使用,以便存取 Spatial Entities 擴充功能,詳情請見後文。

  • 如果您知道自己會專門指定 Android XR 裝置 (例如 XR 頭戴式裝置和有線 XR 眼鏡),則需要 Unity OpenXR Android XR 套件。

  • 由於上述所有作業都遵循外掛程式模式,因此對程式碼的影響應該微乎其微。

  • 部分功能僅適用於特定 AR Foundation 套件。

  • 舉例來說,從這張圖表可以看出,Unity OpenXR Spatial Entities 套件目前不支援網格功能。

  • XR 外掛程式管理功能可讓您管理專案中選取的 OpenXR 功能。

  • 因此,如果您使用 Unity 編輯器,請依序前往「Edit」和「專案設定」,然後在左側欄中找到「XR Plug-in Management」。

  • 您可以在這裡找到 OpenXR 功能和功能群組,並選取或取消選取要納入建構作業的功能和功能群組。

  • 舉例來說,這張螢幕截圖顯示 Android XR OpenXR 套件隨附的所有範例。

  • 如有需要,您也可以將這些實體與空間實體混合搭配。

  • 這是「Package Manager」視窗。

  • 我已醒目顯示一些您可能會在一般 XR 專案設定中看到的套件。

  • 如您所見,我們有 AR Foundation,這在前面已經提過。

  • 我們也提供 OpenXR 外掛程式。

  • 我們有 Android XR 套件。

  • 我也會介紹其他平台,因為您絕對可以建立能編譯至多個平台的專案。

  • 右下方還有其他套件。

  • Composition Layers (適用於 OpenXR 裝置)、Core Utils (適用於任何 XR 相關內容),以及 XR Hands 和 XR Interaction Toolkit (用於取得輸入內容,這點非常重要)。

  • 接下來是專案驗證。

  • 這也是 XR Plug-in Management 的一部分。

  • 專案驗證功能可偵測重複特徵,以及專案中其他需要修正才能執行的問題。

  • 不過,我在這張螢幕截圖中標出了重複的功能。

  • 基本上,這代表兩個相同的功能,例如錨點、平面和光線投射。

  • 使用者選擇從多個套件中加入這些功能的重複項目,系統只是提示使用者選取其中一個。

本機錨點和資料保留

  • 接下來,我們來談談本機錨點和持續性。

  • 錨點可確保數位內容在環境中保持在所需位置,不受 AR 工作階段追蹤狀態變更的影響。

  • AR 平台會隨著時間自動更新錨點的姿勢,確保錨點保持在一致的位置。如果您離開錨點後再返回,錨點是確保內容保持在原位的最佳方式。

  • 如果使用者關閉應用程式,稍後重新啟動,並在另一個工作階段返回應用程式,該怎麼辦?

  • 您可以使用永久錨點,在 AR 工作階段中儲存錨點,並在後續工作階段中載入。

  • 即使應用程式生命週期結束或裝置重新啟動,這些工作仍會繼續執行。

  • 下圖是 AR Foundation Samples 專案中的永久錨點範例。

  • 範例場景包含一個選單,其中列出該平台支援的所有功能,例如儲存及載入錨點。

  • 接下來,我們將介紹永久錨點的生命週期。

  • 首先,您會在本機建立錨點,因此尚未儲存到任何位置,也未儲存到永久儲存空間,只是本機錨點。

  • 然後將資料保存至儲存空間。

  • 接著,您可以重新啟動應用程式,然後從儲存空間載入。

  • 部分平台 (包括 Android XR) 也支援清除或取消永久保存錨點。

  • 空間實體供應器支援自動載入所有已儲存的錨點,因此您不必手動依 ID 要求每個錨點。

  • 在 XR 外掛程式管理設定中,你可以選取「Spatial Entities: Anchors」功能中的「Automatically Load Saved Anchors」選項。

  • 只要幾行程式碼就能儲存錨點。

  • 首先,我們在 Unity 編輯器中,將 AR 錨點管理工具元件新增至 XR Origin 遊戲物件,確保場景已準備就緒。

  • 管理工具會使用執行階段追蹤的錨點,持續更新場景。

  • 接下來,我們來看看程式碼。

  • 如要新增錨點,只要呼叫 TryAddAnchorAsync 並傳入要放置錨點的位置即可。

    async void CreateAnchorAsync(ARAnchorManager manager, Pose pose) {
        var result = await manager.TryAddAnchorAsync(pose);
        if (result.status.IsError()) {
            // handle error
            return;
        }
        var anchor = result.value;
    }
  • 接下來是移除錨定標記。

    void RemoveAnchor(ARAnchorManager manager, ARAnchor anchor) {
        var result = manager.TryRemoveAnchor(anchor.trackableId);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    }
    
    void DestroyAnchorComponent(ARAnchor anchor) {
        Destroy(anchor);
    }
  • 同樣地,這也只需要一行程式碼。

  • 呼叫 TryRemoveAnchor,並傳入錨點的可追蹤 ID。

  • 錨點建立時,系統已指派可追蹤的 ID。

  • 接著是 TrySaveAnchorAsync。

    async void SaveAnchorAsync(ARAnchorManager manager, ARAnchor anchor) {
        var result = await manager.TrySaveAnchorAsync(anchor);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    
        // Save this value, then return it as an input parameter
        SerializableGuid guid = result.value;
    }
  • 您需要傳入 AR 錨點做為輸入內容。

  • 這個函式會傳回 SerializableGuid,用於識別永久儲存空間中的錨點。

  • 之後載入錨點時,您可以使用 GUID。

  • 接著來看看載入錨定廣告。

    async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid) {
        var result = await manager.TryLoadAnchorAsync(guid);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    
        ARAnchor anchor = result.value;
    }
  • 這裡要呼叫的函式是 TryLoadAnchorAsync,並傳入先前序列化的 GUID。

  • 並傳回 AR 錨點做為結果。

  • 大概就是這些

  • 這些函式可讓您輕鬆管理本機和永久錨點。

  • 因此,在大多數 AR 平台上,錨點追蹤是一項相當耗費資源的工作。

  • 使用錨點時,請考量下列最佳做法:避免建立多個距離非常近的錨點、考量是否可以使用附近的錨點,而非建立另一個錨點、移除不再使用的錨點,最後,如果啟用其他 AR 功能來協助計算錨點所需的姿勢,請考量是否可以在成功建立錨點後停用該功能。

  • 接下來,我將交給 Levana。

Android XR SDK for Unity

  • 謝謝,Jennifer。

  • 接下來,我將說明 Google 對 Unity Android XR 發展的貢獻。

  • 因此,除了 Jennifer 介紹過的 Unity OpenXR 外掛程式,Google 也提供 Android XR SDK for Unity,具備 Android XR 平台提供的額外功能。

  • 這個 SDK 包含許多功能和套件,本次講座將著重介紹 Android XR Extension for Unity 套件。

  • 因此,與 OpenXR Android XR 和 OpenXR Spatial Entities 類似,Android XR Extension for Unity 套件實際上是 OpenXR 供應商。

  • 這個套件的架構如下,可以平行或獨立運作。

  • 因此,與所有其他 OpenXR 供應商一樣,Google 的套件直接以標準 OpenXR 介面為基礎建構。

  • 因此,我們在此列出多項功能,與其他所有供應商進行比較。

  • 因此,您可以根據這些資訊,決定何時及為何選擇其中一種。

  • 你可能會對這個表格中顯示的可追蹤和空間實體標記有疑問。

  • 附帶一提,如果先前已在 Android XR 上建構應用程式和體驗,我們已透過可追蹤的擴充功能提供這項功能,且僅適用於 Android XR。

  • 之後,正如 Jennifer 所說,我們以 OpenXR 開放群組的形式運作,然後在右側發布 Spatial Entities Extension,並提供一致的跨平台體驗。

  • 目前,我們正在淘汰可追蹤擴充功能,改用空間實體架構。

  • 後者不僅效能更佳,還支援跨平台。

使用空間實體建構

  • 接著,我們來進一步瞭解空間實體。

  • 除了 Jennifer 提到的平面、光線投射和錨點,空間實體供應商也支援這項功能,例如標記,可偵測、解碼標準 2D 條碼,並追蹤其在 3D 空間中的位置,以及辨識已知 2D 圖形並在 3D 空間中追蹤的圖片。

  • 接著是定界框,用於識別實體物件 (例如鍵盤、筆電或滑鼠),並追蹤 3D 姿勢中的定界框。

  • 在下一個範例中,我們會設定 Google 擴充功能套件提供的定界框功能。

  • 所有 OpenXR 功能的檢視畫面都大同小異。

  • 因此,您只需要先切換至 Android XR 平台,然後啟用 OpenXR 供應商,並選取要建構的 OpenXR 功能 (例如這裡顯示的空間實體定界框功能),然後修正專案驗證問題。

  • 這有助於找出特定功能所需的所有依附元件,然後解決多個功能造成相同子系統衝突的問題。

  • 有時候,您只需要選擇一項功能,例如錨點、平面或定界框。

  • 最後,您可以建構範例場景或測試場景。

  • 如要在執行階段存取可追蹤物件,只要新增 AR 定界框管理工具元件即可,這個元件與 AR 錨點管理工具類似,然後使用 AR Foundation 介面存取。

  • 或者,您也可以匯入擴充套件提供的空間實體範例,做為範本開始使用。

  • 因此,在 Android XR 上使用 AR Foundation 功能時,主要差異如下。

  • 為了瞭解使用者周遭環境,應用程式通常需要存取環境相關的私密資料。

  • 為保護使用者,Android XR 會限制系統,也就是權限背後的資料存取權。

  • 您可能已經熟悉這類權限,例如一般的 Android 權限呼叫,像是相機存取權或位置存取權等。

  • 最佳做法是在啟動時停用管理員元件,可以透過編輯器 (如右側所示) 或指令碼 (例如 Awake 事件) 進行。

    void Awake() {
        planeManager = GetComponent<ARPlaneManager>();
        planeManager.enabled = false;
        boundingBoxManager = GetComponent<ARBoundingBoxManager>();
        boundingBoxManager.enabled = false;
        permissionUtil = GetComponent<AndroidXRPermissionUtil>();
    }
    
    void OnEnable() {
        StartCoroutine(WaitForPermissions());
    }
  • 授予所有必要權限後,您就可以透過指令碼啟用管理工具,就像這裡的 WaitForPermissions 輔助程式一樣。

    IEnumerator WaitForPermissions() {
        planeManager.enabled = false;
        boundingBoxManager.enabled = false;
    
        yield return new WaitUntil(() =>
            permissionUtil.AllPermissionGranted());
    
        // Enable after granted permissions and
        // restart subsystems.
        planeManager.enabled = true;
        boundingBoxManager.enabled = true;
    
        // Save permission status.
        permissionGranted = true;
    }
  • 這樣一來,就能避免因權限不足而導致子系統初始化失敗。

  • 這是一項特殊功能。

  • Jennifer 介紹了如何使用 AR Foundation 建立錨點,而為了擴充 Android XR 的錨點功能,我們的套件也提供一項新功能,稱為可共用錨點。

  • 這項功能會複製 AR Foundation 錨點功能目前支援的功能,並提供一些額外功能,例如地理空間錨點。

  • 如你所想,錨點是連接虛擬和實體世界的橋梁。

  • Jennifer 已經介紹過如何建立、儲存及載入錨點,但這些都是在使用者 Living Room 等本機空間中進行。

  • 但其他國家/地區呢?

  • 如何與彼此分享?

Geospatial API

  • 這時 Geospatial API 就能派上用場。

  • 您可以根據實際位置 (例如喜愛的書店或大型公園中的聚會地點) 建立地理空間錨點。

  • 因此,Geospatial API 可透過 VPS (也稱為虛擬定位系統,類似 GPS) 提供準確的地理位置追蹤器,但會使用戶外街景服務資料提供更準確的追蹤結果。

  • 如果您曾使用過 ARCore 的地理空間功能,可能已經熟悉這個檢視畫面。

  • 因此,如要在 Android XR 上存取 Geospatial API,必須先連線至 Google Cloud 平台託管的後端。

  • 由於地理空間後端實際上是由 Google Cloud 上的 ARCore 服務提供,因此您可以按照完全相同的步驟,設定 Google Cloud 雲端專案的 ARCore API 存取權。

  • 完成上述步驟後,就能使用 Android XR Google Cloud 功能,這項功能是由我們的擴充功能套件提供。

  • 啟用 Geospatial API 並從 Google Cloud 專案取得 API 金鑰後,您就可以啟用這項功能,並透過 OpenXR 設定選單設定驗證,如這裡所示。或者,您也可以透過名為 TrySetAuthenticationAsync 的指令碼 API 手動設定。

    async Task TrySetAuthentication() {
        googleCloudFeature =
            OpenXRSettings.Instance
                .GetFeature<XRGoogleCloudFeature>();
        var result = await googleCloudFeature
            .TrySetAuthenticationAsync(
                strategy, credential);
        if (result.IsSuccess()) {
            // Ready to use for geospatial tracker.
        } else if (result.IsUnsupported())  {
            // Google Cloud feature is not supported
        } else if (result.IsKeylessError()) {
            // Confirm if keyless has been set up properly.
        } else {
            // Handling other common OpenXR errors.
        }
    }
  • 請注意,Android XR 平台目前僅支援 API 金鑰驗證,但日後會支援更多憑證。

  • 因此,驗證完成後,您可以使用地理空間管理工具存取地理空間功能。

  • 強烈建議您在整合前,先查看 Google 地圖的 VPS 涵蓋範圍。

  • 因此,使用這項功能時,您也必須具備網路存取權。

  • XR Geospatial Manager 的運作方式與 AR Foundation 提供的 AR Session 元件類似。

  • 因此,它具有靜態狀態屬性,可指出系統支援和地理空間追蹤器的追蹤狀態。

  • 您可以透過回呼註冊狀態變更事件,如下所示。

    void OnGeospatialStateChanged(XRGeospatialStateChangedEventArgs eventArgs) {
        bool isInitFailed = eventArgs.CurrState == XRGeospatialState.InitializationFailed;
        if (isInitFailed && XRGeospatialManager.InitializationResult.IsCloudAuthError()) {
            // Handle cloud authentication issues.
        } else if (isInitFailed) {
            // Check XRGeospatialManager.InitializationResult for other common OpenXR errors.
        } else {
            // Iterate runtime state change, and handle accordingly.
        }
    }
    
    private void OnEnable() {
        XRGeospatialManager.OnStateChanged += OnGeospatialStateChanged;
    }
  • 地理空間狀態變更為執行中後,您就能使用這個管理工具執行個體,執行下列所有功能。

  • 首先,如要使用指定的 GPS 位置檢查 VPS 可用性,請使用 Unity 的位置 API 取得 GPS 值,然後檢查 VPS 可用性。

    async Task CheckVPSAvailability(CancellationToken token) {
        var location = Input.location.lastData;
        try {
            var result = await geospatialManager.TryCheckVPSAvailabilityAsync(
                location.latitude, location.longitude);
            token.ThrowIfCancellationRequested();
            if (result.status.nativeStatusCode == (int)XrResultAndroid.GeospatialCoordinateInvalid) {
                // Invalid coordinates, try with another location.
            } else if (result.status.IsError()) {
                // Handle common OpenXR error: result.status.ToOpenXRStatus()
            }  else if (result.value == XRVPSAvailability.Available)  {
                // You may use this pose to attach geospatial anchors.
                var currentGeospatialPose = new XRGeospatialPose(location);
            }
        }
    }
  • 因此,如果不想透過地圖查看 VPS 支援,基本上可以改用這個方法,但必須到戶外查看實際的 GPS 狀態。

  • 接著,您也可以透過 TryLocate 在 Unity 姿勢和地理空間姿勢之間轉換姿勢。

    // Geospatial Pose -> Unity Pose
    var geospatialPose =
        new XRGeospatialPose(Input.location.lastData);
    if (geospatialManager.TryLocate(
            geospatialPose, out XRPoseResult result)) {
        if (result.IsPositionValid ||
            result.IsPositionTracked) {
            transform.position = result.Pose.position;
        }
        if (result.IsRotationValid ||
            result.IsRotationTracked) {
            transform.rotation = result.Pose.rotation;
        }
    }
    // Unity Pose -> Geospatial Pose
    Pose pose = new Pose(
        transform.position, transform.rotation);
    if (geospatialManager.TryLocate(
          pose, out XRGeospatialPoseResult result)) {
        if (result.IsPositionValid ||
            result.IsRotationValid) {
            // You may use for anchor attachment.
        }
    }
  • 接著,您可以根據 VPS 檢查或姿勢轉換的結果,將空間錨點附加至特定地理空間姿勢,或附加至已知表面類型,例如地形或屋頂。

    async void PlaceAnchor(XRGeospatialPose pose) {
        try {
            Result<ARShareableAnchor> anchorResult;
            XRGeospatialSurfaceType typeToPlace = /* Choose your target type */;
            if (typeToPlace == XRGeospatialSurfaceType.Default) {
                anchorResult = geospatialManager.TryAttachGeospatialAnchor(pose);
            } else {
                pose.Altitude = 0;
                anchorResult = await geospatialManager.TryAttachSurfaceAnchorAsync(typeToPlace, pose);
            }
            if (anchorResult.status.IsSuccess()) {
                // ARShareableAnchorManager will track the anchor and update it.
            } else {
                // Handle OpenXR failures.
            }
        }
    }
  • 將這些功能結合後,就能打造出真正神奇的空間體驗,讓現實世界成為焦點。

  • 因此,我們在這裡放置箭頭,引導觀眾前往現實世界中的目標地點。

  • 好的。

  • 因此,Android XR SDK 並非我們提供給 Unity 開發人員的唯一套件。

  • Google 的 Android XR SDK for Unity 現在也包含 Android XR Interaction Framework 和 Android XR Audio 套件。

  • 我們很高興能擴充 Android XR SDK,支援更多功能和空間實體。

總結與相關資源

  • 好的,課程即將結束。

  • 您可以查看 Unity 的線上藍圖,瞭解後續發展。

  • 別忘了,新一代 Unity 7 將於明年初推出。

  • 在此同時,您可以試用這些工具,並使用混合實境範本建立專案,或查看 GitHub 上的 AR Foundation 範例專案。

  • 如要進一步瞭解 Spatial Entity 和 Unity 套件支援,請造訪 Android 開發人員網站。

  • 最後,謝謝大家。

  • 我們非常期待看到您使用 AR Foundation 和 Android XR 上的 Spatial Entity 打造精彩內容。

程式碼

程式碼片段

「本機錨點和持續性」影片 10:34 處的 C# 程式碼片段
async void CreateAnchorAsync(ARAnchorManager manager, Pose pose) {
    var result = await manager.TryAddAnchorAsync(pose);
    if (result.status.IsError()) {
        // handle error
        return;
    }
    var anchor = result.value;
}
「本機錨點和持續性」中 10:43 的 C# 程式碼片段
void RemoveAnchor(ARAnchorManager manager, ARAnchor anchor) {
    var result = manager.TryRemoveAnchor(anchor.trackableId);
    if (result.status.IsError()) {
        // handle error
        return;
    }
}

void DestroyAnchorComponent(ARAnchor anchor) {
    Destroy(anchor);
}
「本機錨點和持續性」中 11:00 的 C# 程式碼片段
async void SaveAnchorAsync(ARAnchorManager manager, ARAnchor anchor) {
    var result = await manager.TrySaveAnchorAsync(anchor);
    if (result.status.IsError()) {
        // handle error
        return;
    }

    // Save this value, then return it as an input parameter
    SerializableGuid guid = result.value;
}
「本機錨點和持續性」中的 C# 程式碼片段 (11:17)
async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid) {
    var result = await manager.TryLoadAnchorAsync(guid);
    if (result.status.IsError()) {
        // handle error
        return;
    }

    ARAnchor anchor = result.value;
}
「Building with spatial entities」影片 16:45 處的 C# 程式碼片段
void Awake() {
    planeManager = GetComponent<ARPlaneManager>();
    planeManager.enabled = false;
    boundingBoxManager = GetComponent<ARBoundingBoxManager>();
    boundingBoxManager.enabled = false;
    permissionUtil = GetComponent<AndroidXRPermissionUtil>();
}

void OnEnable() {
    StartCoroutine(WaitForPermissions());
}
「Building with spatial entities」影片 16:58 處的 C# 程式碼片段
IEnumerator WaitForPermissions() {
    planeManager.enabled = false;
    boundingBoxManager.enabled = false;

    yield return new WaitUntil(() =>
        permissionUtil.AllPermissionGranted());

    // Enable after granted permissions and
    // restart subsystems.
    planeManager.enabled = true;
    boundingBoxManager.enabled = true;

    // Save permission status.
    permissionGranted = true;
}
「Geospatial API」影片 19:12 處的 C# 程式碼片段
async Task TrySetAuthentication() {
    googleCloudFeature =
        OpenXRSettings.Instance
            .GetFeature<XRGoogleCloudFeature>();
    var result = await googleCloudFeature
        .TrySetAuthenticationAsync(
            strategy, credential);
    if (result.IsSuccess()) {
        // Ready to use for geospatial tracker.
    } else if (result.IsUnsupported())  {
        // Google Cloud feature is not supported
    } else if (result.IsKeylessError()) {
        // Confirm if keyless has been set up properly.
    } else {
        // Handling other common OpenXR errors.
    }
}
「Geospatial API」影片 20:24 處的 C# 程式碼片段
void OnGeospatialStateChanged(XRGeospatialStateChangedEventArgs eventArgs) {
    bool isInitFailed = eventArgs.CurrState == XRGeospatialState.InitializationFailed;
    if (isInitFailed && XRGeospatialManager.InitializationResult.IsCloudAuthError()) {
        // Handle cloud authentication issues.
    } else if (isInitFailed) {
        // Check XRGeospatialManager.InitializationResult for other common OpenXR errors.
    } else {
        // Iterate runtime state change, and handle accordingly.
    }
}

private void OnEnable() {
    XRGeospatialManager.OnStateChanged += OnGeospatialStateChanged;
}
「Geospatial API」影片 20:38 處的 C# 程式碼片段
async Task CheckVPSAvailability(CancellationToken token) {
    var location = Input.location.lastData;
    try {
        var result = await geospatialManager.TryCheckVPSAvailabilityAsync(
            location.latitude, location.longitude);
        token.ThrowIfCancellationRequested();
        if (result.status.nativeStatusCode == (int)XrResultAndroid.GeospatialCoordinateInvalid) {
            // Invalid coordinates, try with another location.
        } else if (result.status.IsError()) {
            // Handle common OpenXR error: result.status.ToOpenXRStatus()
        }  else if (result.value == XRVPSAvailability.Available)  {
            // You may use this pose to attach geospatial anchors.
            var currentGeospatialPose = new XRGeospatialPose(location);
        }
    }
}
「Geospatial API」影片 21:03 處的 C# 程式碼片段
// Geospatial Pose -> Unity Pose
var geospatialPose =
    new XRGeospatialPose(Input.location.lastData);
if (geospatialManager.TryLocate(
        geospatialPose, out XRPoseResult result)) {
    if (result.IsPositionValid ||
        result.IsPositionTracked) {
        transform.position = result.Pose.position;
    }
    if (result.IsRotationValid ||
        result.IsRotationTracked) {
        transform.rotation = result.Pose.rotation;
    }
}
「Geospatial API」影片 21:03 處的 C# 程式碼片段
// Unity Pose -> Geospatial Pose
Pose pose = new Pose(
    transform.position, transform.rotation);
if (geospatialManager.TryLocate(
      pose, out XRGeospatialPoseResult result)) {
    if (result.IsPositionValid ||
        result.IsRotationValid) {
        // You may use for anchor attachment.
    }
}
「Geospatial API」影片 21:11 處的 C# 程式碼片段
async void PlaceAnchor(XRGeospatialPose pose) {
    try {
        Result<ARShareableAnchor> anchorResult;
        XRGeospatialSurfaceType typeToPlace = /* Choose your target type */;
        if (typeToPlace == XRGeospatialSurfaceType.Default) {
            anchorResult = geospatialManager.TryAttachGeospatialAnchor(pose);
        } else {
            pose.Altitude = 0;
            anchorResult = await geospatialManager.TryAttachSurfaceAnchorAsync(typeToPlace, pose);
        }
        if (anchorResult.status.IsSuccess()) {
            // ARShareableAnchorManager will track the anchor and update it.
        } else {
            // Handle OpenXR failures.
        }
    }
}