前言
最近在學習unity自身封裝的網絡功能,在遊戲物體上掛上Network組件,使其成爲網絡中的一份子,就像這裏所說的,Unity網絡協議本身是一個比較高層的網絡協議,他並不是獨立存在的,而是與引擎的遊戲對象結合在一起,所以在Unity中不存在單純的“發送給服務器”或“發送給客戶端的”網絡消息,不同的遊戲對象之間的通信是獨立的,就好像每個遊戲對象都有一個獨立的通信管道。
在遊戲中,對象經常發生變化,需要進行狀態同步,狀態同步的方向只能是isMine到非isMine。比如一個玩家角色對象hp屬性,如果需要其他玩家能夠看到,就可以使用狀態同步來完成。當isMine的玩家角色的hp變化後,會自動更新這個屬性給其他的主機上的同一個玩家角色對象。狀態同步還支持保證和非保證模式。
要進行狀態同步就需要RPC,在其他機子上對同一遊戲物體進行一個遠程函數調用,RPC一般用來通過網絡通知一次性的遊戲事件,RPC的傳輸方向非常自由,可以在一個主機向任何其他主機,包括自己,發送RPC調用。
在unity網絡中用到的相關類和方法
RPC 可以傳遞的參數
int
float
string
NetworkPlayer
NetworkViewID
Vector3
Quaternion
BitStream可以傳遞的參數
void Serialize (bool value)
void Serialize (char value) - only one byte [0, 255]
void Serialize (short value)
void Serialize (int value)
void Serialize (float value, float maxDelta = 0.00001f);
void Serialize (Quaternion value, float maxDelta = 0.00001f);
void Serialize (Vector3 value, float maxDelta = 0.00001f);
void Serialize (NetworkPlayer value)
void Serialize (NetworkViewID viewID)
RPCMode:
Server - only server
Others - send to everyone except the sender
OthersBuffered - Send to everyone except sender and adds to the buffer
All - send to everyone
AllBuffered - Sends to everyone and adds to the buffer
Network:
Network.AllocateViewID - 自動生成一個可用的viewid
Network.CloseConnection - 關閉當前連接
Network.Connect - 連接到指定端口或者域名
Network.connections -當前網絡的連接數
Network.connectionTesterIP - 測試連接者的IP連接到服務器
Network.connectionTesterPort - 測試連接者的端口到服務器 (Debug)
Network.Destroy - 銷燬跟當前viewID有關的網絡連接
Network.DestroyPlayerObjects - 銷燬跟當前Viewid有關的玩家物體
Network.Disconnect - 關閉所有已經連接好的連接,關閉網絡網絡接口
Network.GetAveragePing - ping 指定連接的玩家
Network.GetLastPing - 獲取最後ping通的玩家
Network.HavePublicAddress - 檢查當前機器是否有一個公共IP地址
Network.incomingPassword - 設置服務器的連接密碼
Network.InitializeSecurity - 設置服務器加密,自動加密處理
Network.InitializeServer - 初始化服務器
Network.Instantiate - 實例化一個已經存在的prefab
Network.isClient - 判斷當前的運行是否爲客戶端
Network.isMessageQueueRunning - 是否取消RPC訪問或者取消網絡數據同步
Network.isServer - 判斷當前的運行是否爲服務器
Network.logLevel - 設置當前網絡信息的日誌信息等級(sys)
Network.macConnections - 設置服務器可以連接的最大數或者遊戲玩家數目
如果設置成0表示只有已經一個連接存在,設置成-1表示最大的連接數與當前
連接數相等,如果是這樣,如果玩家失去連接,則服務器的位置將會爲玩家
繼續保留下來
Network.mininumAllocatableViewIDs - 設置最小從服務器獲取的viewid數量*
Network.natFacilitatorIP - 使用網絡穿透的IP地址
Network.natFacilitatorPort - 設置網絡穿透的端口號
Network.OnConnectedToServer - [client]當連接服務器成功時調用
Network.OnDisconnectedFromServer - [client&svr]當服務器斷開連接時調用
Network.OnFailedToConnect - [client]當客戶端連接失敗時
Network.OnNetworkInstantiate - [client|svr]當網絡實例化一個對象調用
Network.OnPlayerConnected - [svr]當有一個新的玩家連接到服務器時
Network.OnPlayerDisconnected - [svr]當有玩家離開遊戲時
Network.OnSerializeNetworkView - [client|svr]通過network view同步變量*
[void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
Network.OnServerInitialized - [svr]當服務器初始化成功時
Network.peerType - 網絡對等狀態
Network.player - 得到當前的NetworkPlayer實例引用
Network.proxyIP - 代理服務器的IP地址
Network.proxyPassword - 代理服務器的密碼
Network.proxyPort - 代理服務器的端口號
Network.RemoveRPCs - 從服務器那裏移除所有跟當前玩家相關的RPC方法
Network.RemoveRPCsInGroup - 從服務器那裏移除跟當前組有關RPC方法
Network.sendRate - 網絡同步的發送頻率
Network.SetLevelPrefix - 設置網絡前輟,將應用到所有viewid
Network.SetReceivingEnabled - 設置指定玩家的分組信息是否可以接收
Network.SetSendingEnable - 設置指定玩家的分組信息是否可以發送
Network.TestConnection - 測試當前主機的連接狀態*
Network.TestConnectionNAT - 強制測試當前主機的連接狀態
Network.time - 得到當前網絡的時間,單位秒
Network.useProxy - 使用代理服務器
NetworkView:
NetworkView.Find - 通過netview id來查找一個NetworkView
NetworkView.group - 當前NetworkView所在的組
NetworkView.isMine - 當前物體是否有NetworkView控制功能
NetworkView.observed - 當前NetworkView是否在觀察中
NetworkView.owner - 當前NetworkView是歸誰所擁有
NetworkView.RPC - 調用當前所有已經連接了服務器的RPC方法
NetworkView.SetScope - 設置NetworkView的關聯NetworkPlayer信息
NetworkView.stateSynchronization - 網絡數據同步發送安全性*
NetworkView.viewID - 當前NetworkView的NetworkViewID
NetworkViewID:
NetworkViewID.isMine - 是否是自己實例化的物體
NetworkViewID.operator!= - 如果兩個NetworkView不相等
NetworkViewID.operator== - 如果兩個NetworkView相等
NetworkViewID.owner(NetworkView) - 哪個NetworkPlayer擁有的NetworkViewID
NetworkViewID.ToString - 返回已經格式化好的NetworkViewID信息
NetworkViewID.unassigned - 表示當前networkview id無效
NetworkPlayer:
NetworkPlayer.externalIP - 獲取當前NetworkPlayer對外開放的IP地址
NetworkPlayer.externalPort - 獲取當前NetworkPlayer對開開放的端口號
NetworkPlayer.guid - 當前NetworkPlayer的GUID,當使用NAT punchthrough時
NetworkPlayer.ipAddress - 當前NetworkPlayer的IP地址
NetworkPlayer.port - 當前NetworkPlayer的端口號
NetworkPlayer.operator!= - 是否兩個NetworkPlayer不相等
NetworkPlayer.operator== - 是否兩個NetworkPlayer相等
NetworkPlayer.ToString - 返回當前NetworkPlayer的索引
HostData:
HostData.comment - 主機描述信息
HostData.connectedPlayers - 當前的玩家連接數
HostData.gameName - 當前主機對應的遊戲名稱
HostData.gameType - 當前主機對應的遊戲類型
HostData.guid - NAT punchthrough時
HostData.ip - 主機的IP地址
HostData.passwordProtected - 主機是否有密碼保護
HostData.playerLimit - 主機最大能承受的玩家數量
HostData.port - 當前主機的端口號
HostData.useNat - 當前主機是否已經有Nat穿透功能
MasterServer:
MasterServer.ClearHostList - 清除通過從MasterServer.PollHostList獲取的主機列表
MasterServer.dedicatedServer - 當前機器是否有專有服務器
MasterServer.ipAddress - 主服務器的IP地址
MasterServer.OnFailedToConnectToMasterServer - [svr|client]當連接主服務器失敗時回調
MasterServer.OnMasterServerEvent - 當主服務器有事件通知[MasterServerEvent]
MasterServer.PollHostList - 通過MasterServer.RequestHostList檢查最新的主機列表
MasterServer.port - 主服務器的端口號
MasterServer.RegisterHost - 註冊當前服務器到主服務器
MasterServer.RequestHostList - 人主服務器那裏請求主機列表
MasterServer.UnregisterHost - 從主服務器那裏取消註冊當前服務器
MasterServer.updateRate - 設置主服務器的最小更新頻率,如果爲0,則主服務器不進行更新,
默認更新頻率爲60s
using UnityEngine;
using System.Collections;
public class AuthoritativeNetworkServer : MonoBehaviour {
private string ip="127.0.0.1";
private int port=10001;
void OnGUI(){
switch(Network.peerType){
case NetworkPeerType.Disconnected:
StartCreat();
break;
case NetworkPeerType.Server:
OnServer();
break;
case NetworkPeerType.Client:
OnClient();
break;
case NetworkPeerType.Connecting:
GUILayout.Label("連接中···");
break;
}
}
void StartCreat(){
GUILayout.BeginVertical();
if(GUILayout.Button("新建服務器")){
NetworkConnectionError error=Network.InitializeServer(30,port);
Debug.Log (error);
}
if(GUILayout.Button("連接服務器")){
NetworkConnectionError error=Network.Connect(ip,port);
Debug.Log(error);
}
GUILayout.EndVertical();
}
void OnServer(){
GUILayout.Label(" 服務端創建成功,請等待連接");
int length=Network.connections.Length;
for(int i=0;i<length;i++){
GUILayout.Label("客戶端連接ip:"+Network.connections[i].ipAddress);
GUILayout.Label("客戶端連接端口:"+Network.connections[i].port);
}
if(GUILayout.Button("斷開服務器")){
Network.Disconnect();
}
}
void OnClient(){
GUILayout.Label("連接成功");
if(GUILayout.Button("斷開連接")){
Network.Disconnect();
}
}
}
//服務端新建完成後運行
void OnServerInitialized(){
MovePlayer(Network.player);
}
//玩家連接後運行
void OnPlayerConnected(NetworkPlayer player){
MovePlayer(player);
}
void MovePlayer(NetworkPlayer player){
//獲取玩家id值
int playerID=int.Parse(player.ToString());
//初始化目標對象
Transform playerTrans=(Transform)Network.Instantiate(playerPrefab,transform.position, transform.rotation, playerID);
NetworkView playerObjNetworkview=playerTrans.networkView;
//添加HeroController組件至集合中 用於斷開連接時刪除對象
list.Add(playerTrans.GetComponent("HeroController"));
//調用RPC,函數名爲SetPlayer
playerObjNetworkview.RPC("SetPlayer",RPCMode.AllBuffered,player);
}
看看setPlayer方法寫了什麼: //調用客戶端(這裏包括服務端)的SetPlayer函數
[RPC]
void SetPlayer(NetworkPlayer player)
{
ownerPlayer = Network.player;
if (player == Network.player)
{
//當前的客戶端能對其操作,否則不能
canDo = true;
}
}
2 角色的狀態需要同步,比如人物的座標,要用到onSerializeNetworkView方法,用來在一個由網絡視圖監控的腳本中自定義變量同步。它自動決定被序列化的變量是否應該發送或接收,取決於誰擁有該物體,即所有者發送,其他所有人接收。 void OnSerializeNetworkView(BitStream stream, NetworkMessageInfo info)
{
Vector3 pos = Vector3.zero;
if (stream.isWriting)
{
pos = this.transform.position;
stream.Serialize(ref pos);
}
else
{
stream.Serialize(ref pos);
this.transform.position = pos;
}
}
這樣人物的位置信息就會傳達到各個客戶端了,但是動畫等狀態不能通過這個方法傳遞,仍需要調用RPC函數 //玩家斷開連接時調用
void OnPlayerDisconnected(NetworkPlayer player) {
//遍歷集合對象上的PlayerControl組件
foreach(PlayerControl script in list){
if(player==script.ownerPlayer){
//移除所有屬於這個id的rpc函數
Network.RemoveRPCs(script.gameObject.networkView.viewID);
//銷燬物體
Network.Destroy(script.gameObject);
list.Remove(script);
break;
}
}
int playerNumber= int.Parse(player+"");
Network.RemoveRPCs(Network.player, playerNumber);
Network.RemoveRPCs(player);
Network.DestroyPlayerObjects(player);
}
//客戶端斷開連接時調用
void OnDisconnectedFromServer(NetworkDisconnection info) {
Application.LoadLevel(Application.loadedLevel);
}
最後大概實現了,創建服務器,生成玩家,連接服務器,生成玩家,點擊地面對玩家進行操作,玩家位置相互同步。