Placeholder Image

字幕列表 影片播放

由 AI 自動生成
  • Hi XR developers!

    你好,XR 開發人員!

  • In previous videos we've looked at a lot of mixed reality features such as pass-through, scene understanding and environment occlusion.

    在之前的視頻中,我們已經瞭解了很多混合現實功能,例如穿透、場景理解和環境閉塞。

  • We've also looked at several methods on how to place objects in our room with the MRUK or Mixed Reality Utility Kit or MetaScene API with the OVR Scene Manager.

    我們還研究瞭如何使用 MRUK 或混合現實實用工具包或 MetaScene API 以及 OVR 場景管理器在房間中放置物體的幾種方法。

  • But what if we want to save our objects consistently across game sessions in our scene and load them where they were before?

    但是,如果我們想在場景中跨遊戲會話一致地保存對象,並將它們加載到原來的位置,該怎麼辦呢?

  • This is where spatial anchors come into play.

    這就是空間錨點發揮作用的地方。

  • So today we're going to look at how to create, save, unsave and load spatial anchors in our scene.

    是以,今天我們將瞭解如何在場景中創建、保存、取消保存和加載空間錨點。

  • If you like this type of content, please take a second to like and subscribe to this channel.

    如果您喜歡這類內容,請花點時間點贊並訂閱本頻道。

  • If you'd like to get access to the source code of each project, please consider to join my Patreon.

    如果您想獲得每個項目的源代碼,請考慮加入我的 Patreon。

  • If you have any questions, feel free to join our growing XR developer community on Discord.

    如果您有任何問題,請隨時加入我們在 Discord 上不斷壯大的 XR 開發人員社區。

  • And now, let's jump into OVR Spatial Anchors.

    現在,讓我們進入 OVR 空間錨點。

  • As always, we start by setting up a new Unity project.

    像往常一樣,我們首先要建立一個新的 Unity 項目。

  • I am using the MetaXR SDK version 60.

    我使用的是 MetaXR SDK 60 版本。

  • I also installed the Mixed Reality Utility Kit for a later showcase.

    我還安裝了混合現實實用工具包,以便稍後展示。

  • To know how to use the Utility Kit, watch my video here.

    要了解如何使用實用工具包,請觀看我的視頻。

  • Make sure you set up your project correctly with Meta's project setup tool as well.

    確保使用 Meta 的項目設置工具正確設置項目。

  • To set up our scene, we used Meta's building blocks, which I explained in detail in a previous video, that you can watch here.

    為了建立我們的場景,我們使用了 Meta 的構建模塊,我在之前的視頻中對其進行了詳細介紹,您可以點擊此處觀看。

  • For this scene, I only need a camera rig and a pass-through layer.

    在這個場景中,我只需要一個攝影機裝備和一個直通層。

  • I then created a new game object called Spatial Anchor Manager, where we will attach our custom components in a minute.

    然後,我創建了一個名為 "空間錨點管理器 "的新遊戲對象,稍後我們將在此添加自定義組件。

  • I would like to start with a simple anchor placement script.

    我想從一個簡單的錨點放置腳本開始。

  • Inside this class, we declare a public variable called AnchorPrefab to hold a reference to the prefab you want to instantiate in the game.

    在該類中,我們聲明瞭一個名為 AnchorPrefab 的公有變量,用於保存對要在遊戲中實例化的預製板的引用。

  • Inside the update method, there's a conditional check using the OVR input to determine if the primary index trigger button on the right Oculus Touch controller has been pressed.

    在更新方法中,有一個使用 OVR 輸入的條件檢查,以確定右側 Oculus Touch 控制器上的主索引觸發按鈕是否被按下。

  • If you are interested to learn in detail how to read the input from your OVR controllers, check out my video about it here.

    如果您想詳細瞭解如何讀取 OVR 控制器的輸入,請點擊此處觀看我的相關視頻。

  • When pressed, it calls the CreateSpatialAnchor method.

    按下時,它會調用 CreateSpatialAnchor 方法。

  • This method creates an instance of the prefab at the current position and rotation of the right controller, utilizing the OVR input, and then adds an OVRSpatialAnchor component to newly created prefab instance.

    該方法利用 OVR 輸入,在右控制器的當前位置和旋轉位置創建一個預製件實例,然後在新創建的預製件實例中添加一個 OVRSpatialAnchor 組件。

  • Let's set it up in our scene now by applying the script to our new game object.

    現在,讓我們將腳本應用到新遊戲對象,在場景中進行設置。

  • We then need a prefab we would like to spawn.

    然後,我們需要一個想要生成的預製件。

  • The prefab we will use here has no logic on it, it simply contains the visuals of our anchor.

    我們在這裡使用的預製件沒有任何邏輯,它只是包含了我們錨點的視覺效果。

  • Drag the prefab into the inspector, and let's give this a try.

    將預製件拖入檢查器,讓我們試一試。

  • Fantastic!

    太棒了

  • As you can see, we can now easily create anchors at the pressing the trigger button.

    如你所見,我們現在只需按下觸發按鈕,就能輕鬆創建錨點。

  • Let's go one step further and see how we can save or unsave these anchors at this exact location in our space.

    讓我們更進一步,看看如何在我們空間的這個確切位置保存或取消這些錨點。

  • We create a new script for our extended logic that we will call SpatialAnchorManager.

    我們為擴展邏輯創建一個新腳本,並將其稱為 SpatialAnchorManager。

  • We can keep the reference to our prefab, but we want to add a little bit more information about our anchor.

    我們可以保留對預製件的引用,但我們希望添加更多關於錨點的資訊。

  • We create a private variable for a canvas that we will later add to our prefab.

    我們為畫布創建一個私有變量,稍後將把它添加到預製板中。

  • This canvas holds two text fields for the anchor's ID and also a text that tells us if the anchor has been saved or not.

    該畫布上有兩個文本字段,分別用於顯示錨點的 ID 和告訴我們是否保存了錨點的文本。

  • We then need a list of anchors to keep track of them in order to save or unsave them.

    然後,我們需要一個錨點列表來跟蹤它們,以便保存或取消保存。

  • Lastly, we define a private spatial anchor called LastCreatedAnchor to be able to either save or unsave the currently created anchor.

    最後,我們定義了一個名為 LastCreatedAnchor 的私有空間錨點,以便保存或取消保存當前創建的錨點。

  • Perfect!

    太完美了

  • We check again if we pressed our trigger button, and if so, we call the CreateSpatialAnchor method.

    我們再次檢查是否按下了觸發按鈕,如果是,則調用 CreateSpatialAnchor 方法。

  • The difference here is that we will already apply an OVR spatial anchor component to our prefab, so we can simply instantiate it here without adding this component.

    這裡的不同之處在於,我們已經將 OVR 空間錨組件應用到了預製件中,是以我們可以直接在這裡將其實例化,而無需添加此組件。

  • We then save the canvas and text fields of this particular anchor in our variables.

    然後,我們將此特定錨點的畫布和文本字段保存在變量中。

  • Our goal is to get the anchor's ID after it has been created.

    我們的目標是在創建錨點後獲取其 ID。

  • Since this process is working asynchronously by the Oculus Runtime, we can best handle this delay by calling a coroutine which we will call AnchorCreated.

    由於這個過程是由 Oculus Runtime 異步處理的,我們可以通過調用一個 coroutine(我們將其稱為 AnchorCreated)來最好地處理這種延遲。

  • In this case, we provide this method with our created anchor and then wait until the creation is complete and the anchor has been localized by the Oculus Runtime.

    在這種情況下,我們向該方法提供已創建的錨點,然後等待創建完成並由 Oculus Runtime 本地化錨點。

  • If so, we can go ahead to fetch the ID of the anchor.

    如果是,我們就可以繼續獲取錨點的 ID。

  • We then add the newly created anchor to our list of anchors.

    然後,我們將新創建的錨添加到錨列表中。

  • After that, we save the new anchor as the anchor we last created.

    之後,我們將新錨點保存為上次創建的錨點。

  • This allows us to finally set our ID text as well as our status text.

    這樣,我們就可以最終設置 ID 文本和狀態文本了。

  • Let's set this up in our Unity scene now.

    現在讓我們在 Unity 場景中設置一下。

  • We can remove the old script and apply the SpatialAnchorManager.

    我們可以刪除舊腳本並應用 SpatialAnchorManager。

  • We then have to apply a new prefab.

    然後,我們必須應用一個新的預製件。

  • You can see that I already created one which contains an OVR spatial anchor component.

    您可以看到,我已經創建了一個包含 OVR 空間錨點組件的錨點。

  • Furthermore, I added a canvas which contains two text fields.

    此外,我還添加了一個畫布,其中包含兩個文本字段。

  • We can now apply this prefab to our and test out our new prefab creation logic.

    現在我們可以將該預製件應用到我們的預製件中,並測試我們新的預製件創建邏輯。

  • Great!

    好極了

  • As you can see, we are now able to not only create but wait for and display each anchor's ID which is crucial for our next step.

    如您所見,我們現在不僅可以創建,還可以等待並顯示每個錨的 ID,這對我們下一步的工作至關重要。

  • In the next step, we want to be able to save and also unsave the currently selected anchor.

    下一步,我們希望能夠保存或取消保存當前選定的錨點。

  • To do that, we make use of Unity Player Prefs.

    為此,我們使用了 Unity Player Prefs。

  • Unity Player Prefs are a simple key value storage system used to save and load small amounts of data such as player preferences or game settings between game sessions without requiring complex file handling.

    Unity 玩家首選項是一個簡單的鍵值存儲系統,用於在遊戲會話之間保存和加載玩家首選項或遊戲設置等少量數據,而無需複雜的文件處理。

  • To save those key values, we create a constant string that will save the anchor's IDs.

    為了保存這些鍵值,我們創建了一個常量字符串來保存錨的 ID。

  • We use constant strings for Unity Player Prefs keys to prevent typos and ensure consistency.

    我們對 Unity 播放器預置鍵使用常量字符串,以防止錯別字並確保一致性。

  • In the update method, we would like to introduce two new buttons.

    在更新方法中,我們要引入兩個新按鈕。

  • If we press button 1, we would like to save our anchor and we unsave it when pressing button 2.

    如果我們按下按鈕 1,就會保存錨點,按下按鈕 2 則會取消保存。

  • If we now look at the save last created anchor method, we can see that all we need to do is to call the save method.

    如果我們現在看看保存最後創建的錨點方法,就會發現我們需要做的就是調用保存方法。

  • If the saving was successful, we would like to update the status text on the anchor to saved.

    如果保存成功,我們希望將錨點上的狀態文本更新為已保存。

  • Lastly, to save the anchor to our Player Prefs, we call the save ID to Player Prefs method and provide it with the anchor's ID.

    最後,為了將錨點保存到玩家首選項中,我們調用了將 ID 保存到玩家首選項的方法,並向其提供了錨點的 ID。

  • It first checks if the key exists, indicating the total count of IDs saved.

    它首先檢查鍵是否存在,然後顯示保存的 ID 總數。

  • If not found, it initializes this count to zero.

    如果未找到,則將該計數初始化為零。

  • Then, it retrieves the current number of IDs saved, saves the new ID using a key formed by appending this number to UUID, and increments the saved ID count by updating the string with the new total.

    然後,它會檢索當前保存的 ID 數量,使用將該數量追加到 UUID 形成的鍵保存新的 ID,並用新的總數更新字符串,從而增加保存的 ID 數量。

  • Lastly, to unsave, we literally just have to call the erase method from Oculus, from our anchor, and update the status text.

    最後,要取消保存,我們只需從錨點調用 Oculus 的擦除方法,並更新狀態文本即可。

  • Optionally, if you would like to actually delete your anchor, you could now simply call the destroy method from Unity.

    如果你想真正刪除錨點,現在只需調用 Unity 中的 destroy 方法即可。

  • Let's go back to Unity and give this a go.

    讓我們回到 Unity 試試吧。

  • By pressing the A button, we can now save the anchor and easily unsave it again by pressing the B button.

    按下 A 按鈕,我們就可以保存錨點,按下 B 按鈕又可以輕鬆取消保存。

  • Fantastic!

    太棒了

  • We now built the foundation for our last and final iteration of our code.

    現在,我們已經為最後一次迭代代碼打下了基礎。

  • Here, I would like to show you how to unsave all anchors at once, and how to load anchors we have saved before.

    在這裡,我想向大家展示如何一次性取消保存所有錨點,以及如何加載我們之前保存的錨點。

  • Firstly, we create another private variable called anchorLoader.

    首先,我們創建另一個名為 anchorLoader 的私有變量。

  • This will be a script we create in a second.

    這將是我們稍後創建的腳本。

  • Since we want to attach this component to the same game object, we can fetch a reference directly in the awake method.

    由於我們要將該組件附加到同一個遊戲對象上,是以可以直接在喚醒方法中獲取引用。

  • Then, in the update method, if we press the grab button, we want to unsave all the anchors in our list and the ones that are already saved to the player prefs.

    然後,在更新方法中,如果我們按下抓取按鈕,就會取消保存列表中的所有錨點和已經保存到播放器預置中的錨點。

  • If we press down on the thumbstick, we would like to load all the anchors we have previously saved to the player prefs.

    如果我們向下按拇指杆,就會加載之前保存到播放器預置中的所有錨點。

  • In the unsaveAllAnchors method, we iterate through our list of anchors and call the unsaveAnchor method for each of them.

    在 unsaveAllAnchors 方法中,我們會遍歷錨點列表,並調用每個錨點的 unsaveAnchor 方法。

  • We provide this method with the anchor parameter and call the erase method on that anchor similarly to before.

    我們為該方法提供錨點參數,並在該錨點上調用擦除方法,與之前的方法類似。

  • We then get a reference to the status text of each anchor and set the text to not saved.

    然後,我們獲取每個錨點狀態文本的引用,並將文本設置為未保存。

  • After that, back in the unsaveAllAnchors method, we clear the anchors list and call the clearAllIds from player prefs method to not only remove our list of anchors, but also the anchors that were already saved previously.

    之後,在 unsaveAllAnchors 方法中,我們會清除錨點列表,並調用播放器預置方法中的 clearAllIds,這樣不僅能刪除錨點列表,還能刪除之前已保存的錨點。

  • This method checks if the key exists in player prefs, indicating there are already saved IDs.

    該方法會檢查播放器預置中是否存在密鑰,這表明已經保存了 ID。

  • If it exists, it retrieves the count of saved IDs and iterates through each one, deleting the keys associated with them.

    如果存在,它會檢索已保存 ID 的計數,並遍歷每個 ID,刪除與之相關的鍵。

  • After deleting all ID keys, it removes the key itself.

    刪除所有 ID 密鑰後,它會刪除密鑰本身。

  • Effectively resetting the count and finally calls playerPrefs.save to actually commit these changes to the metaquest device.

    有效重置計數,最後調用 playerPrefs.save 將這些更改提交到元查詢設備。

  • Next, for loading our saved anchors, we call the loadAnchorsById method from a new script that we are going to create now called anchorLoader.

    接下來,為了加載已保存的錨點,我們將調用一個新腳本中的 loadAnchorsById 方法,該腳本名為 anchorLoader。

  • The script starts with a declaration of several important variables.

    腳本首先聲明瞭幾個重要變量。

  • First, we declare a private variable that holds a reference to the anchor prefab.

    首先,我們聲明一個私有變量,該變量包含對錨點預製件的引用。

  • We then create another variable that holds a reference to the SpatialAnchorManager component.

    然後,我們創建另一個變量,該變量包含對 SpatialAnchorManager 組件的引用。

  • Lastly, we create a unity action that defines a callbacks related to anchor loading operations.

    最後,我們創建一個統一動作,定義與錨點加載操作相關的回調。

  • The boolean parameter indicates the success of the loading operation, since this component will sit on the same game object as the SpatialAnchorManager.

    布爾參數表示加載操作成功,因為該組件將與 SpatialAnchorManager 位於同一個遊戲對象上。

  • We can easily get the component in the awake method.

    我們可以通過喚醒方法輕鬆獲取組件。

  • We can then also get a reference to the prefab that we already assigned in the SpatialAnchorManager.

    然後,我們還可以獲取我們已經在 SpatialAnchorManager 中分配的預製板的引用。

  • Lastly, we call the onLocalized method as soon as our loading process has been successful.

    最後,一旦加載過程成功,我們就會調用 onLocalized 方法。

  • This loadAnchorsById method, which we call directly from the SpatialAnchorManager, initiates the process of loading spatial anchors that have been previously saved by their IDs.

    我們直接從空間錨點管理器(SpatialAnchorManager)中調用 loadAnchorsById 方法,啟動加載空間錨點的過程,這些空間錨點之前已按其 ID 保存。

  • It starts by checking if there is a record in player prefs for the number of IDs saved.

    首先,它會檢查播放器預置中是否有保存 ID 數量的記錄。

  • If no record exists, it sets this count to zero, indicating there are IDs to load.

    如果不存在記錄,則會將此計數設為零,表示有 ID 需要加載。

  • It then retrieves the count of IDs and proceeds only if there is at least one ID to load.

    然後,它會檢索 ID 的計數,只有在至少有一個 ID 需要加載時才繼續。

  • For each saved ID, it constructs a GUID object from the string retrieved from player prefs.

    對於每個已保存的 ID,它都會根據從播放器預置中獲取的字符串構建一個 GUID 對象。

  • These IDs are used to configure a loadOptions object, specifying that the anchors should be loaded from local storage with no timeout, and then passed to the load method.

    這些 ID 用於配置 loadOptions 對象,指定錨點應從在地存儲加載,且不超時,然後傳遞給加載方法。

  • There is two options of saving and loading anchors, locally or in the cloud.

    保存和加載錨點有兩種選擇,在地或雲端。

  • We will look at cloud saving and loading in a different video about shared spatial anchors.

    我們將在另一個關於共享空間錨點的視頻中介紹雲保存和加載。

  • The load method is responsible for the asynchronous loading of unbound anchors based on the provided load options, which include the IDs to load, the storage location, and a timeout setting.

    加載方法負責根據提供的加載選項(包括要加載的 ID、存儲位置和超時設置)異步加載未綁定的錨點。

  • It calls the static method loadUnboundedAnchors on the OVR spatial anchor to load these anchors, providing a callback function that is called once the loading process completes.

    它調用 OVR 空間錨點上的靜態方法 loadUnboundedAnchors 來加載這些錨點,並在加載過程完成後提供一個回調函數。

  • For each loaded anchor, the method checks if it is already localized.

    對於每個加載的錨點,該方法都會檢查它是否已經本地化。

  • If so, it immediately triggers the onLoadAnchor callback with the anchor and a true success flag.

    如果是這樣,它會立即觸發 onLoadAnchor 回調,並給出錨點和 true 成功標誌。

  • If the anchor is not currently localizing, it initiates the localization process using the onLoadAnchor callback to handle completion.

    如果錨點當前未進行本地化,它將使用 onLoadAnchor 回調啟動本地化進程,以處理完成事宜。

  • This callback function is invoked for each anchor that has been successfully localized or was already localized at the time of loading.

    每個已成功本地化或在加載時已本地化的錨點都會調用該回調函數。

  • It first checks if the localization was successful.

    它首先檢查本地化是否成功。

  • If not, it exits early.

    否則,它會提前退出。

  • For successful localizations, it instantiates the prefab at the pose of the unbound anchor.

    如果定位成功,它就會以未綁定錨點的姿態實例化預製件。

  • It then binds the unbound anchor to this newly instantiated game object, creating a permanent link between the anchor data and the game object.

    然後,它會將未綁定的錨綁定到新實例化的遊戲對象上,在錨數據和遊戲對象之間建立永久鏈接。

  • Lastly, it updates the text components of the instantiated anchor game object with the ID of the anchor and a message indicating that the anchor has been loaded from device storage, providing visual feedback within the scene regarding the status of each loaded anchor.

    最後,它會更新實例化錨點遊戲對象的文本組件,其中包含錨點的 ID 和一條表明已從設備存儲中加載錨點的資訊,從而在場景中提供有關每個已加載錨點狀態的視覺反饋。

  • Let's attach this script to our game object and press play.

    讓我們把這個腳本附加到遊戲對象上,然後按播放鍵。

  • We can now test all our features.

    現在我們可以測試所有功能了。

  • First, we create some anchors and press the A button on the anchors we want to save.

    首先,我們創建一些錨點,然後按下要保存的錨點上的 A 按鈕。

  • If we then stop the game and start it again, we can simply press the thumbstick to load all previously saved anchors.

    如果我們停止遊戲,然後再次啟動,只需按下拇指杆,就能加載之前保存的所有錨點。

  • This process may take a few seconds if the number of anchors is large.

    如果錨點數量較多,這一過程可能需要幾秒鐘。

  • By pressing the grab button, we can erase all our anchors from storage.

    按下 "抓取 "按鈕,我們就可以從存儲中刪除所有錨點。

  • When we then start the game again and try to load our anchors, no anchors will be loaded anymore.

    當我們再次啟動遊戲並嘗試加載錨點時,就不會再加載錨點了。

  • Fantastic!

    太棒了

  • This was a lot of code, but you now know exactly how to handle every aspect of spatial anchors.

    雖然代碼量很大,但你現在已經完全知道如何處理空間錨的各個方面了。

  • Let's look at one last use case before we close off this video.

    在本視頻結束之前,讓我們來看最後一個使用案例。

  • I prepared a scene using the Mixed Reality Utility Kit, where I want to create some logic to attach anchors to my wall.

    我使用混合現實實用工具包準備了一個場景,在這個場景中,我想創建一些邏輯將錨固定在牆上。

  • For this, I replaced the SpatialAnchorManager with a new script called WallPrefabPlacer, which we created in a previous video.

    為此,我用一個名為 WallPrefabPlacer 的新腳本替換了 SpatialAnchorManager,我們在之前的視頻中創建了這個腳本。

  • I added the whole logic of the SpatialAnchorManager to this script.

    我把 SpatialAnchorManager 的整個邏輯都添加到了這個腳本中。

  • The only difference is that we need to wait until the room model is created.

    唯一不同的是,我們需要等待房間模型創建完成。

  • For this, we create this private variable isInitialized and set it true when the initialized method is called.

    為此,我們創建了這個私有變量 isInitialized,並在調用初始化方法時將其設置為 true。

  • Then, in the update method, if we initialized our script, we get the controller position and rotation.

    然後,在更新方法中,如果我們初始化了腳本,就會得到控制器的位置和旋轉。

  • We then shoot a ray in the direction of our controller and check if we hit a wall in our room using the mRUKSingleton and its getCurrentRoom method.

    然後,我們向控制器的方向發射射線,並使用 mRUKSingleton 及其 getCurrentRoom 方法檢查是否擊中了房間的牆壁。

  • If we hit a wall and we are pressing the trigger button, like before, we would like to call the createSpatialAnchor method, with the difference that we now send the position and rotation of the prefab according to the hit position of the ray.

    如果我們撞到牆壁並按下觸發按鈕,就像之前一樣,我們會調用 createSpatialAnchor 方法,不同的是,現在我們會根據射線的撞擊位置發送預製件的位置和旋轉。

  • In the inspector, we assign the AnchorManager that contains our script to the SceneLoadedEvent of the MixedRealityUtilityKit component.

    在檢查器中,我們將包含腳本的 AnchorManager 分配給 MixedRealityUtilityKit 組件的 SceneLoadedEvent。

  • From there, we can call the initialized method.

    在這裡,我們可以調用初始化方法。

  • Also, make sure that you apply a prefab that contains a OVR SpatialAnchor component to the script.

    此外,請確保將包含 OVR SpatialAnchor 組件的預製板應用到腳本中。

  • I created this beautiful image frame that we can now apply to our walls.

    我製作了這個漂亮的相框,現在我們可以把它貼在牆上了。

  • Before I forget it, the last thing we have to do is to fetch the new WallPrefabPlacer in our AnchorLoader, instead of the SpatialAnchorManager.

    在我忘記之前,我們要做的最後一件事是在 AnchorLoader 中獲取新的 WallPrefabPlacer,而不是 SpatialAnchorManager。

  • Let's give this a go.

    讓我們試一試。

  • And as you can see, we can now use the same functionalities as before, with the difference that our anchor now accurately attaches to our walls and gets loaded in exactly the same position when we load them from our device.

    正如你所看到的,我們現在可以使用與之前相同的功能,不同的是,我們的錨點現在可以準確地附著在牆壁上,並且在我們從設備加載它們時,它們會被加載到完全相同的位置。

  • I can't wait to see what you guys will build with this.

    我迫不及待地想看看你們會用它做什麼。

  • Alright guys, and that's it for this video.

    好了,各位,本視頻就到這裡。

  • I hope you learned a lot, and again, please take a second to like and subscribe to this channel.

    我希望你能學到很多東西,再次感謝你對本頻道的支持和訂閱。

  • Subscribe to my Patreon if you want to get access to all the source code.

    如果你想獲得所有源代碼,請訂閱我的 Patreon。

  • If you have any questions, feel free to join our growing XRDeveloper community on Discord.

    如果您有任何問題,請隨時加入我們在 Discord 上不斷壯大的 XRDeveloper 社區。

  • Thank you so much for watching, and see you in the next one.

    感謝您的收看,下期節目再見。

Hi XR developers!

你好,XR 開發人員!

字幕與單字
由 AI 自動生成

單字即點即查 點擊單字可以查詢單字解釋