首先要說明的是,Unity本身的網路功能並不適合做MMO類型的遊戲。如果要使用Unity作為MMO遊戲的用戶端,一般來說都是在C#中通過socket建立自訂的網路通訊來實現。Unity本身的網路功能是為多人遊戲設計的,這種遊戲模式一般來說就是一個玩家建立遊戲(這個玩家既是server,又是client),其他玩家串連進來。從頻寬考慮一般同時支援的玩家數量小於64個(這個也不一定,要看遊戲本身的設計)。這些玩家一般都在一個區域網路內互聯,但是如果使用MasterServer進行配對,也可以在Internet上相互串連。很多所謂的單機並支援多人連線的遊戲基本上都是用這樣的網路模式。Unity本身的網路功能是可以跨平台的,可以運行於PC,IOS,Android,並且支援不同平台的用戶端通訊。 網路通訊協定
Unity網路通訊協定本身是一個比較高層的網路通訊協定,他並不是獨立存在的,而是與引擎的遊戲對象結合在一起,為遊戲提供網路功能支援。要說明Unity的網路通訊協定,就先要說明一下在網路模式下遊戲對象的管理。 網路遊戲對象管理
在Unity網路模式下,建立一個遊戲對象執行個體,是通過Network.Instantiate()函數來實現的。這個函數與Object.Instantiate()類似,會在本地建立一個遊戲對象執行個體,並且還會在所有互聯的主機上也同時建立同樣的遊戲對象執行個體,這些遊戲對象具有相同的NetworkView ID。每個被建立出來的遊戲對象都使用NetworkView.isMine來標識自己的身份。如果isMine為true,就表示此遊戲對象是本機建立的;false則表示是其他主機建立,並同步到原生。遊戲對象的isMine身份與網路底層的server和client身份沒有任何關係,在server和client上都可以有任何身份的遊戲對象存在,這主要取決於遊戲對象是誰建立的。如果要銷毀遊戲對象,應該調用Network.Destroy()來代替Object.Destroy(),他會在所有主機上銷毀這個對象。 基於遊戲對象的網路通訊協定
在Unity中不存在單純的“發送給伺服器”或“發送給用戶端的”網路訊息,Unity的網路通訊協定是基於一個具體的遊戲對象的通訊協定,這就意味著所有的通訊上下文都與一個具體的遊戲對象有關。一旦在網路環境下建立了遊戲對象,這些在各個主機上被建立的遊戲對象之間就已經建立好了通訊關係,任何一個主機上的遊戲對象都可以與其他主機上的“自己”進行通訊。不同的遊戲對象之間的通訊是獨立的,就好像每個遊戲對象都有一個獨立的通訊管道。
遊戲對象之間的通訊在形式上分為兩種: 狀態同步
遊戲對象中經常發生變化,並且可以被描述成對象屬性的資料。狀態同步的方向只能是isMine到非isMine。比如一個玩家角色對象hp屬性,如果需要其他玩家能夠看到,就可以使用狀態同步來完成。當isMine的玩家角色的hp變化後,會自動更新這個屬性給其他的主機上的同一個玩家角色對象。狀態同步還支援保證和非保證模式。 RPC
在其他主機的相同遊戲對象上執行一個遠程函數調用。RPC一般用來通過網路通知一次性的遊戲事件,比如”遊戲對象A受到了30點傷害“等等。RPC的傳輸方向非常自由,可以在一個主機向任何其他主機,包括自己,發送RPC調用。
網路遊戲對象管理+狀態同步+RPC組成了Unity的網路通訊協定,所有需要實現的多人遊戲的網路功能都是構建在這套Unity網路通訊協定的基礎之上的。
使用Unity構建多人遊戲 傳遞遊戲對象引用
在構建多人遊戲過程中,除了需要通過網路傳遞一些基礎資料型別 (Elementary Data Type)的資料(int, string...),有時候還需要傳遞一個遊戲對象的引用。比如遊戲對象A通過RPC通知給其他主機他消滅了遊戲對象B,這裡B就是遊戲對象的引用。類似的需要還有很多,那麼我們如何傳遞這個引用呢。這就需要NetworkView.viewID了。NetworkView.viewID是一個NetworkViewID類型的對象,RPC本身支援傳輸這種資料類型。在發送方只要將B的NetworkView.viewID當作RPC的參數傳遞給接收方,接收方再通過NetworkView.Find()在本地將viewID轉換成對應的遊戲對象引用就可以了。 全域對象
在單機遊戲中,情境中一般會建立一個遊戲的全域對象,來處理整體的遊戲狀態。比如關卡的完成情況,全域的事件處理等等。在多人遊戲中,同樣也需要這樣的功能。一般來說,這個全域對象可以在server端建立成網路遊戲對象,並複製給其他的client。這個全域對象一般用來處理以下功能: 新串連的處理:當一個client串連到server後,全域對象會被自動建立到該client。client可以使用基於這個全域對象的一個RPC,當作第一條發送給伺服器的訊息,這個訊息一般帶有自己的身份和遊戲資訊。server處理這個RPC來實現遊戲層面的玩家登入。 同步情境切換:當遊戲需要所有玩家同時切換情境時,需要全域對象進行通知和同步。 遊戲狀態管理和同步:全域遊戲對象一般在server端運行遊戲邏輯,並將需要client知道的遊戲狀態和遊戲事件通知給所有的client。 融合單人遊戲
多人遊戲一般帶有單人模式,而且他們很多遊戲邏輯是相同的。在編寫遊戲邏輯代碼時,除了必須的網路功能代碼外,應該盡量掩蓋單人與多人遊戲的區別,使代碼更好理解和維護。我們可以把單人遊戲看成是只有一個本地用戶端的多人遊戲,並且屏蔽掉網路功能而已。
使用NetworkViewWrapper代替直接使用NetworkView,使isMine在單機模式下也有效,並且屏蔽掉單機模式的RPC功能。
NetworkViewWrapper.cs [csharp] view plain copy print ? using UnityEngine; using System.Collections; /** 封裝NetworkView,使之能夠相容非網路模式. */ [RequireComponent(typeof(NetworkView))] public class NetworkViewWrapper : MonoBehaviour { public int group { get { return networkView.group; } set { networkView.group = value; } } public bool isMine { get { if(Network.peerType == NetworkPeerType.Disconnected) return true; return networkView.isMine; } } public Component observed { get { return networkView.observed; } set { networkView.observed = value; } } public NetworkPlayer owner { get { return networkView.owner; } } public NetworkStateSynchronization stateSynchronization { get { return networkView.stateSynchronization; } set { networkView.stateSynchronization = value; } } public NetworkViewID viewID { get { return networkView.viewID; } set { networkView.viewID = value; } } public static NetworkViewWrapper Find(NetworkViewID viewID)