1. 程式人生 > >UNITY多人遊戲基礎

UNITY多人遊戲基礎

1.總覽。

多人遊戲基本結構:Clent/Server,分為Authoritative Server和Non-Authoritative Server兩種,前者客戶端傳送訊息,伺服器端反饋結果,好處是有效防止客戶端作弊,並統一不同客戶端之間的物理表現和互動狀況,缺陷是存在網路延時,很有可能每發出一個命令要過一段時間才能接收到反饋。解決方法是client-side prediction客戶端預測,客戶端預測伺服器的反饋,但不能做出關鍵性事件的預測,比如某個怪物的死亡。關於client-side prediction技術可以到網上查閱相關資料,這裡不贅述。

Non-Authoritative Sever有客戶端自己處理使用者輸入和物件邏輯,不需要要client-side prediction技術。這種情況伺服器端要處理的資料量相對比較小。

網路通訊的方法:Remote Procedure Calls(RPC,遠端程序呼叫)和State Synchronization(狀態同步)。

NAT punchthrough:網路連線可能是複雜的,比如中間要通過一個帶公共IP的NAT路由器,怎樣把NAT的內部私有IP和外部計算機連線?可以利用一個叫做Facilitator的伺服器和private IP接觸並獲知其所用的IP地址和埠號。另外,內部防火牆玩家可以更改設定,外部防火牆也可以使用這種NAT punchthrough技術穿透。

Minimizing Network Bandwidth:最小化網路開支,只傳輸絕對重要的資料,比如客戶端完全可以獨立處理角色模型而不需要伺服器傳送資料來同步。另外記住,你可以使用一個自帶關卡標示的RPC呼叫來讓所有客戶端同時裝載這個關卡。

2.基礎函式。

建立伺服器:Network.InitializeServer().

函式原型:static function InitializeServer (connections : int, listenPort : int, useNat : boolean) : NetworkConnectionError
其中connections是允許連線數,useNat是否允許NAT punchthrough連線。

一個初始化例子:

public class example : MonoBehaviour {
    void LaunchServer() {
        Network.incomingPassword = "HolyMoly";
        bool useNat = !Network.HavePublicAddress();
        Network.InitializeServer(32, 25000, useNat);
    }
}

其中Network.HavePublicAddress()檢視是否擁有IPV4公共IP.這是簡單那的測試方法,另外一個更值得研究的方法是Network.TestConnction().

建立連線到伺服器:Network.Connect().
熟悉一下Network類總是有好處。

使用Network View元件進行通訊,這一點很重要,它讓你可以使用RPC呼叫和State Synchronization.每個元件有一個唯一的NetworkViewID,每個資料包都對應傳送到一個由NetworkViewID指定的Network View所連結的物件上。當使用Network.Instantiate()建立Network物件時,會自動分配Network View,不用開發者操心。當然你也通過RPG呼叫在每個客戶機上例項化一個Network View物件,然後使用Network.AllocateViewID()函式手動分配NetworkViewID。

當你向一個物件新增NetworkView元件,並指定狀態同步,需要選擇NetworkView所監視的元件,這可以是該物件的transform,animation,script,或是rigidbody.如果不使用狀態同步而只是用PRC呼叫,則state Synchronization設為off即可,只要有一個Network View(不論是否開啟state Synchronization)你都可以使用RPC.

設定state Synchronization為Reliable Delta Compression,為可靠傳輸,資料按序傳送接收。

同步監視物件是script時,需要使用OnSerializeNetworkView顯式化序列資料:

function OnSerializeNetworkView (stream : BitStream, info : NetworkMessageInfo) {
     var horizontalInput : float = Input.GetAxis ("Horizontal");
     stream.Serialize (horizontalInput);
}

此程式碼當你本地更新或收到stream的更改時總是寫horizontalInput的值。有時候你在本地更新和收到更新時,需要做不同的事情,此時敲入如下程式碼:

function OnSerializeNetworkView (stream : BitStream, info : NetworkMessageInfo) {
     var horizontalInput : float = 0.0;
     if (stream.isWriting) {
          // Sending
          horizontalInput = Input.GetAxis ("Horizontal");
          stream.Serialize (horizontalInput);
     } else {
          // Receiving
          stream.Serialize (horizontalInput);
          // ... do something meaningful with the received variable
     }
}


在C#Script中Serialize()的引數是ref型別,在引數horizontalInput的前面加上引用符號ref即可。

OnSerializeNetworkView的呼叫頻率在Network Manager的SendRate裡設定,預設是15/s.

3.RPC和狀態同步。

如果要呼叫RPC,所呼叫函式必須帶有@RPC屬性,如下:

var playerBullet : GameObject;
function Update () {
     if (Input.GetButtonDown ("Fire1")) {
          networkView.RPC ("PlayerFire", RPCMode.All);
     }
}
@RPC
function PlayerFire () {
     Instantiate (playerBullet, playerBullet.transform.position, playerBullet.transform.rotation);
}


RPC呼叫是可靠的有序的通訊。

以下是使用Network.AllocateViewID()手動分配ViewID的例項,其中使用RPC呼叫例項化cubePrefab的遠端呼叫,ViewID作為該遠端呼叫的引數傳遞:

public class example : MonoBehaviour {
    public Transform cubePrefab;
    void OnGUI() {
        if (GUILayout.Button("SpawnBox")) {
            NetworkViewID viewID = Network.AllocateViewID();
            networkView.RPC("SpawnBox", RPCMode.AllBuffered, viewID, transform.position);
        }
    }
[RPC]
    void SpawnBox(NetworkViewID viewID, Vector3 location) {
        Transform clone;
        clone = Instantiate(cubePrefab, location, Quaternion.identity) as Transform as Transform;
        NetworkView nView;
        nView = clone.GetComponent();
        nView.viewID = viewID;
    }
}

那麼,RPC(Remote Procedure Call)的規則和定義到底是什麼?
RPC是某物件的指令碼元件中包含的宣告為[RPC]的(JS為@RPC)函式,這個物件必須包含一個Network View元件,並且監視物件是該指令碼元件。這樣,其他指令碼就可以從遠端通過這個物件呼叫該RPC.

RPC函式的引數個數任意,但是為了減少網路消耗提高網路效能,個數最好優化到最少。
RPC呼叫(Network.RPC())的引數還應加上RPC函式名稱(string)和RPCMode(接收物件,包括All,Allbuffered,Others,Server等等),接下來才是RPC函式的引數。

RPC呼叫可以用來在所有客戶端上執行某個事件,或者在兩個特定的部分之間傳遞事件資訊,你也可以創造性的發揮。比如,一個遊戲伺服器只有當4個客戶端連線上才開始遊戲,當第四個客戶端連上以後,伺服器可以向所有客戶端同時傳送RPC從而開始遊戲。一個客戶端可以向所有客戶端傳送RPC告知他拿到了某個物品。伺服器可以在客戶端連線時傳送RPC讓客戶端執行初始化,比如給它一個player number,出生地點,隊伍顏色,等等。客戶端可以傳送RPC定製它的啟動設定,比如顏色偏好,已購物品等等。

NetworkMessageInfo結構體中包含timestamp時間戳和sender傳送者兩個資料成員。在RPC函式的引數列表中加入一項NetworkMessageInfo,可自動獲取該資訊,而在RPC呼叫中不用顯示列出該引數。

@RPC
function PrintText (text : String, info : NetworkMessageInfo)
{
    Debug.Log(text + " from " + info.sender);
}


RPC Buffer:使用buffer模式可以讓你之前傳送的RPC被新連線的客戶端接受。比如向當前玩家傳送裝載某個關卡的RPC,過段時間新玩家連線到遊戲,需要裝載當前其他玩家正在進行的關卡。這時新連線的玩家不會錯過裝載該關卡的RPC,因為它被快取了起來。另外還可以重新整理buffer,比如開始新的關卡後新連線的玩家不如從之前的關卡開始一個一個裝載。

State Synchronization的一些細節:
如前所述,狀態同步的物件只能是以下四種類型之一:transform,animation,rigidbody和monobehaviour(也就是script),其中script的需要用onSerializeNetworkView()函式顯示指定監視的變數資料。BitStream.Serialize()可接受的引數可以是bool,char,short,int,float,Quaternion,Vector3和NetworkPlayer.詳情參考官方script手冊中的BitStream.Serialize().

關於狀態同步兩種方式的選擇:

Reliable Delta Compressed:在屬性級別自動對比當前狀態(比如position和rotation是transform的兩個不同屬性,如果其中之一發生變動,只發送該變動資料),如果無變動則不傳送。Unity會利用ACK和NACK來保證UDP包的傳送可靠性,丟失的包會等待重傳(是的,Unity傳輸層使用的是UDP協議)。缺陷是網路不好會導致明顯延時。

Unreliable:不保證可靠性,因此它不是僅僅傳送更改的部分資料保留未更改的部分,而是每次更新都發送所有的資料,不管某部分改動還是未改動。這種模式用於遊戲每幀都在變動的情況,比如賽車遊戲。

綜上,遊戲變化頻繁且迅速,而丟一兩個包無關緊要,這時候選擇Unreliable,如果不是頻繁變化,為了節省頻寬,建議選擇Reliable Delta Compressed.

4.Network.Instantiate.

此方法在每個客戶機上例項化一個prefab,並且是buffered型RPC,這樣當一個新的客戶機連線到伺服器後,會自動例項化一個prefab.
函式原型:static function Instantiate (prefab : Object, position : Vector3, rotation : Quaternion, group : int) : Object
其中group(組)可以用來設定訊息的過濾,如果不需要這個功能則設定為0.

呼叫此RPC的例子:

public class example : MonoBehaviour {
    public Transform playerPrefab;
    void OnConnectedToServer() {
        Network.Instantiate(playerPrefab, transform.position, transform.rotation, 0);
    }
}

可以用Network.RemoveRPCs()函式來去掉buffer區的Network.Instantiate()函式。你可以去掉特定組的RPC(如果Instantiate指定了組的話),也可以去掉第一個NetworkView ID的RPC,因為當例項化發生的時候RPC訊息是與第一個Network View連線的。
函式原型:static function RemoveRPCs (playerID : NetworkPlayer, group : int) : void

從RPC buffer裡去掉Instantiation的例子:

public class example : MonoBehaviour {
    void OnNetworkInstantiate(NetworkMessageInfo info) {
        Debug.Log(networkView.viewID + " spawned");
        if (Network.isServer) {
            Network.RemoveRPCs(networkView.viewID);
            Network.Destroy(gameObject);
        }
    }
}

組的作用:讓特定(組)的客戶端只能收到特定的訊息。比如,如果兩個玩家分隔在物理上互不影響的兩個區域,但是可以進行聊天。這時候他們之間遊戲狀態資料的交換就很有限,但是要保留聊天功能。物理上的遊戲物件的例項化就應該和提供聊天功能的物件的例項化分開,設定為不同的組。

5.Master Server:

在Master Server中可以顯示所有的伺服器,這樣客戶端可以在這裡選擇適合的伺服器,另外Master Server隱藏埠和IP,解決連線問題,比如防火牆和NAT的punchthrough.
每個遊戲需要向Master Server提交一個Game Type(注意避免和別的遊戲重複),當客戶端選擇好Game Type,Master Server會提供一個包含遊戲人數、可能設定密碼的執行中游戲列表。
這兩個方法分別是:MasterServer.RegisterHost()(Host端)和MasterServer.RequestHostList()(客戶端).
函式原型:static function RegisterHost (gameTypeName : String, gameName : String, comment : String = “”) : void

RegisterHost的例子:

public class example : MonoBehaviour {
    void OnGUI() {
        if (GUILayout.Button("Start Server")) {
            bool useNat = !Network.HavePublicAddress();
            Network.InitializeServer(32, 25002, useNat);
            MasterServer.RegisterHost("MyUniqueGameType", "JohnDoes game", "l33t game for all");
        }
    }
}

函式原型:static function RequestHostList (gameTypeName : String) : void

RequestHostList的例子:

public class example : MonoBehaviour
{
    void Awake()
    {
        MasterServer.ClearHostList();
        MasterServer.RequestHostList("LarusTest");
    }
    void Update()
    {
        if (MasterServer.PollHostList().Length != 0)
        {
            HostData[] hostData = MasterServer.PollHostList();
            int i = 0;
            while (i < hostData.Length)
            {
                Debug.Log("Game name: " + hostData[i].gameName);
                i++;
            }
            MasterServer.ClearHostList();
        }
    }
}

上面的程式碼使用ClearHostList()清除掉由PollHostList()獲取的HostList,然後Request新的HostList,把新的HostList通過PollHostList()儲存在HostData陣列中,其中HostData是儲存HostList中資料的資料結構,其成員變數如下圖:

HostData結構的資料成員列表

以下是連線MasterServer

程序例項:

function Awake() {
     MasterServer.RequestHostList("MadBubbleSmashGame");
}
function OnGUI() {
     var data : HostData[] = MasterServer.PollHostList();
     // Go through all the hosts in the host list
     for (var element in data)
     {
          GUILayout.BeginHorizontal();
          var name = element.gameName + " " + element.connectedPlayers + " / " + element.playerLimit;
          GUILayout.Label(name);
          GUILayout.Space(5);
          var hostInfo;
          hostInfo = "[";
          for (var host in element.ip)
               hostInfo = hostInfo + host + ":" + element.port + " ";
          hostInfo = hostInfo + "]";
          GUILayout.Label(hostInfo);
          GUILayout.Space(5);
          GUILayout.Label(element.comment);
          GUILayout.Space(5);
          GUILayout.FlexibleSpace();
          if (GUILayout.Button("Connect"))
          {
               // Connect to HostData struct, internally the correct method is used (GUID when using NAT).
               Network.Connect(element);
          }
          GUILayout.EndHorizontal();
     }
}



可以繼續向其中新增ping值和地理位置等資訊,這裡不再贅述。

關於NAT punchthrough,如果某個客戶端裝置在NAT中且沒有NAT punchthrough技術,可以依賴Facilitator,如果做伺服器的客戶端和其他客戶端都能連線到這個Facilitator,那麼伺服器和客戶端就能進行通訊,前提是Facilitator使用外部IP和埠。Master Server可以提供這個外部IP和埠,所以一般Master Sever同時擔當Faciliator的角色,此時預設兩者擁有共同的IP地址。使用MasterServer.ipAddress,MasterServer.port,Network.natFacilitatorIP,Network.natFacilitatorPort修改IP地址和埠號。

Unity提供預設的Master Server服務,但是你也可以自己組建一個Mater Server.方法詳見官網

6.Debug.

在Network Manager中可以設定debug級別為看到所有進出的交通訊息。


NetworkManager設定

在Inspector和Hierarchy裡觀察遊戲物件的建立以及ViewID等。
Unity可以雙開,你可以讓它執行不同的project.在windows環境只需要再開啟一個Unity並開啟一個新的project即可。在Mac OS X環境需要在終端輸入以下命令:

/Applications/Unity/Unity.app/Contents/MacOS/Unity -projectPath “/Users/MyUser/MyProjectFolder/”
/Applications/Unity/Unity.app/Contents/MacOS/Unity -projectPath “/Users/MyUser/MyOtherProjectFolder/”

在對Network進行debug的時候,要選中Run In Background選項,因為當你執行兩個例項並在其中一個上debug,另一個會失去焦點,你要確保在後臺它也能執行。在Project Setting>Player裡面設定,也可以直接使用Apllication.runInBackground = true語句開啟該功能。


在Player設定面板中選中Run In Background

【轉】原轉載地址如下:http://mrraindrop.com/blog/2012/02/unity%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B05%EF%BC%9A%E5%A4%9A%E4%BA%BA%E6%B8%B8%E6%88%8F%E5%9F%BA%E7%A1%80/

相關推薦

UNITY遊戲基礎

1.總覽。 多人遊戲基本結構:Clent/Server,分為Authoritative Server和Non-Authoritative Server兩種,前者客戶端傳送訊息,伺服器端反饋結果,好處是有效防止客戶端作弊,並統一不同客戶端之間的物理表現和互動狀況,缺陷是存

Unity遊戲和網路功能(二) 使用網路管理類

[本文翻譯自Unity 5.2的官方文件] NetworkManager是一個可以管理多玩家遊戲的網路狀態的元件。實際上,它是完全用HLAPI實現的,因此開發者可以使用其他的方式實現他的所有功能。然而,NetworkManager把很多有用的功能整合在了一起,

Steamworks and Unity – P2P遊戲

working 事情 異常 研究 there 是否 ont else lock 之前我們討論過“如何把Steamworks.Net和Unity整合起來”,這是一個很好的開始,現在我們研究深一點,談一談Steam中的多人遊戲。這不是教程,但是可以指導你在你的遊戲中如何使用St

使用Multiplayer Networking做一個簡單的遊戲例子-1/2

lap settings isl log atime round 窗口 bottom -m 原文地址: http://blog.csdn.net/cocos2der/article/details/51006463 本文主要講述了如何使用Multiplayer Networ

Photon遊戲開發教程

udp 隨機 r12 dom pid log 時機 觸發器 動作 http://gad.qq.com/article/detail/26112 PUN介紹 入門 Photon Unity Networking(首字母縮寫PUN)是一個Unity多人遊戲插件包。它提供了身份驗

Unity3d學習之路-初識GameSparks遊戲外掛

初識GameSparks多人遊戲外掛 初識GameSparks多人遊戲外掛 簡介 GameSparks介紹 建立遊戲 雲服務配置

虛幻引擎開發遊戲聯機

Online Subsystem 藍圖部分(簡略): Create Session Find Session Join Session 配置檔案(DefaultEngine.ini) LAN: 新增如下配置 [OnlineS

[NodeJS]Node.js 打造實時遊戲框架

【編者按】Node.js的大紅大紫也造就了一大批新應用、新工具的誕生。比如基於Node.js的開發框架、開源軟體等等。本文轉自阿里巴巴使用者體驗部有一點部落格,作者詳細描述了使用Node.js、Node-Webkitk開發的實時多人遊戲框架Spaceroom過程。 在 Node.js 如火如荼發展的今天

遊戲對戰技術(坦克大戰、狀態同步)

  用狀態同步的方式實現一個坦克大戰的小遊戲,這也是一次全新的嘗試,從遊戲的效果來看,在正常的網路速度下效果符合預期。這裡跟大家分享下游戲客戶端中用到的關鍵技術點。 一、 同步方式的選擇,狀態同步or 幀同步?   狀態同步: 同步的是遊戲中的各種狀態,遊戲

【Unity3D】 Photon遊戲開發教程

一、前言 Photon Unity Networking(首字母縮寫PUN)是一個Unity多人遊戲外掛包。它提供了身份驗證選項、匹配,以及快速、可靠的通過我們的Photon後端實現的遊戲內通訊。 二、原文 三、正文 Photon多人遊戲開發

使用Multiplayer Networking做一個簡單的遊戲例子-2/3(Unity3D開發之二十六)

7. 在網路中控制Player移動 上一篇中,玩家操作移動會同時控制同屏內的所有Player,且只有自己的螢幕生效。因為咱們還沒有同步Transform資訊。 下面我們通過UnityEngine.Networking元件來實現玩家控制各自Player

PHP下查詢遊戲《Minecraft》遊戲 伺服器的人數。

廢話不多說,直接上圖:                作為一個優雅的Minecraft伺服器,肯定需要官網的嗯。 很多伺服器的官網都有顯示當前伺服器線上人數,延遲,每一個子服線上人數,甚至出了個流量圖。 我們不搞花裡胡哨的查詢

Unity Networking開發聯機射擊遊戲

UNet開發多人聯機射擊遊戲 引言: Networking作為Unity官方的用於開發多人線上遊戲的網路模組,開發者可以不用自己搭建網路模組的底層,通過使用Unity提供的一些相關元件,可以輕鬆實現簡單的多人線上遊戲。本片部落格為泰課線上賈老師的《Unity多人網路系統講解》的學習筆記,

即時戰鬥遊戲服務端系列[2]--90坦克Online遊戲對象介紹以及渲染機制

d+ ica 部分 avi 產生 4.4 1.8 timer lock 先上類圖,略大,點擊此處放大: 1.先說下方接口 1.1 場景物品接口 ISceneObject : OpLog.IOpItem, IStackPoolObject 全部場景對象的基本

Unity 5實戰 使用C#和Unity開發平臺遊戲pdf

name clas position ati 下載地址 組件 inspector ins over 下載地址:城通網盤 作者簡介編輯 Joseph Hocking是一位交互式媒體開發方面的軟件工程師。他就職於Synapse Games公司並在芝加哥哥倫比亞學院教授遊戲開發

用CocosCreator和Pomelo編寫在線實時聊天室(一)----基礎知識和環境安裝

shu 以及 pan 信息 ast pre alt web 技術 客戶端:Cocos Creator 1.6.2服務器端:Pomelo 2.2.5源碼地址:https://github.com/foupwang/CocosCreatorChatForPomelo.git 本

(轉整)UE4遊戲優化 大地型遊戲的優化(一)遊戲線程的優化

兼容 source bubuko 又是 mic ini 策略 動態 author 施主分享隨緣,評論隨心,@author:白袍小道 小道暗語: 1、因為小道這裏博客目錄沒自己整,暫時就用隨筆目錄結構,所以二級目錄那啥就忽略了。標題格式大致都是

火星時代;老師談VR遊戲《The Assembly》幕後製作過程

雖然大多數業界分析師都預測虛擬現實技術將有光明的未來,但要讓普通消費者信服就稍微有點困難了。他們的意見並不是來自在VR方興未艾之時編寫的財務預報或優秀個股預測,而是來自VR本身能夠提供的實際體驗。對很多人來說,歸根結底要看VR有多大能力提高互動式娛樂體驗的兩個基本要素——沉浸感和故事講述。 VR的沉浸感不僅

2018TGDC王禰:UE4製作大地型遊戲的優化

在2018TGDC大會上,來自Epic Games中國資深技術工程師王禰先生髮表了《UE4製作多人大地型遊戲的優化》主題演講。王禰有近10年的虛幻引擎使用經驗,從console遊戲、掌機到PC端MMO遊戲,再到手遊,都有過相關開發經驗;現在Epic Games China,他作為唯一的引擎技術專家,

Unity+Slua實現遊戲常用UI元件(六)——單()元素滾動佈局元件

單元素滾動佈局元件——LSIScrollView 定義 單元素滾動佈局元件下只有一種元素,支援元素自動佈局並在滾動過程中元素複用 元件效果展示 LSIScrollView效果展示 LSIScrollView元素複用 特性 建立滿足Mask顯示的最小