Unity 中的世界鎖定和空間錨點
本文內容
讓全像投影保持原位、與您一起移動,或在某些情況下,相對於其他全像投影本身的位置,是建立混合實境應用程式的一大部分。 本文將帶您完成使用世界鎖定工具的建議解決方案,但我們也會討論在 Unity 專案中手動設定空間錨點。 在我們跳入任何程序代碼之前,請務必瞭解 Unity 如何處理自己的引擎中的座標空間和錨點。
世界縮放座標系統
今天,在撰寫遊戲、數據視覺效果應用程式或虛擬實境應用程式時,典型的方法是建立一個絕對 世界座標系統 ,讓所有其他座標都能可靠地對應回去。 在該環境中,您隨時都能找到一個穩定轉換,以定義該世界中任何兩個對象之間的關聯性。 如果您未移動這些物件,其相對轉換一律會維持不變。 在轉譯純虛擬世界時,這種全域座標系統很容易正確,您事先知道所有幾何。 室內規模的 VR 應用程式今天通常會建立這種絕對房間規模的座標系統,其起源在地板上。
相反地,HoloLens 等未系結的混合實境裝置對世界有動態感測器驅動的瞭解,在使用者周圍的環境經過一段時間后持續調整其知識,因為他們在建築物的整個樓層走多米。 在世界規模的體驗中,如果您將所有全像投影放在天真的固定座標系統中,這些全像投影最終會隨著時間而漂移,無論是根據世界還是彼此相對。
例如,頭戴式裝置目前可能相信世界上的兩個位置相距4米,然後改進該理解,了解這些位置實際上相距3.9米。 如果這些全像投影最初被放置在單一剛性座標系統中,其中一個則總是與真實世界相距0.1公尺。
您可以在 Unity 中手動放置 空間錨點 ,以在用戶移動時維護全像投影在實體世界中的位置。 不過,這會犧牲虛擬世界中的自我一致性。 不同的錨點會不斷彼此移動,而且也會通過全域座標空間移動。 在此案例中,版面配置等簡單工作變得困難。 物理模擬也可能有問題。
世界鎖定工具 (WLT)可讓您充分利用這兩個世界,使用空間錨點的內部供應在使用者四處移動時,穩定單一剛性座標系統。 WLT 會分析相機的座標,以及每個畫面的空間錨點。 WLT 不會變更世界上所有專案的座標,以補償用戶頭部座標中的修正,而是改為修正頭部的座標。
選擇您的世界鎖定方法
可能的話,請使用 世界鎖定工具 進行全像投影定位。
世界鎖定工具 提供穩定的座標系統,可將虛擬和真實世界標記之間的可見不一致降到最低。 世界鎖定工具會使用共用錨點集區鎖定整個場景,而不是使用群組自己的個別錨點鎖定每個物件群組。
世界鎖定工具會自動處理空間錨點的內部建立和管理。 您不需要與 ARAnchorManager 或 WorldAnchor 互動,即可將您的全像投影世界鎖定。
針對使用 OpenXR 或 Windows XR 外掛程式的 Unity 2019/2020,請使用 ARAnchorManager 。
針對舊版 Unity 或 WSA 專案,請使用 WorldAnchor 。
設定世界鎖定
若要開始使用世界鎖定工具, 請下載混合實境功能工具 。 若要深入瞭解基本概念,請參閱主要世界鎖定工具文件頁面,以取得概觀、快速入門和其他實用主題的連結。
自動化設定
當您的專案準備好進行時,請從 混合實境 > 世界鎖定工具 執行設定場景公用程式:
重要
設定場景公用程式可以隨時重新執行。 例如,如果 AR 目標已從舊版變更為 XR SDK,則應該重新執行。 如果場景已正確設定,則執行公用程序沒有任何作用。
視覺化工具
在早期開發期間,新增可視化檢視有助於確保WLT已設定並正常運作。 您可以使用移除可視化檢視公用程式,移除生產效能,或基於任何原因不再需要移除它們。 如需可視化檢視的詳細資訊,請參閱 工具檔 。
混合實境 OpenXR 外掛程式透過實作 Unity 的 ARFoundation ARAnchorManager 來提供基本錨點功能。 若要瞭解 ARFoundation 中 ARAnchors 的基本概念,請流覽 AR Anchor Manager 的 ARFoundation 手冊。
命名空間: UnityEngine.XR.WSA
類型: WorldAnchor
關鍵技術是建立 空間錨點 ,以在實體世界中精確鎖定全像投影叢集,無論使用者漫遊了多遠,然後在 稍後的會話 中再次找到這些全像投影。
在舊版 Unity 中,您會將 WorldAnchor Unity 元件新增 至 GameObject 來建立空間錨點。
新增世界錨點
若要新增世界錨點,請使用您想要在真實世界中錨定之轉換的遊戲物件上呼叫 AddComponent<WorldAnchor>()
。
WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
此遊戲物件現在錨定在實體世界中的目前位置。 您可能會看到其 Unity 世界座標會隨著時間稍微調整,以確保實體對齊。 請參閱 載入世界錨點 ,以在未來的應用程式會話中再次尋找此錨點位置。
拿掉世界錨點
如果您不再想要 GameObject
鎖定到實體世界位置,而且不想移動此框架,請在 World Anchor 元件上呼叫 Destroy
。
Destroy(gameObject.GetComponent<WorldAnchor>());
如果您要移動 GameObject
此框架,請改為呼叫 DestroyImmediate
。
DestroyImmediate(gameObject.GetComponent<WorldAnchor>());
移動世界錨定 GameObject
當世界錨點在上面時,你就無法移動 GameObject
。 如果您需要移動 GameObject
此框架,您需要:
DestroyImmediate
World Anchor 元件。
GameObject
移動 。
將新的 World Anchor 元件新增至 GameObject
。
DestroyImmediate(gameObject.GetComponent<WorldAnchor>());
gameObject.transform.position = new Vector3(0, 0, 2);
WorldAnchor anchor = gameObject.AddComponent<WorldAnchor>();
處理可擷取性變更
在某個時間點,世界錨點可能無法在實體世界中。 Unity 接著不會更新錨定對象的轉換。 當應用程式正在執行時,也可能會發生這種情況。 無法處理locatability中的變更,會導致物件不會出現在世界上正確的實體位置。
若要收到關於可擷取性變更的通知:
OnTrackingChanged
訂閱事件。 OnTrackingChanged
每當基礎空間錨點在可擷取或無法擷取的狀態之間變更時,就會呼叫 事件。
anchor.OnTrackingChanged += Anchor_OnTrackingChanged;
處理 事件。
private void Anchor_OnTrackingChanged(WorldAnchor self, bool located)
{
// This simply activates/deactivates this object and all children when tracking changes
self.gameObject.SetActiveRecursively(located);
}
如果錨點會立即找到,isLocated
當傳回時AddComponent<WorldAnchor>()
,錨點的 屬性會設定為 true
。 因此, OnTrackingChanged
不會觸發事件。 更簡潔的模式是在附加錨點之後,呼叫 OnTrackingChanged
具有初始 IsLocated
狀態的處理程式。
Anchor_OnTrackingChanged(anchor, anchor.isLocated);
持續性世界鎖定
空間錨點會在應用程式會話之間的真實世界空間中儲存全像投影。 儲存在 HoloLens 錨點存放區之後,就可以在不同的會話中找到空間錨點並載入,而且在沒有因特網連線時是理想的後援。
根據預設,世界鎖定工具會在支援本機空間錨點持續性的裝置上,還原 Unity 的座標系統相對於實體世界。 若要在結束並重新執行應用程式之後,將全像投影出現在實體世界中的相同位置,應用程式只需要將相同的姿勢還原至全像投影。
如果應用程式需要更精細的控制,您可以在偵測器中停用 自動儲存 和 自動載入 ,以及管理腳本中的持續性。 如需詳細資訊,請參閱 保存空間座標系統 。
世界鎖定工具僅支援 HoloLens 裝置上的本機錨點持續性。
稱為的 XRAnchorStore
API 可讓錨點在會話之間保存。 XRAnchorStore
是裝置上已儲存錨點的表示法。 您可以從 Unity 場景中保存錨點、將錨點 ARAnchors
從記憶體載入新的 ARAnchors
,或刪除記憶體中的錨點。
命名空間
針對 Unity 2020 和 OpenXR :
using Microsoft.MixedReality.ARSubsystems.XRAnchorStore
或 Unity 2019/2020 + Windows XR 外掛程式 :
using UnityEngine.XR.WindowsMR.XRAnchorStore
公用方法
{
// A list of all persisted anchors, which can be loaded.
public IReadOnlyList<string> PersistedAnchorNames { get; }
// Clear all persisted anchors
public void Clear();
// Load a single persisted anchor by name. The ARAnchorManager will create this new anchor and report it in
// the ARAnchorManager.anchorsChanged event. The TrackableId returned here is the same TrackableId the
// ARAnchor will have when it is instantiated.
public TrackableId LoadAnchor(string name);
// Attempts to persist an existing ARAnchor with the given TrackableId to the local store. Returns true if
// the storage is successful, false otherwise.
public bool TryPersistAnchor(TrackableId id, string name);
// Removes a single persisted anchor from the anchor store. This will not affect any ARAnchors in the Unity
// scene, only the anchors in storage.
public void UnpersistAnchor(string name);
}
取得錨點存放區參考
若要使用 Unity 2020 和 OpenXR 載入 XRAnchorStore,請在 ARAnchorManager 的子系統 XRAnchorSubsystem 上使用擴充方法:
public static Task<XRAnchorStore> LoadAnchorStoreAsync(this XRAnchorSubsystem anchorSubsystem)
若要使用 Unity 2019/2020 和 Windows XR 外掛程式 載入 XRAnchorStore,請在 XRReferencePointSubsystem (Unity 2019) 或 XRAnchorSubsystem (Unity 2020) 上使用擴充方法,這是 ARReferencePointManager/ARAnchorManager 的子系統:
// Unity 2019 + Windows XR Plugin
public static Task<XRAnchorStore> TryGetAnchorStoreAsync(this XRReferencePointSubsystem anchorSubsystem);
// Unity 2020 + Windows XR Plugin
public static Task<XRAnchorStore> TryGetAnchorStoreAsync(this XRAnchorSubsystem anchorSubsystem);
載入錨點存放區
若要在 Unity 2020 和 OpenXR 中 載入錨點存放區,請從 ARAnchorManager 的子系統存取它,如下所示:
ARAnchorManager arAnchorManager = GetComponent<ARAnchorManager>();
XRAnchorStore anchorStore = await arAnchorManager.subsystem.LoadAnchorStoreAsync();
或搭配 Unity 2019/2020 和 Windows XR 外掛程式 :
// Unity 2019
ARReferencePointManager arReferencePointManager = GetComponent<ARReferencePointManager>();
XRAnchorStore anchorStore = await arReferencePointManager.subsystem.TryGetAnchorStoreAsync();
// Unity 2020
ARAnchorManager arAnchorManager = GetComponent<ARAnchorManager>();
XRAnchorStore anchorStore = await arAnchorManager.subsystem.TryGetAnchorStoreAsync();
若要查看保存/取消停駐錨點的完整範例,請參閱 [混合實境 OpenXR 外掛程式範例場景]() 中的 Anchors -> Anchors 範例 GameObject 和AnchorsSample.cs腳本():https://github.com/microsoft/OpenXR-Unity-MixedReality-Samples
針對舊版 Unity 或 WSA 專案中的全像投影持續性,請使用 WorldAnchor 。
命名空間: UnityEngine.XR.WSA.Persistence
類別: WorldAnchorStore
WorldAnchorStore 會建立全像攝影體驗,其中全像投影會保留在應用程式實例的特定真實世界位置。 用戶可以在想要的地方釘選個別全像投影,並在稍後在應用程式會話的相同位置找到它們。
WorldAnchorStore
可讓您跨會話保存世界錨點的位置。 若要跨會話保存全像投影,請分別追蹤 GameObjects
使用特定世界錨點的投影。 您可以使用世界錨點建立 GameObject
根,並使用本機位置位移來錨定子全像投影。
若要從先前的會話載入全像投影:
WorldAnchorStore
取得 。
載入世界錨點應用程式數據,這會提供您世界錨點的標識碼。
依標識碼載入世界錨點。
若要儲存未來會話的全像投影:
WorldAnchorStore
取得 。
儲存世界錨點,並指定標識符。
儲存與世界錨點相關的應用程式數據以及標識碼。
取得 WorldAnchorStore
保留 的 WorldAnchorStore
參考,讓您知道何時準備好執行作業。 由於此呼叫是異步的,因此只要應用程式啟動時,您就可以呼叫:
WorldAnchorStore.GetAsync(StoreLoaded);
StoreLoaded
是完成載入時的 WorldAnchorStore
處理程式:
private void StoreLoaded(WorldAnchorStore store)
{
this.store = store;
}
您現在有 的 WorldAnchorStore
參考,可用來儲存和載入特定的世界錨點。
儲存世界錨點
若要儲存世界錨點,請將世界錨點命名為 ,並在您之前取得的中傳遞它 WorldAnchorStore
。 如果您嘗試將兩個錨點儲存至相同的字串, store.Save
則傳回 false。 儲存新的儲存之前,請先刪除先前的儲存。
private void SaveGame()
{
// Save data about holograms that this world anchor positions
if (!this.savedRoot) // Only save the root once
{
this.savedRoot = this.store.Save("rootGameObject", anchor);
Assert(this.savedRoot);
}
}
載入世界錨點
若要載入世界錨點:
private void LoadGame()
{
// Saved data about holograms that this world anchor positions:
this.savedRoot = this.store.Load("rootGameObject", rootGameObject);
if (!this.savedRoot)
{
// Game root not saved. Re-place objects or start over.
}
}
您也可以使用 store.Delete()
來移除您先前儲存的錨點,以及 store.Clear()
移除所有先前儲存的數據。
列舉現有的錨點
若要列出儲存的錨點,請呼叫 GetAllIds
。
string[] ids = this.store.GetAllIds();
for (int index = 0; index < ids.Length; index++)
{
Debug.Log(ids[index]);
}
下一步
共用世界鎖定的座標空間:
瞭解空間對應:
傳回 Unity 開發檢查點:
另請參閱