unity網絡編程學習

前言

最近在學習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


小練習
首先搭建一個場景,人物,在人物身上掛上Network,人物控制腳本等。
創建服務器(同時也是客戶端),將腳本掛在主攝像機上
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();
			
		}
		
		
	}
	
}
設置一個人物生成點,掛上 人物創建腳本,當創建服務器或者連接服務器成功時,生成一個遊戲人物。可以在地面上通過鼠標點擊進行行走,當客戶端斷開連接時,銷燬對應的遊戲對象。這裏要注意的幾點:
1 unity的網絡機制是在聯網時,在各個機器上創建遊戲對象實例,所以要保證照相機發出的射線只能控制當前遊戲對象,不能控制其他機器的遊戲對象,我的做法是將當前player和network.player(本地實例)比較,相同說明就是當前角色,可以進行射線操作
	//服務端新建完成後運行
	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);	
    }

最後大概實現了,創建服務器,生成玩家,連接服務器,生成玩家,點擊地面對玩家進行操作,玩家位置相互同步。





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