Unity的網絡功能

首先要說明的是,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構建多人遊戲

傳遞遊戲對象引用

在構建多人遊戲過程中,除了需要通過網絡傳遞一些基本數據類型的數據(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
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)
    {
        NetworkView nvw = NetworkView.Find(viewID);
        return (nvw == null) ? null : nvw.GetComponent<NetworkViewWrapper>();
    }
    public void RPC(string name, NetworkPlayer target, params object[] args)
    {
        if (Network.peerType == NetworkPeerType.Disconnected)
            return;
        networkView.RPC(name, target, args);
    }
    public void RPC(string name, RPCMode mode, params object[] args)
    {
        if (Network.peerType == NetworkPeerType.Disconnected)
            return;
        networkView.RPC(name, mode, args);
    }
}

使用NetObject封裝對象的創建和銷燬
NetObject.cs
using UnityEngine;
using System.Collections;

public static class NetObject
{
    public static Object instantiate(Object prefab, Vector3 position, Quaternion rotation)
    {
        if (Network.peerType == NetworkPeerType.Disconnected)
            return Object.Instantiate(prefab, position, rotation);
        else
            return Network.Instantiate(prefab, position, rotation, 0);
    }
    public static void destroy(GameObject gameObject)
    {
        if (Network.peerType == NetworkPeerType.Disconnected)
            Object.Destroy(gameObject);
        else
            Network.Destroy(gameObject);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章