Unity5網絡模塊UNet介紹

  前段時間,研究了一下UNet,通過項目實踐,大體整理了下遇到的問題。服務器

  源碼Bitbucket:須要說明的是,這個項目只包含上層的包裝,一些低層的網絡實如今Unity內部,如NetworkTransport類等並不包含。網絡

  UNet常見概念簡介

  Spawn:簡單來講,把服務器上的GameObject,根據上面的NetworkIdentity組件找到對應監視鏈接,在監視鏈接裏生成相應的GameObject.異步

  Command:客戶端調用,服務器執行,這樣客戶端調用的參數必須要UNet能夠序列化,這樣服務器在執行時才能把參數反序列化。須要注意,在客戶端須要有權限的NetworkIdentity組件才能調用Command命令。ide

  ClientRpc:服務端調用,客戶端執行,同上,服務端的參數序列化到客戶端執行,通常來講,服務端會找到上面的NetworkIdentity組件,肯定那些客戶端在監視這個NetworkIdentity,Rpc命令會發送給全部的監視客戶端。函數

  Server/ServerCallback:只在服務器端運行,Callback是Unity內部函數。動畫

  Client/ClientCallback:同上,只在客戶端運行,Callback是Unity內部函數。spa

  SyncVar:服務器的值能自動同步到客戶端,保持客戶端的值與服務器同樣。客戶端值改變並不會影響服務器的值。設計

  上面的大部分特性都會轉化成相應的MsgType,其中服務器調用,客戶端執行對應MsgType有如Spawn,ClientRpc,SyncVar對應的MsgType分別爲ObjectSpawn,Rpc,UpdateVars,這些都是NetworkServer調用,客戶端獲得相應消息,執行相應方法。客戶端調用,服務器執行的MsgType有如Command,客戶端發送,服務器檢測到相應消息後執行。orm

  UNet主要類介紹

  NetworkIdentity組件介紹:網絡物體最基本的組件,客戶端與服務器確認是不是一個物體(netID),也用來表示各個狀態,如是不是服務器,是不是客戶端,是否有權限,是不是本地玩家等。對象

  一個簡單例子,A是Host(又是服務器,又是客戶端),B是一個Client,A與B分別有一個玩家PlayA與PlayB.在機器A上,playA與playB isServer爲true,isClent爲true,其中playA有權限,是本地玩家,B沒權限,也不是本地玩家。在機器B上,playA與playB isServer爲false,isClent爲true,其中playB有權限,是本地玩家,A沒權限,也不是本地玩家。A與B上的PlayA的netID相同,A與B上的PlayB的netID也相同,其中netID用來表示他們是同一網絡物體在不一樣的機器上。

  在下面用網絡物體來表示帶有NetworkIdentity組件的GameObject.

  NetworkConnection:定義一個客戶端與服務器的鏈接,包含當前客戶端監視那些服務器上的網絡物體,以及封裝發送和接收到服務器的消息。

  NetworkClient:主要持有當前NetworkConnection對象與全部NetworkClient列表的靜態對象,處理一些默認客戶端的消息。

  網絡物體上的監視者就是一個或多個NetworkConnection,用來表示一個或多個客戶端對這個網絡物體保持監視,那麼當這個網絡物體在服務器上更新後,會自動更新對全部監視者的對應的網絡物體。

  NetworkScene:簡單來講,1Server與Client須要維護一個網絡物體列表,Server能夠遍歷全部網絡物體發送消息等,而且維持Server與Client上的網絡物體保持同步,而且客戶端記錄須要註冊的prefab列表.其中NetworkServer與ClientScene都包含一個NetworkScene對象,引用網絡物體列表。

  NetworkServer:主要持有一個NetworkScene而且作一些只有在服務器上才能對網絡服務作的事,如spawn, destory等。以及維護全部客戶端鏈接。

  ClientScene:主要持有一個靜態NetworkScene對象,用於註冊網絡物體的prefab列表,以及客戶端場景上已經有的網絡物體列表,處理SyncVar,Rpc,SyncEvent特性等,還有以及ObjectSpawn,objectDestroy,objectHide消息等。

  UNet用時想到的問題與源碼的答案

  問題1 spawn發生了什麼,客戶端爲何要註冊相應的prefab.

  1 當服務器spawn一個網絡物體時,網絡物體調用OnStartServer,分配netID.並註冊到相應服務器上的的NetworkScene的網絡物體列表中,更新如isServer爲true等信息。

  2 查找全部客戶端鏈接,查看每一個客戶端鏈接是否須要監視這個網絡物體,若是爲true,那麼給這個客戶端上一個消息MsgType.ObjectSpawn或是MsgType.ObjectSpawnScene(這種通常是服務場景變換後自動調用),並傳遞上面的netID.

  3 當客戶端接受到ObjectSpawn消息,會在註冊的prefab裏查找,查找到後Instantiate個網絡物體,當接受到ObjectSpawnScene時,會在場景裏查找這個網絡物體,而後都註冊到ClientScene裏的NetworkScene的網絡物體列表中,並更新netID與服務器的同樣。更新如isClent爲true等信息。

  咱們手動spawn一個物體時,調用的是ObjectSpawn消息,客戶端接到這個消息處理獲得一個assetID,咱們要根據prefabe實例一個新對象,只有客戶端註冊了相應的prefabe信息才能根據對應的assetID找到prefabe.

  問題2 NetworkIdentity的netID表示什麼,那個時候分配。

  當服務器與客戶端的netID相同,表示他們是同一物體,相應標示如SyncVar,服務器變了,對應客戶端上相同的netID的網絡物體,更新成服務器上的數據,Rpc,Commandg 通常也是相同的netID之間調用。

  分配通常發生在服務器spawn一個網絡物體時,網絡物體調用OnStartServer時發生產生netID。

  在客戶端接受相應的ObjectSpawn消息,會把服務器上的對應物體的netID傳遞過來,產生新的網絡物體並賦這個netID。

  問題3 NetworkIdentity的sceneID是什麼,在場景裏已經有NetworkIdentity組件的物體是如何在客戶端與服務器聯繫的。

  當網絡物體並非spawn產生在服務器與客戶端,而是在服務器與客戶端場景自己就有時,咱們也須要在服務器與客戶端之間創建聯繫,這種物體會有一個sceneID來標示,這種模型通常是服務器場景變換完成後,NetworkServer調用spawnObjects會把這種網絡物體與全部客戶端同步,當spawn完成後事後,相應客戶端會產生一個和服務端相同的netID。

  問題4 服務器場景切換後,各個NetworkIdentity組件的物體如何與客戶端聯繫。

  以下順序由於有異步操做,並不能肯定,以下順序只是通常可能的順序。

  1。服務器異步調用場景,發送給全部客戶端開始切換場景。MsgType.Scene

  2。客戶端接受MsgType.Scene,開始切換場景。

  3。服務器場景完成,會查找全部的網絡物體,而後spawn這些網絡物體,這樣各個網絡物體經過相同的netID聯繫起來。

  4。客戶端場景完成後,再次調用OnClientConnect,通常來講,不執行任何操做。

  問題5 客戶端爲何要網絡物體的權限,它有了權限能作什麼。

  通常來講,當spawn某個服務器上的網絡物體後,服務器有它的權限,客戶端並不能更改這個網絡物體,或是說更改這個網絡物體相應的屬性後並不能同步到服務器和別的客戶端上,只是本機上能看到改變。

  那麼我若是須要能改變這個網絡物體上的狀態,並能同步到全部別的客戶端上,咱們須要擁有這個網絡物體的權限,由於這樣才能在本機上發送Command命令,才能告訴服務器我改變了狀態,服務器也才能告訴全部客戶端這個網絡物體改變了狀態。

  其中本地player在建立時,當前客戶端對本地player有權限。客戶端上有權限的網絡物體上的SyncVar改變後,也並不會能同步到服務器,服務器根本沒有註冊UpdateVars消息,這種仍是須要客戶端本身調用Command命令。

  問題6 UNet常見的封裝狀態同步處理有那些,其中NetworkTransform與NetworkAnimator分別怎樣通訊,若是是客戶端權限的網絡物體又是怎麼通訊的了。

  UNet常見的封裝狀態同步狀態方法有二種。

  一是經過ClientRpc與Command是封裝發送消息。客戶端與服務端一方調用,而後序列化相應的參數,而後到服務器與客戶端反序列化參數執行。

  二是網絡內置的序列化與反序列化,序列化服務器的狀態,而後客戶端反序列化相應的值,如SyncVar經過相應的OnSerialize,OnDeserialize.這種只能同步服務器到客戶端。

  這二種本質都是客戶端與服務器互相發送MsgType消息,對應的服務器與客戶端註冊相應消息處理。NetworkAnimator 服務器上的動畫改變,會發消息通知全部客戶端相應狀態改變了,如Rpc。NetworkTransform 服務器經過OnSerialize序列化相應的值,而後客戶端反序列化相應的值。

  若是客戶端有對應NetworkTransform與NetworkAnimator網絡物體的權限。NetworkAnimator 相應客戶端提交狀態到服務器上,而後分發到全部客戶端,至關於調用了Command,並在Command裏調用了Rpc方法。NetworkTransform 相應客戶端發送消息到服務器上,服務器更新相應位置,方向。而後經過反序列化到全部客戶端。

  因此若是客戶端有受權,那麼NetworkAnimator與NetworkTransform在服務器或是有受權的客戶端的狀態改變都能更新到全部客戶端,注意這二個組件對localPlayerAuthority的處理不一樣,在NetworkTransform中,localPlayerAuthority爲false時,客戶端不能更新到全部客戶端,在NetworkAnimator中,localPlayerAuthority爲true時,服務器不能更新到客戶端上。

  其中注意SyncVar特性,就算客戶端受權,客戶端改變後,也不會同步到別的機器上。

  因此若是咱們本身設計相似的網絡組件,須要考慮客戶端受權的相應處理,就是差很少添加一個Command命令。

  問題7 客戶端受權與本地player受權有什麼區別。

  通常物體的權限都在服務器上,若是要對網絡物體受權給客戶端,通常經過SpawnWithClientAuthority實現,這樣在相應客戶端上的hasAuthority爲true,其中相應的playerControllerID爲-1。

  而本地player受權localPlayerAuthority,在相應的網絡物體上的Local Player Authority勾選上,在對這個網絡物體的全部監視客戶端上,本地player受權都是true,這種通常用於玩家,或是玩家控制位移的物體,playerControllerID大於等於0。

  因此客戶端受權針對是某個客戶端,在這個客戶端上的這個網絡物體的hasAuthority爲true,而本地player針對是某個網絡物體,在全部客戶端上的這個網絡物體的localPlayerAuthority都爲true.

  問題8 UNet怎麼實現迷霧地圖

  經過NetworkProximityChecker,這樣每楨檢測當前網絡物體的監視鏈接,肯定那些客戶端須要這個網絡物體。一樣,想實現更復雜的能夠本身實現相似。

  問題9 NetworkServer.Destroy作了啥

  必須是網絡物體,且最好能在服務器調用,調用時,發給全部的監視Connect,銷燬對應網絡物體,而後服務器銷燬。請看MsgType.ObjectDestroy消息流程.

  須要注意的是在服務器中,Destroy某網絡物體,會自動調用NetworkServer.Destroy。代碼在NetworkIdentity.OnDestroy.

  問題10 服務器添加角色時作了那些事。

  當客戶端鏈接服務器時,設置自動建立角色後,會自動建立角色。

  1 服務器添加一個player,設定playercontrollerID

  2 設置當前conn的ready爲true.而後檢測當前的conn是否須要監視服務器上NetworkScene的網絡物體列表的各個網絡物體,其中客戶端上的isspawnFinished表示NetworkScene的網絡物體列表是否檢測完成。

  3 把服務器的player的spawn下去,設定對應網絡物體記錄的本地權限客戶端爲當前客戶端,相應的playercontrollerid發送到客戶端。

  問題11 NetworkClient與networkServer的active表示什麼,那些時候用

  networkServer開始監聽後,設定active爲true。

  networkClient鏈接上服務器後,設定爲true。

  當有些消息發送,或是Rpc與Command等的調用時,時機可能會在active以前,引起錯誤。

  問題12 網絡的Update作些啥。

  1 服務器更新,處理一些如客戶端連接與丟失連接,還有接收消息並找到對應事件處理,以及序列化服務器網絡物體要更新的數據。

  2 客戶端更新,如上服務器的處理,主要也是相應消息處理。

  3 檢查服務器與客戶端的場景是否加載完成。

 

  最後,想象一下,在網絡環境下,咱們拉開弓箭,生成箭,箭在客戶端上緩緩拉開,咱們應該如何作?

  首先弓箭要讓全部客戶端看的到,咱們要在服務器上生成,而後spawn分發到相應多個客戶端,而後當前客戶端還須要當前箭的權限,這樣當前用戶才能控制這把箭,並把當前用戶控制箭產生的新位置同步給全部的客戶端。

  其次若是採用Valve的LabRender渲染器,須要在開始服務器時關閉,等到對應的角色加載後,再經過localplayer打開各自對應的valveCamer,否則服務器上的valveCamer可能得不到正確的陰影圖。

  若是有分析不對的地方,歡迎你們指出。

相關文章
相關標籤/搜索