注:UNet 已經被廢棄, 且未來會被Unity移除。在本文中,將會使用到 NetworkManager/High Level API,import方法:Window->Package Manager->Multiplayer HLAPI。
Networking
網絡功能有兩種類型的用戶:
- 制作多玩家的用戶。這類用戶應該使用 NetworkManager/High Level API。
- 搭建網絡基礎設施或 advanced 多玩家游戲(Multiplayer game) 的用戶。這類用戶應該使用 NetworkTransport API。
High Level API
Unity網絡有一個 “high-level”腳本API(HLAPI)。使用它可以滿足你開發多人游戲的大部分需求,而不用去擔心 “lower level”實現細節。HLAPI允許你:
- 使用 “Network Manager”控制游戲的網絡狀態。
- 操作 “client hosted”游戲,其中 host也是一個 player client。
- 使用通用的序列化器(serializer)序列化數據。
- 發送和接收網絡信息。
- 從 clients發送網絡命令到 servers。
- 從 servers到 clients進行遠程過程調用(remote procedure calls,RPCs)。
- 從 servers發送聯網事件到 clients。
引擎和編輯器集成(Engine and Editor integration)
Unity網絡被集成到引擎和編輯器中,從而能讓你使用組件和可視化工具來構建多人游戲。它提供了:
- 用於聯網物體的 NetworkIdentity組件
- 用於聯網腳本的 NetworkBehaviour。
- 物體 transforms的可配置自動同步。
- 腳本變量的自動同步。
- 在 Unity場景中支持放置聯網物體。
- Network組件。
網絡傳輸實時傳輸層
Unity 實時傳輸層(Real-Time Transport Layer)提供了:
- 基於UDP協議的優化。
- 多通道設計以避免 隊首阻塞(head-of-line blocking)問題。
- 每個通道都支持各種級別的 Quality of Service (QoS)。
構建多玩家項目
接下來介紹構建多玩家項目的最基礎且常見的事項。項目中需要:
- Network Manager
- 用戶接口(玩家可以找到並加入游戲)
- 聯網玩家 Prefabs
- 多玩家感知的 腳本和 GameObjects
對於不同的游戲,這個列表會有一些變化。比如說,在多人象棋游戲,或實施策略游戲中,你不需要代表玩家的可視化 GameObject。然而,你可能始終需要一個不可見的空 GameObject來表示玩家,並將其與腳本關聯。
后面會簡要介紹上面列出的每個條目。你需要理解以下兩點重要概念並在構建游戲時做出適當的選擇: - client、server和 host的關系。
- GameObject和動作的 authority概念。(The idea of authority over GameObjects and actions)
Network Manager
Network Manager負責管理你的多玩家游戲的網絡方面的東西。在你的 Scene中同時應當只有一個激活的 Network Manager。
Unity內置的 Network Manager組件集中了所有用於管理多玩家游戲的特性。如果你有特殊需求,可以自己寫一個專屬 Network Manager,否則還是老老實實用這個組件。
用戶接口
幾乎每款多玩家游戲都要提供用於玩家發現,創建和加入游戲 “instances”(或“matches”)的方法。游戲的這部分通常被稱為 “大廳(lobby)”,且有時還能在里面交談。
Unity有一個最為基礎的內置接口:NetworkManagerHUD。他在你構建游戲的早期極為有用,它能讓你簡單地構建matches並測試游戲。然而,由於它功能性和可視化設計過於基礎,你應該在完成project前替換它。
聯機玩家 GameObjects
大多數多玩家游戲都有玩家可以操縱的對象,比如說角色,汽車等等。一些多人游戲沒有可見的“玩家對象”,取而代之地,玩家能控制許多單位或物品,實時戰略游戲就是這樣。有些甚至就沒有特定的對象,比如說畫布繪畫游戲。在這些場景中,你通常需要創建一個 GameObject來表示玩家。將此 GameObject設置為 Prefab,並關聯上所有控制玩家能做的操作的腳本。
如果你使用的是 Network Manager組件,將此 Prefab分配給 Player Prefab域。
當游戲運行時,Network Manager為每個連接到此match的玩家創建一份你的玩家 Prefab拷貝(“instance)。
然而,多人游戲編程新手很容易陷入疑惑————你需要確認你的玩家 Prefab實例上的腳本 “意識到”,玩家是在使用 host(管理游戲的計算機)還是 client(與host不同的計算機)控制此實例。
多玩家意識腳本(Multiplayer-aware Scripts)
為多玩家游戲寫腳本與單玩家游戲是不同的。這是因為你需要考慮腳本運行的不同上下文。比如說,你添加在你的玩家 Prefab上的腳本應該允許該玩家實例的“owner”來操控它,而不是其他玩家。你需要考慮是否server或client有控制該腳本的權力(authority)。有時,你想讓腳本同時運行在 server和 clients上,有時,你又只想讓腳本運行在 server上,並且只希望 clients復制 GameObjects的移動方式。(比如說,在一個游戲中,玩家拾起可收集的 GameObject,腳本應該只在 server上運行,這樣 server就能成為已收集 GameObjects數量的官方權威。
取決於你腳本的作用,你應該決定你腳本的哪些部分在哪些情形是可用的。
對於玩家 GameObject,每個人通常都有控制它們玩家實例的權限。這意味着每個 client都自己的 local authority,且 server接收 client告訴它的信息,比如說玩家正在做什么。
對於非玩家 GameObjects,server通常都有 authority,來知曉它們發送了什么(物品是否已經被收集),所有 clients接收 server發布的,關於此 GameObject的信息。
使用 Network Manager
Network Manager有以下特性:
- 游戲狀態管理(Game state management)
- 產生管理(Spawn management)
- 場景管理(Scene management)
- Debug信息(Debugging information)
- Matchmaking
- 定制化(Customization)
開始使用 Network Manager
Network Manager是多人游戲地核心控制組件。首先先創建一個 empty GameObject,然后添加 NetworkManager組件:
注:你應該只有一個激活的 Network Manager。不要把 Network Manager組件放到一個聯網的 GameObject(有 Network Identity組件),因為 Unity會在場景加載時 disable它們。
因為 Network Manager是基於 High-level API (HLAPI)實現的,所以它能做的你也可以使用腳本來實現。對於高級開發者,如果需要擴展 Network Manager組件的特性,可以在腳本中繼承 NetworkManager類,並通過重載虛函數來自定義它的行為。
游戲狀態管理
網絡多人游戲有三種模式:client,專用server,或“Host”,“Host”即是client又是server。如果你正在使用 Network Manager HUD,它會自動告訴 Network Manager要以哪種模式開始,基於玩家選擇的選項。如果你用的是自己的UI,那么你需要在你的代碼中調用這些:
- NetworkManager.StartClient
- NetworkManager.StartServer
- NetworkManager.StartHost
網絡地址和端口設置
無論游戲以哪種模式(client,server,host)開始,網絡地址和端口屬性都會被使用。在client模式,游戲會試圖連接特定的地址和端口號。在server或host模式,游戲會監聽特定端口號的輸入連接。
在游戲開發過程中,設置一個固定的地址和端口號是很有用的。然而,你最后可能想要你的玩家能夠選擇他們想要連接的host。當你到達那個階段后,Network Discovery組件能被用於廣播並尋找局域網上的地址和端口號,且 Matchmaker服務能被用於為玩家尋找網絡上可以連接的matches。
產生管理
使用 Network Manager來管理聯網 GameObject的產生(聯網實例化)。
大多數游戲都有代表玩家的 Prefab,因此 Network Manager有一個 Player Prefab槽。你應該將你的玩家 Prefab分配到此槽中。當你有了一個玩家Prefab設置,玩家 GameObject能為每個玩家使用該Prefab自動自動產生。你必須為 Player Prefab關聯一個 Network Identity組件。
一旦你分配了一個玩家 Prefab,你可以以host身份開始游戲並看到玩家 GameObject產生。停止游戲會摧毀玩家 GameObject。如果你以client的身份運行游戲的另一份副本,並連接到localhost,Network Manager會產生另一個玩家 GameObect。
除了玩家 Prefab,你必須在 Network Manager為你想要動態產生的物體注冊 Prefabs。你可以添加 Prefabs到Inspector中的 Registered Spawnable Prefabs標簽中的列表。你也可以使用代碼來注冊,通過ClientScene.RegisterPrefab()方法。如果每個場景中都有單獨的 Network Manager,你只需要注冊Prefabs到與該場景相關的Network Manager中。
自定義玩家實例
Network Manager使用 NetworkManager.OnServerAddPlayer()的實現來產生玩家 GameObject。如果你想要自定義玩家 GameObject產生的方式,你可以重載此虛函數。它的默認實現如下:
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}
注:如果你要實現自定義版本的 OnServerAddPlayer,NetworkServer.AddPlayerForConnection()方法必須唄新產生的玩家 GameObject調用,這樣它就能被產生,並與client連接相關聯。AddPlayerForConnection產生 GameObject,所以你不需要使用 NetworkServer.Spawn()。
起始位置
要控制玩家產生的位置,你可以使用 Network Start Position組件。要使用它,先關聯 Network Start Position組件到場景中的一個 GameObect,並把此 GameObject放置到你希望玩家開始的地方。你可以在場景中添加許多起始點。Network Manager會檢測場景中的起始位置,並在每個玩家實例產生時,它會使用其中一個位置和方向。
Network Manager有一個 Player Spawn Method屬性:
- Random:隨機產生
- Round Robin:循環產生
如果 Random和 Round Robin模式無法滿足你,你也通過代碼可以自定義起始位置是如何被選取的。你可以通過 NetworkManager.startPositions訪問可用的 Network Start Position組件,並在 OnserverAddPlayer的實現中使用 GetStartPosition()方法,來找到一個起始位置。
場景管理
大多數游戲都不止一個場景。至少,除了游戲場景外,每個游戲都有標題場景和起始菜單場景。Network Manager被設計於可以自動管理游戲場景狀態和場景切換。
NetworkManager Inspector中有兩個槽:Offline Scene和 Online Scene。拖動場景assets到這些槽里來激活聯網場景管理。
當server或host啟動后,Online Scene會被加載。這會成為當前的網絡場景,任何連接到該server的clients都會加載此場景。場景名字存儲在 networkSceneName屬性中。當網絡中斷后(停止server/host或client斷開連接),Offline Scene會被加載。這能當多人游戲斷開連接后,游戲能自動返回菜單場景。
你也可以在游戲運行時調用 NetworkManager.ServerChangeScene()來切換場景。這也可以令所有連接的clients切換場景,並更新 networkSceneName。當聯網場景管理處於激活狀態時,任何調用游戲狀態管理函數,如NetworkManager.StartHost(), NetworkManager.StopClient()都會導致場景變化。通過設置場景並調用這些方法,你可以控制多人游戲的flow。
注意場景切換會導致前一個場景中的所有 GameObjects被摧毀。
你應該確保 NetworkManager在場景中始終存在,否則網絡連接會在場景變化時斷開。要做到這個,確保 Dont't Destroy On Load應該被選中。
定制化
NetworkManager類有許多虛函數,你可以在你的繼承自 NetworkManager的類中自定義行為。在實現這些函數時,確保注意這些默認實現提供的功能性。比如說,在OnServerAddPlayer()中,NetworkServer.AddPlayer函數必須被調用,以激活連接中的玩家 GameObject。
使用 Network Manager HUD
你應該能夠理解基礎的網絡概念,如host,server和client之間的關系。見 Network System Concepts。
Network Manager HUD組件如下所示:
屬性 | 功能 |
---|---|
Show Runtime GUI | 點擊此 checkbox來在運行時顯示 Network Manager HUD GUI。這能讓你在快速debug時顯示或隱藏它。 |
GUI Horizontal Offset | 設置HUD的水平像素偏移,從屏幕左邊緣開始測量。 |
GUI Vertical Offset | 設置HUD的垂直像素偏移,從屏幕頂部開始測量。 |
Network Manager HUD提供了基礎功能,讓玩你游戲的人能夠掌管一個聯網游戲,或找到並加入到已存在的聯網游戲中。
使用 HUD
Network Manager HUD有兩個基礎模式:LAN(局域網)和 Matchmaker模式。LAN模式用於創建並加入局域網托管的游戲。Matchmaker模式用於通過互聯網創建並尋找加入到游戲中。
Network Manager HUD開始於 LAN模式。要切換到 Matchmaker模式,點擊 Enable Match Maker (M)按鈕。
LAN HOST
點擊 LAN Host按鈕來以host身份在局域網上開始游戲。這個 client既是host又是游戲內的一個玩家。它使用來自 Network Info的信息來掌管游戲。
當你點擊此按鈕后, HUD切換到一個簡單的網絡細節展示。Stop(X)按鈕能讓你停止掌管游戲並返回到主 LAN菜單。當你以 host身份開始游戲時,其他玩家才能連接到此游戲。點擊 Stop(X)后,連接到host的所有玩家都會斷開連接。
LAN Client
要連接到局域網上的host,使用文本域來指定host地址。默認host地址為 “localhost”,這意味着 client會查看本機上的游戲。點擊 LAN Client (C)來連接到你指定的host地址。
當你想要在處於同一個網絡的多台機器上測試你的游戲時,你需要將host機器的地址填入文本框中。扮演host的機器需要告訴所有人他的IP地址。輸入IP地址后點擊 LAN Client來連接到host。當client連接時,HUD會展示一個 Cancel Connection Attempt按鈕,你可以點擊它來停止嘗試連接。
如果連接成功,HUD會顯示 Stop(X)按鈕。點擊它可以停止游戲並斷開與host的連接:
LAN Server Only
點擊 LAN Server Only來以 server身份開始游戲,但是不具有client身份。這類游戲稱為 “專用服務器(dedicated server)”。用戶不能在這個特殊實例上玩游戲。所有玩家必須以client身份連接。
局域網的專用服務器對於所有連接玩家來說會有更好的性能,因為服務器除了擔任服務器之外,不需要處理本地玩家的 gameplay。
如果你想要 host一個能在互聯網上玩的游戲,但是希望自己維護對服務器的控制,你可能會選擇此選項。比如說,防止client作弊,因為只有 server有游戲的權限。
Enable Match Maker
點擊 Enable Match Maker (M)來切換 HUD到 Matchmaker模式。如果你想要創建或連接互聯網游戲上,你需要使用 Matchmaker模式。(這個模式具體參數以后再來填坑)
注:記住 Network Manager HUD 特性是開發時的臨時幫助。它能讓你快速運行多人游戲,但是在你准備好后,你應該把它替換成自己的UI控件。