UNet官方文檔:https://docs.unity3d.com/Manual/UNet.html
1.建立Offline場景
建立一個新場景,命名為Offline,在場景中建立一個空GameObject,添加NetworkManager、NetworkManagerHUD兩個元件
Network Manager:
網絡管理器功能包括:
-
- Game state management(遊戲狀态管理,NetworkManager中有大量Hook,例如OnServerConnect服務端啟動時自動調用,OnClientConnect用戶端連接配接時自動調用)
- Spawning management(同步産生遊戲對象,例如有玩家進入遊戲,就需要Server端通知所有已連接配接Clients同步建立出這個玩家)
- Scene management(場景管理,開始多人遊戲時進入聯網場景(Play Scene),斷開連接配接時切換到離線場景(Offline Scene))
- Debugging information(調試資訊)
- Matchmaking(這是Unity Cloud提供的服務,用于遊戲房間的建立、比對、加入、銷毀等)
- Customization(NetworkManager雖然封裝了好了功能,但通常需要自己寫NetworkManager的擴充類以滿足自己遊戲的需求,例如Unity還有一個NetworkLobbyManager,也是繼承NetworkManager,但是擴充出一個遊戲大廳的功能)
Network Manager HUD:
這是用來配合NetworkManager的元件,隻是一個功能展示UI,控制NetworkManager的基本功能。如StartHost()啟動為主機,StartClient()啟動為用戶端等,一般隻有測試的時候才用這個元件
2.建立Online場景
1.搭建UI
建立場景,命名為Game,建立Scroll View作為聊天資訊顯示區域,建立InputField作為消息輸入框,建立一個發送Button。
2.制作消息框
1.建立一個Panel,添加VerticalLayoutGroup、ContentSizeFitter元件,并做一下設定,用于根據文字多少消息框自适應大小
2.在Panel下建立一個Text,用于顯示使用者發送的文字消息
3.儲存成Prefab,用于在代碼中動态建立消息框
3.制作玩家Prefab
1.建立空GameObject,添加NetworkIdentify元件,并勾選Local Player Authority,隻允許本地用戶端控制。NetworkIdentify是網絡物體必須要有的元件,用于網絡身份識别
2.建立腳本命名為Speaker,用于将用戶端輸入的消息發送給服務端,然後由服務端發給所有用戶端以實作消息同步.
代碼如下:
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class Speaker : NetworkBehaviour
{
public GameObject itemPrefab;
private Transform content;
private InputField inputBox;
private Button sendButton;
//[SyncVar(hook = "OnValueChanged")]
[SyncVar]
private int onlineNum = 0;
void Start()
{
content = GameObject.Find("Canvas/Scroll View/Viewport/Content").transform;
inputBox = GameObject.Find("Canvas/InputField").GetComponent<InputField>();
sendButton = GameObject.Find("Canvas/SendButton").GetComponent<Button>();
sendButton.onClick.AddListener(SendButtonCallback);
}
/// <summary>
/// 顯示線上人數
/// </summary>
private void OnGUI()
{
if (!isLocalPlayer)
return;
GUI.Label(new Rect(new Vector2(10, 10), new Vector2(150, 50)),
string.Format("線上人數:{0}", onlineNum));
}
/// <summary>
/// 更新Serve端線上人數
/// </summary>
private void Update()
{
if(isServer)
onlineNum = NetworkManager.singleton.numPlayers;
}
/// <summary>
/// 發送按鈕響應事件
/// 将使用者輸入消息發送給服務端
/// </summary>
void SendButtonCallback()
{
if (!isLocalPlayer)
return;
if (inputBox.text.Length > 0)
{
string str = string.Format("{0}:{1}{2}", Network.player.ipAddress, System.Environment.NewLine, inputBox.text);
CmdSend(str);
inputBox.text = string.Empty;
}
}
/// <summary>
/// 使用Command修飾的函數表示在用戶端調用,在服務端執行
/// </summary>
/// <param name="str"></param>
[Command]
void CmdSend(string str)
{
RpcShowMessage(str);
}
/// <summary>
/// ClientRpc修飾的函數 表示由服務端調用,在所有用戶端執行
/// </summary>
/// <param name="str"></param>
[ClientRpc]
void RpcShowMessage(string str)
{
GameObject item = Instantiate(itemPrefab, content);
item.GetComponentInChildren<Text>().text = str;
}
}
代碼非常簡單,沒有寫發包收包卻實作了用戶端和服務端的消息同步,這就是UNet的強大之處,UNet的口号是讓網絡遊戲開發就像開發單機遊戲一樣,最重要的是UNet Server端與用戶端運作的是同樣的代碼,也就是說Server端有Unity所有的功能(實體引擎、網格導航等),這些資訊直接在Server端計算出來同步給Clients,而用其它網絡引擎就需要在Server端自己寫碰撞和導航系統。不過UNet也有很多不足,例如Server和Client執行的是同樣的代碼,一旦有用戶端被反編譯那麼Server端的邏輯也就暴露了。
代碼中有幾個UNet重要的關鍵字:
自動同步:
[SyncVar]或[SyncVar(hook="FuncName")]: 用于自動同步Server端變量,當Server端SyncVar修飾的變量發生改變時就會自動同步給所有Clients, 第二種寫法是當此變量改變時會自動調用FuncName函數,這種同步方法通常用于變化比較頻繁的變量。
手動同步:
[Command]:函數名必須以Cmd開頭,Command修飾的函數表示在用戶端調用,在服務端執行。如代碼中在用戶端調用CmdSend(string message), 把目前用戶端使用者輸入消息作為參數傳進去,該函數在用戶端調用時傳入了消息,然後此函數會在服務端執行,這樣服務端就得到了用戶端傳遞進來的消息。
[ClientRpc]:函數名必須以Rpc開頭,ClientRpc修飾的函數 表示由服務端調用,在所有用戶端執行。服務端執行了CmdSend()方法,而此方法又調用了RpcShowMessage(string str),把從用戶端傳來的消息又直接傳入了RpcShowMessage(), 而[ClientRpc]修飾的函數是服務端調用用戶端執行,服務端又将從用戶端收到的消息轉發給了所有用戶端,這樣就實作了消息的同步。
4.設定NetworkManager
如圖,把Offline和Game場景以及Speaker預制體拖入
5.運作,測試
想要使用Unity Cloud的線上遊戲房間比對系統需要在Unity開啟MultiPlayer服務
Build項目,然後運作兩個,一個啟動為Host,一個啟動為Client,啟動後NetworkManager會自動切換至Online場景:
測試圖: