基於XR Interaction ToolKit與PUN實現VR多人協同功能

ssssssilver發表於2020-11-06

        最近在整VR多人協同相關的開發工作,因為沒怎麼接觸過多人開發這一塊,碰到了不少坑,在這裡總結一下個人的一些經驗。

一.開發環境配置

       Unity版本:2019.3.4

       相關外掛版本:

      Universal RP 7.1.8

      XR Interaction Tookit preview -0.9.4

      PUN2 2.22

    關於XR Interaction Tookit

      之前有做過相關的介紹與使用說明 參考這篇文章 基於XR Interaction ToolKit開發的VR雙平臺相容專案(一) 這裡就不過多說明了

    關於PUN2

        PUN(Photon Unity Networking)是unity上一款比較有名的多人互動的解決方案 網上有不少關於它的介紹與說明,這裡也不再贅述了。不過有個可以優化一下PUN的網路的配置在這裡要說明一下。因為PUN的伺服器預設都是在海外的,國內用可能會有延時的情況出現。可以去Poton的中國官網申請免費的國內伺服器,具體方法如下

1.進入官網(https://vibrantlink.com/

2.首頁中找到“免費申請中國區光子云”

3.填寫相關的資料並提交稽核 ,這裡需要注意的是需要把之前在官網建立的需要切到中國區的appid輸入到申請表裡面,並注意不要選擇錯型別。

4.一兩個工作日後郵箱收到成功的郵件,就可以在Unity進行配置。

5.在unity中找到PUN的配置檔案PhotonServerSettings,修改裡面的Fixed Region為CN;找到指令碼LoadBalancingClient.cs 把裡面的NameServerHost屬性修改成ns.photonengine.cn

配置好以上的選項後,測試了下網速基本是在50ms以下的延時。

二.開發的一些細節

1.模型的載入與同步

通過PUN提供的PhotonNetwork.Instantiate方法,可以把本地的模型建立並且同步到各自的客戶端。但如果要下載伺服器上的模型資源,並且同步到其他的使用者的話還需要配合其他相關的方法來實現。

本人是通過Photon提供的元件同步的方法來實現類似的效果,具體實現如下:

a. 通過PhotonNetwork.Instantiate來建立一個空物體,並在上面掛載photonView元件,同時也掛上自定義的同步載入類ModelEntity,這個類要繼承IPunObservable介面,並且實現OnPhotonSerializeView方法的話,就會自動被photonView檢測到。

    ModelEntity的相關程式碼如下:

public class ModelEntity : MonoBehaviourPunCallbacks, IPunObservable
    {

        string id;
        int num;
        public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
        {
            //擁有者 會把資料不斷髮送給其他人
            if (stream.IsWriting)
            {

                stream.SendNext(id);
                stream.SendNext(num);
            }
            else
            {

                // 其他使用者 當擁有者傳送資料會接收到資料
                id= (string)stream.ReceiveNext();
                num= (string)stream.ReceiveNext();


            }
        }

    }
}

b.在例項化這個同步的gameObject物件後,建立者把需要同步的id資料傳進ModelEntity中,同時也要載入伺服器的場景到本地;其他使用者會通過Photon網路獲取到建立的這個gameObject物件空物體,在檢測到對應的id後,再通過網路載入對應id的模型到本地。這樣就簡單實現了同步載入伺服器的模型的功能

2.物件的接管與操作

在應用中建立的同步物件,預設是隻能建立者操作,如果其他使用者想要接管這個物件並對它進行變換或者刪除操作,需要獲取到這個物件的操作權,具體操作如下:

a.將同步的物件掛載的photonView元件上的Ownership Transfer設定成 Takeover,表示這個物件是可以被接管的

b.通過程式碼獲取到這個物件的photonView,設定gameObject.GetComponent<PhotonView>().TransferOwnership(PhotonNetwork.LocalPlayer),這個操作是強行把物件的一個控制權轉交給本地的使用者

c.如果需要同步物體的變換資訊的話,可以在物件上掛在PhotonTransformView元件,根據需求勾選position,rotation和scale,這樣的話本地使用者不管是場景中直接拖拽或者是在程式碼中設定,都能過同步給其他網路使用者

d.刪除物件 通過PhotonNetwork.Destroy()可以把網路同步的物件銷燬掉。

 

3.VR使用者物件的建立

在多人VR中,並不需要為每個使用者建立一個camera,因為我們只需要通過一個Camera元件看到世界物體,增加其他的使用者Camera反而會出現報錯 。如果真的需要同步其他的VR使用者,只需通過PhotonNetwork.Instantiate建立一個玩家的物件模型,並且把本地的camera位置不斷同步到這個物件模型上(VR中頭像的位置就是camera的位置)

4.VR畫線功能的實現

在多人互動中,如果需要通過把手柄當成“筆”來進行畫線,並且同步到其他的使用者,這就涉及了LineRenderer與同步的相關知識點。通過LineRenderer來記錄手柄經過的每個途徑點並且繪製成線,並且同步到其他使用者的世界座標中。在這裡要注意的是,同步畫線“點”的操作是在使用者繪製完一條線之後,而不是邊繪製邊同步。因為不確定兩邊的網路情況延時怎樣,如果是邊畫邊同步途徑點的話,在本地看起來是正常的,但其他網路使用者裡看到的東西基本是一塌糊塗。

同時也不建議每幀進行取點,不然對網路的負荷太大。而且也不建議每個位置都取點,而是判斷點與點的距離大於某個閥值後再取點,這樣也避免了大量點在同一個位置的情況。

5.使用者離開房間的處理

當使用者離開了房間或者由於異常斷網,使用者通過PhotonNetwork.Instantiate建立的物件並不會刪除,而是預設轉給了ClientMaster。如果使用者建立過人物模型模型用來同步位置的話,這時就會遺留在場景裡面。所以還需要對掉線的物件進行處理。 我的處理方法是建立出的使用者虛擬形象裡面新增一個使用者離開房間的檢測事件,當檢測到使用者是對應這個虛擬形象的模型時,就刪除該物件,具體實現程式碼如下:

    public override void OnPlayerLeftRoom(Player otherPlayer)
    {
        if (owner == otherPlayer.NickName)
        {
            PhotonNetwork.Destroy(gameObject);
        }
    }

其中這個owner是在例項化這個物件時獲取到的LocalPlayer.NickName。可能有人問為什麼不直接用photonView.owner.NickName跟掉線的使用者名稱進行判斷,上面已經說過,當使用者掉線時,模型就預設切換成ClientMaster,自然獲取到的photonView.owner就不是對應掉線的使用者了。

隨便總結一下

因為是第一次接觸多人互動程式設計這一塊,很多東西都是瞎摸出來的,可能大多數並不是最優的方案,如果有錯誤的地方也希望有人能指正一下。同時也非常感謝【我是橙子a】這位博主的文章教程,真的學習了不少。

 

相關文章