資深軟體工程師
瞭解如何使用 Unity 和 Android XR,在真實世界中錨定及保留 3D 體驗。本課程將介紹 Spatial Entities 架構、Cloud Anchors 和 Geospatial API,示範如何建構可記住實體位置,並在不同工作階段同步多使用者空間資料的情境感知應用程式。
我們將探討空間實體的架構,瞭解如何將數位資產錨定至實體物件、表面和地理位置。您將學會實用的 Unity 工作流程,包括對應空間、在不同工作階段中保留內容,以及建立共用、可辨識位置的 AR/XR 體驗。
TrySaveAnchorAsync 保留這些資料,並使用序列化 GUID 重新載入。轉錄稿
現在我們來做個小測驗
大家好,歡迎各位的加入。
本課程是「使用 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 以數位方式呈現真實世界的方法之一,就是平面。
這些是實體環境中的平面,例如地板、牆壁、天花板,甚至是桌子和沙發。
網格是代表實體世界幾何圖形的 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。
謝謝,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 可透過 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,支援更多功能和空間實體。
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);
}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;
}async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid) {
var result = await manager.TryLoadAnchorAsync(guid);
if (result.status.IsError()) {
// handle error
return;
}
ARAnchor anchor = result.value;
}void Awake() {
planeManager = GetComponent<ARPlaneManager>();
planeManager.enabled = false;
boundingBoxManager = GetComponent<ARBoundingBoxManager>();
boundingBoxManager.enabled = false;
permissionUtil = GetComponent<AndroidXRPermissionUtil>();
}
void OnEnable() {
StartCoroutine(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;
}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.
}
}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;
}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 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.
}
}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.
}
}
}