簡化多用戶設置
(中國AI網 2025年05月16日)Mixed Reality Motif是一個幫助你在混合現實體驗中快速執行常見機制的藍圖。為了幫助開發者克服在混合現實中構建高質量、同步的多人游戲體驗所帶來的挑戰,Meta日前分享了Mixed Reality Motif的新系列:簡化多用戶設置。

多用戶社交體驗正在興起。對于Meta Horizon Store而言,一項重要的功能是Colocation,它指的是當用戶距離非常接近(通常在10米以內)時,自動檢測并接入Meta Quest設備的游戲和應用。
通過使用諸如基于藍牙的Colocation Discovery和Shared Spatial Anchor等技術,開發者可以將虛擬內容與物理世界同步,并確保用戶能夠與共享物理空間中的相同數字對象進行交互。
從Shared Spatial Anchors的基本知識和通過網絡共享它們到使用Colocation Discovery立即設置托管體驗,團隊將在這個Motif中引導你完成支持Colocation的關鍵步驟。
為了擴展你的Colocation 用例,你可以訪問GitHub的Motif項目,查找有關如何設置Space Sharing AP的步驟指南。你同時可以查看這個YouTube視頻教程,或參閱下方的文字介紹。

1. Spatial Anchor基礎
Spatial Anchor是一個世界鎖定的參考框架,它為物理世界中的虛擬對象提供位置和方向。應用程序可以為每個虛擬對象使用一個Spatial Anchor,或者選擇多個虛擬對象使用同一個Spatial Anchor,只要對象在其三米的覆蓋范圍內即可。
Anchor API提供了數個關鍵特性:
跨會話的持久性:錨的姿態可以持久化。
跨會話發現:可以發現和重用錨。
與其他用戶共享:錨可以同步或異步共享。
GitHub項目提供了SpatialAnchorManager, SpatialAnchorStorage和SpatialAnchorLoader類來幫助管理錨,介紹了如何使用Anchor API來實現創建、保存、加載和刪除錨等基本操作。
1.1 創建錨
你可以通過實例化一個包含OVRSpatialAnchor組件的prefab來創建一個錨,或者在場景中使用一個現有的對象并添加OVRSpatialAnchor組件。這將分配一個UUID并異步創建錨。
// C/C++var anchor = gameObject.AddComponent();while (!anchor.Created){await Task.Yield();}
錨包含一個名為“Created”的屬性,用于檢查創建是否已經成功完成。作為最佳實踐參考,建議等待創建錨完成。
1.2 保存錨
可以通過調用OVRSpatialAnchor.SaveAnchorAsync來保存錨。
// C/C++await anchor.SaveAnchorAsync();SpatialAnchorStorage.SaveUuidToPlayerPrefs(anchor.Uuid);
ColocatedExperiences項目包含一個靜態的SpatialAnchorStorage類,它允許你將錨保存到Unity PlayerPrefs中。你可以調用示例的SaveUuidToPlayerPrefs函數來在設備存儲空間錨。
這個函數用一個計數將新的UUID添加到PlayerPrefs中。這樣,你就可以輕松地跟蹤多個UUID并在以后訪問它們。
// C/C++public static void SaveUuidToPlayerPrefs(Guid uuid){var count = PlayerPrefs.GetInt(NumUuidsKey, 0);PlayerPrefs.SetString($"{UuidKeyPrefix}{count}", uuid.ToString());PlayerPrefs.SetInt(NumUuidsKey, count + 1);PlayerPrefs.Save();}1.3 加載錨
加載錨可以通過SpatialAnchorLoader類實現,它包括三個獨立的步驟:加載、定位和綁定。
當加載時,錨最初未綁定,這意味著錨尚未接到預期的GameObject的OVRSpatialAnchor組件。錨必須綁定以管理其生命周期,并提供對其他功能(如保存和擦除)的訪問。
要加載錨,你需要它的UUID。由于UUID存儲在PlayerPrefs中,你可以從靜態SpatialAnchorStorage類查詢UUID列表。
// C/C++var uuids = SpatialAnchorStorage.LoadAllUuidsFromPlayerPrefs();
接下來,你可以調用LoadUnboundAnchorsAsync函數。在這個步驟中,最佳實踐是在調用LocalizeAsync對錨進行定位之前等待成功的結果。當你定位一個錨時,它會致使系統確定錨點在世界中的姿勢。
// C/C++var unboundAnchors = new List();await OVRSpatialAnchor.LoadUnboundAnchorsAsync(uuids, unboundAnchors);foreach (var unboundAnchor in unboundAnchors){ if (await unboundAnchor.LocalizeAsync()) { // Bind anchor here }}
定位成功后,你可以通過調用內置的BindTo函數將未綁定的錨綁定到它的OVRSpatialAnchor組件。
// C/C++if (!unboundAnchor.TryGetPose(out var pose)){return;}var anchorObject = Instantiate(anchorPrefab.gameObject, pose.position, pose.rotation);var spatialAnchor = anchorObject.GetComponent();unboundAnchor.BindTo(spatialAnchor); 1.4 擦除錨
OVRSpatialAnchor.EraseAnchorAsync函數用于從持久存儲中擦除空間錨。在那之后,最好的做法是擦除錨GameObject,停止在運行時跟蹤它,從而為新的錨釋放存儲空間。
// C/C++await anchor.EraseAnchorAsync();Destroy(anchor.gameObject);
使用上述步驟,你現在可以輕松地創建、保存、加載和刪除錨。接下來,我們將介紹共享錨和對齊用戶的姿勢的關鍵配置功能。
2. 創造共在體驗
為了設置Colocation,你可以在Unity Meta XR SDK的v71及以上版本中使用Colocation Discovery。這個功能允許附近的用戶通過藍牙發現彼此,并且它屬于OVRColocationSession類。通常,廣告客戶端尋求充當多用戶體驗的主機,而發現客戶端對加入托管體驗感興趣。
2.1 Colocation Discovery的需求
要啟用Colocation Discovery,需要將OVR Manager的Colocation Session Support權限設置為“Required”,從而將以下權限添加到Android Manifest文件中:
// C/C++
Colocation的要求非常少,但需要滿足以下三個條件之一:
用戶是經過驗證的開發者組織的成員。
用戶是來自擁有應用程序的開發者組織的測試用戶。
用戶由開發者組織通過Release Channel邀請(Production除外)。
對于共享空間錨,共享空間錨支持權限同樣需要設置為“Required”。另外,設備必須接到互聯網。最后,Meta Quest設備必須啟用增強空間服務。打開“增強空間服務”,進入“設置>隱私和安全>設備權限”,選擇“增強空間服務”。應用程序將檢測設置何時禁用,并通知用戶打開它。
2.2 理解基于組的錨共享和加載
從v71開始,空間錨共享和加載變成基于組,而不是基于用戶。這消除了用戶對應用的授權需求,以及開發者通過在Developer Dashboard驗證應用來管理用戶ID的需求。你可以使用任意組UUID來共享和加載錨,所以推薦使用Group Sharing。
在與組共享空間錨之前,其中一個參與者(通常是主機)必須創建一個表示組的UUID,并將其傳遞給其他參與者。這種通信可以通過應用管理的網絡來實現,比如Unity Netcode或Photon Fusion,或者通過Colocation Discovery來實現,從而大大減少最終用戶在設置Colocation體驗方面的摩擦。
2.3 Colocation Discovery:會話廣告和錨共享
組ID是自動生成。在下面的代碼中,主機開始發布,成功發布后,你可以從發布結果中讀取組ID。
// C/C++var advertisementResult = await OVRColocationSession.StartAdvertisementAsync(null);_groupId = advertisementResult.Value;// Create and save anchor here
除了在StartAdvertisementAsync函數中發送null之外,你同時可以以字符串的形式發送一定的會話信息,例如會話名稱,或者簡單地作為消息發送給其他用戶。最多可以發送1024字節的數據。
在使用上面介紹的步驟創建并保存空間錨之后,你可以調用基于組的函數來共享錨。
// C/C++OVRSpatialAnchor.ShareAsync(new List{ anchor }, _groupId);
2.4 Colocation Discovery:會話發現和錨加載
在主機開始發布會話并成功共享錨(包括創建的組ID)之后,所有其他用戶都可以發現會話。下面你可以看到,在使用StartDiscoveryAsync開始發現之前,已訂閱了OnColocationSessionDiscovered事件。
// C/C++OVRColocationSession.ColocationSessionDiscovered += OnColocationSessionDiscovered;OVRColocationSession.StartDiscoveryAsync();
一旦發現了Colocation會話,就會激活OnColocationSessionDiscovered。接下來,你可以從發現的會話中讀取組ID,并使用它來加載錨。
// C/C++private void OnColocationSessionDiscovered(OVRColocationSession.Data sessionData){_groupId = session.AdvertisementUuid;// Load anchor here}如前所述,現在可以加載錨,但這次函數名為LoadUnboundSharedAnchorsAsync,并將你的組ID作為輸入。
// C/C++var unboundAnchors = new List();await OVRSpatialAnchor.LoadUnboundSharedAnchorsAsync(_groupId, unboundAnchors);
成功加載錨之后,請記住按照上面介紹的步驟定位并綁定到OVRSpatialAnchor組件。
2.5 錨對齊
現在,用戶已經準備好與錨的姿勢對齊。對齊是創建真正共在體驗的關鍵步驟,因為它確保所有用戶與主機擁有相同的追蹤空間。為了實現這一點,你可以調整用戶的Camera Rig位置并旋轉到錨的姿勢。
// C/C++cameraRig.Transform.position = anchor.Transform.InverseTransformPoint(Vector3.zero);cameraRig.Transform.eulerAngles = new Vector3(0, -anchor.Transform.eulerAngles.y, 0);
3. 使用Space Sharing API
現在,你可以使用Meta XR SDK v74及以上版本的Space Sharing API來更進一步。
在MRUK中托管的Space Sharing API確保房間布局能夠在客戶端之間無縫共享,同時為流行的Colocation用例提供無縫解決方案。

3.1 需求和限制
只有主機需要事先或在體驗開始時掃描房間。
APK必須上傳到Developer Dashboard的Release Channel,所有用戶或測試用戶必須獲邀或成為組織的一份子。
不可能在兩個具有相同帳戶的設備之間共享空間。所以,在開發過程中測試空間共享的方法有兩種:
使用你的設備和別人的設備,并在兩者之間共享一個空間。確保其他設備的帳戶有權使用你的應用程序。
創建一個測試用戶,并在第二臺設備使用測試用戶登錄。對于這種情況,確保測試用戶的電子郵件受邀到Release Channel,并有權使用應用。
3.2 空間共享設置
為了簡化設置過程,你可以將Space Sharing API 與 Colocation Discover結合起來。要共享一個房間,主機需要與MRUK單例實例通信,獲取MRUK房間列表并調用內置的ShareRoomsAsync函數。
// C/C++// For sharing multiple roomsvar rooms = MRUK.Instance.Rooms;MRUK.Instance.ShareRoomsAsync(rooms, _groupId);// For sharing a single (current) roomvar room = MRUK.Instance.GetCurrentRoom();room.ShareRoomAsync(_groupId);
類似地,其他用戶可以加載與組ID共享的所有房間,并將自己與房間的地板世界姿勢對齊。
// C/C++MRUK.Instance.LoadSceneFromSharedRooms(null, _groupId, alignmentData: (roomUuid, remoteFloorWorldPose));
有關如何獲取所有房間信息(如地板世界姿勢)的詳細代碼和信息,請訪問這個。

