UNet详解(转)
Unity Networking(UNet)函數時序統計和分析
背景和概述
Unity Networking是官方自Unity5.1以來推出的新網絡通信解決方案。UNet是非官方但更民間更精簡的叫法。
本文需要讀者有基礎的UNet知識。
了解UNet時序,可以更好更嚴謹地編寫UNet相關的業務邏輯代碼。
本文針對UNet的HLAPI進行時序統計和分析。
本文可作為工具文檔,需要時可進行時序查閱。
在有時序統計的基礎上,本文再參考Unity Networking 5.3源代碼(Bitbucket網站可能需要翻墻)進行整合分析,可以幫助了解底層發生的具體邏輯。
當前將Unity(就算是Headless)運行在Linux服務器上,會出現一定的性能問題。聯系咨詢過Unity內部開發同學Ian和一位和藹大胖子,獲知Headless所剔除的功能模塊并不多,僅僅是最終不提交(也不能提交)到GPU、DSP而已。原話是“Modulization is hard”、“Should not run Unity on the server”。
所以當前,應避免粗暴地將Unity運行在“關鍵”服務器上。
- 應從業務層著手剔除Headless模式下所啟用的業務功能(如模型、渲染、物理、音效等)
- 將Unity運行于“非關鍵”服務器(比如用于外掛分析,等)是可能可行的
- 將Unity無狀態地運行,多“關鍵”服務器(比如用于戰斗)共享該Unity服務器,是有成功案例的
- 但粗暴地每一局游戲都在服務器運行一個Unity進程是欠妥的
可惜的是,UNet的默認思路正是最后一種。由于Ian并非UNet Team的開發同學,所以其并不十分了解將于Unity5.4(但被delay了)的Server Library所完成的功能。但一種推測是,Server Library正是為了避免將Unity運行于服務器,而是提供UNet、Unity的基礎功能(Math等),讓我們服務器同學利用UNet接口,重新實現邏輯。
測試方法
測試Unity版本為5.3.1。運行平臺是OSX。
測試NetworkManager通過NBNetworkManager繼承并override掉關鍵函數;測試Player的Prefab名字為NBPlayer。通過在這個Prefab加上測試腳本TestNetworkBehaviour進行日志輸出。
通過分析日志,可以統計UNet函數的時序。
函數時序概括
以下為關鍵函數的羅列,以供快速查詢之用。
如需可細看下一章節的詳細文檔及分析。
Dedicated Server情況
| Server初始化階段 | Server初始化階段 |
| Awake() | ? |
| Start() | ? |
| OnStartServer() | ? |
| ServerChangeScene() | ? |
| OnServerSceneChanged() | ? |
| Client初始化階段 | Client初始化階段 |
| OnServerConnect() | ? |
| OnServerReady() | ? |
| Player初始化階段 | Player初始化階段 |
| OnServerAddPlayer() | ? |
| ? | Awake() |
| ? | OnEnable() |
| ? | OnStartServer() |
| ? | OnRebuildObservers() |
| ? | OnSerialize()(多次) |
| ? | Start() |
| Player運轉階段 | Player運轉階段 |
| ? | FixedUpdate()(多次) |
| ? | Update()(多次) |
| ? | OnSerialize()(多次) |
| Player銷毀階段 | Player銷毀階段 |
| ? | OnDisable() |
| ? | OnDestroy() |
| OnServerDisconnect | ? |
| Server銷毀階段 | Server銷毀階段 |
| OnStopServer() | ? |
Remote Client情況
| Client初始化階段 | Client初始化階段 |
| Awake() | ? |
| Start() | ? |
| OnStartClient() | ? |
| OnClientConnect() | ? |
| OnClientSceneChanged() | ? |
| Player初始化階段 | Player初始化階段 |
| ? | Awake() |
| ? | OnEnable() |
| ? | OnDeserialize() |
| ? | PreStartClient() |
| ? | OnStartClient() |
| ? | OnStartLocalPlayer() |
| ? | OnStartAuthority() (后面運轉階段也可能調到) |
| ? | OnDeserialize()(多次) |
| ? | Start() |
| Player運轉階段 | Player運轉階段 |
| ? | FixedUpdate()(多次) |
| ? | Update()(多次) |
| ? | OnDeserialize()(多次) |
| Player銷毀階段 | Player銷毀階段 |
| ? | OnNetworkDestroy() |
| ? | OnDisable() |
| ? | OnDestroy() |
| Client銷毀階段 | Client銷毀階段 |
| OnStopClient() | ? |
Host情況
| Host初始化階段 | Host初始化階段 |
| Awake() | ? |
| Start() | ? |
| OnStartHost() | ? |
| OnStartServer() | ? |
| ServerChangeScene() | ? |
| OnServerConnect() (LocalClient混雜進來的Server函數) | ? |
| OnStartClient() (LocalClient混雜進來的Client函數) | ? |
| OnClientConnect() (LocalClient混雜進來的Client函數) | ? |
| OnServerSceneChanged() | ? |
| OnClientSceneChanged() (LocalClient混雜進來的Client函數) | ? |
| OnServerReady() (LocalClient混雜進來的Server函數) | ? |
| OnServerAddPlayer() (LocalClient混雜進來的Server函數) | ? |
| OnServerConnect() | ? |
| OnServerReady() | ? |
| Player初始化階段 | Player初始化階段 |
| OnServerAddPlayer() | ? |
| ? | Awake() |
| ? | OnEnable() |
| ? | OnStartServer() |
| ? | PreStartClient() |
| ? | OnStartClient() |
| ? | OnRebuildObservers() |
| ? | OnSerialize()(多次) |
| ? | OnSetLocalVisibility() |
| ? | Start() |
| Player運轉階段 | Player運轉階段 |
| ? | FixedUpdate()(多次) |
| ? | Update()(多次) |
| ? | OnSerialize()(多次) |
| Player銷毀階段 | Player銷毀階段 |
| ? | OnNetworkDestroy() |
| ? | OnDisable() |
| ? | OnDestroy() |
| OnServerDisconnect | ? |
| Host銷毀階段 | Host銷毀階段 |
| OnStopHost() | ? |
| OnStopServer() | ? |
| ServerChangeScene() (LocalClient混雜進來的Server函數) | ? |
| OnStopClient() (LocalClient混雜進來的Client函數) | ? |
函數時序的詳細文檔及分析
以下為嚴格按照時間次序進行羅列的UNet函數時序,附上官方文檔。重要地方也結合源碼進行解釋。
Dedicated Server情況
Dedicated Server的Server初始化階段
NetworkManager.Awake()
NetworkManager目前的Awake()(被不好地設計)為非virtual的私有方法。所以子類應注意不能再定義Awake(),否則將hide掉基類的Awake()。
NetworkManager.Start()
NetworkManager (NewBorn.NBNetworkManager).Start()NetworkManager.OnStartServer()
public void OnStartServer();
Description
This hook is invoked when a server is started - including when a host is started.
StartServer has multiple signatures, but they all cause this hook to be called.
Server初始化函數。調用肯定比看似相似的OnStartClient()早。
注意在OnStartServer()之后,才進行網絡Connect的初始化、才進行場景的切換。
NetworkManager.ServerChangeScene()
public void ServerChangeScene(string newSceneName);
Parameters
newSceneName?The name of the scene to change to. The server will change scene immediately, and a message will be sent to connected clients to ask them to change scene also.
Description
This causes the server to switch scenes and sets the networkSceneName.
Clients that connect to this server will automatically switch to this scene. This is called autmatically if onlineScene or offlineScene are set, but it can be called from user code to switch scenes again while the game is in progress. This automatically sets clients to be not-ready. The clients must call NetworkClient.Ready() again to participate in the new scene.
StartServer()里、OnStartServer()之后,調用ServerChangeScene()進行場景切換。之后在任意時刻,也可以手動調用它進行中途的場景切換。
在ServerChangeScene()里,會發出MsgType.Scene通知當前已連接上的Client也進行場景的切換。
Battle_Demo_Official
NetworkManager (NewBorn.NBNetworkManager).ServerChangeScene()
NetworkManager.OnServerSceneChanged()
public void OnServerSceneChanged(string sceneName);
Parameters
sceneName?The name of the new scene.
Description
Called on the server when a scene is completed loaded, when the scene load was initiated by the server with ServerChangeScene().
Server完成場景切換后的一個回調。
在本回調之前,Server會收集場景所有已有NetworkIdentity的GameObject,并發出Spawn的Message,從而通知已連接上的Client進行Spawn。
Battle_Demo_Official
NetworkManager (NewBorn.NBNetworkManager).OnServerSceneChanged()
2016-01-01T10:57:28.8472060+08:00
至此,Server的初始化階段結束。之后(通過上面的時間10:57:28和下面的時間11:04:10就可以看出時間差),當有Client連接進Server的時候,函數流程就進入該Client的初始化階段。
Dedicated Server的Client初始化階段
NetworkManager.OnServerConnect()
public void OnServerConnect(Networking.NetworkConnection conn);
Parameters
conn?Connection from client.
Description
Called on the server when a new client connects.
新玩家新Client和Server建立連接后的回調函數。
Client剛連接上來,第一個問題肯定是“我現在在什么場景?”。所以在本回調之前,Server會發出MsgType.Scene,通知客戶端進行場景加載。
hostId: 0 connectionId: 1 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnServerConnect()
2016-01-01T11:04:10.7621350+08:00
NetworkManager.OnServerReady()
public void OnServerReady(Networking.NetworkConnection conn);
Parameters
conn?Connection from client.
Description
Called on the server when a client is ready.
The default implementation of this function calls NetworkServer.SetClientReady() to continue the network setup process
當Client“準備好”(加載好場景,一些自定義的初始化)后,需要發送MsgType.Ready給Server。
Server收到這個Message了之后,就會調用本OnServerReady()函數。
Client準備好了之后,接著問題是“我Client當前場景有什么網絡對象可見和需要同步?”
所以在OnServerReady()里會調用NetworkServer.SetClientReady(),進行該Client的可見性檢測,然后在NetworkServer.SendSpawnMessage()里下發MsgType.ObjectSpawn進行Spawn。
hostId: 0 connectionId: 1 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnServerReady()
至此,Client已經連接好、加載好場景、同步好已有的網絡對象。
所以Server將進入Player初始化階段。
Dedicated Server的Player初始化階段
NetworkManager.OnServerAddPlayer()
public void OnServerAddPlayer(Networking.NetworkConnection conn, short playerControllerId);
Parameters
conn?Connection from client.
playerControllerId?Id of the new player.
extraMessageReader?An extra message object passed for the new player.
Description
Called on the server when a client adds a new player with ClientScene.AddPlayer.
The default implementation for this function creates a new player object from the playerPrefab.
新連接上來的Client連接好了、場景準備好了、其他有NetworkIdentity的GameObject同步好了,接下來準備為該Client準備屬于它自己的Player了。
通過調用ClientScene.AddPlayer()發出MsgType.AddPlayer可以通知服務器添加屬于該connection的Player,然后Server就響應該Message會調用OnServerAddPlayer()。
用戶可以在OnServerAddPlayer()自定義新建Player的邏輯,包括直接Instantiate新Player、或者從自己的Spawn機制里取出Player、給Player修改初始化屬性等。
然后,在OnServerAddPlayer()里就會調用NetworkServer.AddPlayerForConnection(),繼而一系列初始化Player邏輯(生成netId、決定Observer、收集SyncVar、發送MsgType.Spawn給Client),通知Client真正去創建Player。
hostId: 0 connectionId: 1 isReady: True channel count: 2, 0
NetworkManager (NewBorn.NBNetworkManager).OnServerAddPlayer()
至此,Player的Prefab在Server已被Instantiate出來,繼而調用
- NetworkServer.AddPlayerForConnection()、
- NetworkServer.FinishPlayerForConnection()
- NetworkServer.SpawnObject()
等函數,正式開始NetworkIdentity/NetworkBehaviour的函數流程。
NetworkBehaviour和NetworkIdentity是相互依存的。許多NetworkBehaviour的UNet相關函數事實上都是在其配對的NetworkIdentity中被NetworkIdentity所觸發調用的。
NetworkBehaviour.Awake()
留意到這次測試Player的GameObject的instanceID是-10746。
只有localPlayerAuthority=True
這個UNet配置變量是合法的。其他UNet變量都是非法的。
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Awake()
NetworkBehaviour.OnEnable()
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnEnable()
NetworkBehaviour.OnStartServer()
public void OnStartServer();
Description
This hook is invoked when a server is started - including when a host is started.
StartServer has multiple signatures, but they all cause this hook to be called.
在NetworkIdentity.OnStartServer()里,
- 會cache住NetworkIdentity所同GameObject的所有NetworkBehaviour
- 會生成netId給自己
- 通過NetworkServer.instance.SetLocalObjectOnServer(),更新isServer的標志位
- 調用這些NetworkBehaviour的OnStartServer()函數。
所以這個時候,netId和isServer合法了。
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnStartServer()
NetworkBehaviour.OnRebuildObservers()
public bool OnRebuildObservers(HashSet<NetworkConnection> observers, bool initialize);
Parameters
observers?The new set of observers for this object.
initialize?True if the set of observers is being built for the first time.
Returns
bool?Return true if this function did work.
Description
Callback used by the visibility system to (re)construct the set of observers that can see this object.
Implementations of this callback should add network connections of players that can see this object to the observers set.
這個新的NetworkIdentity在Server創建了,但哪些Client是其真正的“觀察者”(Observer)呢?只有這些觀察者Client,才需要在他們的運行時里創建這個新NetworkIdentity及其GameObject。
NetworkIdentity會調用其所有NetworkBehaviour的OnRebuildObservers()。
默認情況下,是當前已連接的所有Client都能觀察到這個新NetworkIdentity。
但如果有NetworkBehaviour的OnRebuildObservers()返回了true,則以HashSet<NetworkConnection> observers里存在的連接作為Observer。
observers=System.Collections.Generic.HashSet`1[UnityEngine.Networking.NetworkConnection], initialize=True,
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnRebuildObservers()
NetworkBehaviour.OnSerialize()
public bool OnSerialize(Networking.NetworkWriter writer, bool initialState);
Parameters
writer?Writer to use to write to the stream.
initialState?If this is being called to send initial state.
Returns
bool?True if data was written.
Description
Virtual function to override to send custom serialization data.
決定了Observer后、給這些Observer發送MsgType.Spawn之前,需要在Server把這個新NetworkIdentity的GameObject的所有同步屬性進行序列化。
OnSerialize()和OnDeserialize()
是用于自定義NetworkBehaviour中變量的序列化和反序列化的虛函數。前者必然是只在Server被調用、后者必然是只在Client被調用。
事實上,[SyncVar]修飾的變量和SyncList變量都是通過編譯時UNet將這些變量的序列化反序列化邏輯自動生成在OnSerialize()和OnDeserialize()中的。
所以要注意,如果你在NetworkBehaviour中顯式override掉了這兩個函數,則該NetworkBehaviour的[SyncVar]修飾的變量和SyncList變量都需要你自行編寫代碼實現序列化反序列化。
serializeCount=1, writer=UnityEngine.Networking.NetworkWriter, initialState=True,
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnSerialize()
NetworkBehaviour.Start()
一幀真正開始。
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Start()
至此,在Dedicated Server的Player初始化階段已結束。接下來是Player運轉階段。
Dedicated Server的Player運轉階段
NetworkBehaviour.FixedUpdate()(多次)
fixedUpdateCount=22, go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).FixedUpdate()
NetworkBehaviour.Update()(多次)
updateCount=22, go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Update()
NetworkBehaviour.OnSerialize()(多次)
serializeCount=16, writer=UnityEngine.Networking.NetworkWriter, initialState=False,
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnSerialize()
Dedicated Server的Player銷毀階段
通過調用NetworkServer.Destroy(gameObject);,gameObject進入銷毀階段。
NetworkBehaviour.OnDisable()
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnDisable()
NetworkBehaviour.OnDestroy()
留意到所有變量皆已非法。
留意到在Server并不會調用OnNetworkDestroy()。
go.instanceID=-10746,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 2 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnDestroy()
至此,Player的NetworkIdentity/NetworkBehaviour流程結束。
NetworkManager.OnServerDisconnect()
public void OnServerDisconnect(Networking.NetworkConnection conn);
Parameters
conn?Connection from client.
Description?Called on the server when a client disconnects.
hostId: 0 connectionId: 2 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnServerDisconnect()
Dedicated Server的Server銷毀階段
略。因為在Server kill掉Unity不能及時輸出日志。
至此,Player的整個Dedicated Server流程結束。
Remote Client情況
在上面已有Dedicated Server情況的前提下,Remote Client情況將適度從簡,僅針對差異性進行描述。
Remote Client的Client初始化階段
NetworkManager.Awake()
NetworkManager目前的Awake()(被不好地設計)為非virtual的私有方法。所以子類應注意不能再定義Awake(),否則將hide掉基類的Awake()。
NetworkManager.Start()
NetworkManager (NewBorn.NBNetworkManager).Start()NetworkManager.OnStartClient()
public void OnStartClient(Networking.NetworkClient client);
Parameters
client?The NetworkClient object that was started.
Description
This is a hook that is invoked when the client is started.
StartClient has multiple signatures, but they all cause this hook to be called.
當調用NetworkManager.StartClient()的時候,在其內部進行:
- Client注冊各種Client相關的MsgType監聽(MsgType.Connect/Disconnect/Scene/)
- 連接,
- 然后會調用OnStartClient()。
UnityEngine.Networking.NetworkClient
NetworkManager (NewBorn.NBNetworkManager).OnStartClient()
Client連接成功后,第一個問題肯定是“我現在在什么場景?”。通過之前Dedicated Server情況的分析可知,Server會在Client連接成功后、OnServerConnect()之前通過MsgType.Scene通知客戶端切換場景。
所以此時之后,Client將進行場景加載并成功。
BattleStarter.Awake()(場景中本就有的GameObject)
BattleStarter (NewBorn.BattleStarter).Awake()GlobalObject.OnLevelWasLoaded()(加載場景前就DontDestroyOnLoad的GameObject)
GlobalObject (MoreFun.GlobalObjectComponent).OnLevelWasLoaded()NetworkManager.OnClientConnect()
public void OnClientConnect(Networking.NetworkConnection conn);
Parameters
conn?Connection to the server.
Description
Called on the client when connected to a server.
The default implementation of this function sets the client as ready and adds a player.
當場景加載成功后,才在NetworkManager.FinishLoadScene()里調用OnClientConnect()。
在OnClientConnect()里,當沒有OnlineScene或當前就是OnlineScene時,就會立刻調用ClientScene.Ready()告訴Server本Client已準備好。
所以此時的isReady是False。
hostId: 0 connectionId: 1 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnClientConnect()
NetworkManager.OnClientSceneChanged()
public void OnClientSceneChanged(Networking.NetworkConnection conn);
Parameters
conn?The network connection that the scene change message arrived on.
Description
Called on clients when a scene has completed loaded, when the scene load was initiated by the server.
Scene changes can cause player objects to be destroyed. The default implementation of OnClientSceneChanged in the NetworkManager is to add a player object for the connection if no player object exists.
當場景加載成功后、調用OnClientConnect()后、在NetworkManager.FinishLoadScene()里繼續調用OnClientSceneChanged()。
OnClientSceneChanged()必然會調用ClientScene.Ready()告訴Server本Client已準備好。所以根據之前的Dedicated Server情況分析可知,Server會在OnServerReady()里會調用NetworkServer.SetClientReady(),進行該Client的可見性檢測、并進行已在Server的NetworkIdentity的GameObject進行反序列化和Spawn。
然后,如果NetworkManager配置成AutoCreatePlayer為true,則OnClientSceneChanged()還會在本Client找不到LocalPlayer時調用ClientScene.AddPlayer(0)通知Server生成本Client的玩家。
hostId: 0 connectionId: 1 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnClientSceneChanged()
其他Player的GameObject的其他腳本的Awake()
因此,其他Player就會比LocalPlayer先行在本Client被Spawn出來。
go.instanceID=-64522
NBPlayer(Clone) (NewBorn.PlayerController).Awake()
BattleStarter.OnStartClient()
本身就在場景里的有NetworkIdentity的GameObject也比LocalPlayer先行被Spawn出來。
至此,Remote Client的Client本身就初始化好了。
接下來,由于ClientScene.OnObjectSpawn()監聽了MsgType.ObjectSpawn,所以當Server生成本Client的LocalPlayer(或者Spawn其他任意GameObject時),本Client都會進入NetworkIdentity/NetworkBehaviour的函數流程。
Remote Client的Player初始化階段
NetworkBehaviour.Awake()
只有localPlayerAuthority=True這個UNet配置變量是合法的。其他UNet變量都是非法的。
go.instanceID=-65642,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Awake()
NetworkBehaviour.OnEnable()
go.instanceID=-65642,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnEnable()
NetworkBehaviour.OnDeserialize()
public void OnDeserialize([Networking.NetworkReader reader, bool initialState);
Parameters
reader?Reader to read from the stream.
initialState?True if being sent initial state.
Description
Virtual function to override to receive custom serialization data.
Server把這個新NetworkIdentity的GameObject的所有同步屬性進行序列化會連同發送MsgType.Spawn一并下發。
所以Client接受Server的數據后也通過OnDeserialize()在本地進行反序列化。留意到這是第一次反序列化initialState=True。
OnSerialize()和OnDeserialize()
是用于自定義NetworkBehaviour中變量的序列化和反序列化的虛函數。前者必然是只在Server被調用、后者必然是只在Client被調用。
事實上,[SyncVar]修飾的變量和SyncList變量都是通過編譯時UNet將這些變量的序列化反序列化邏輯自動生成在OnSerialize()和OnDeserialize()中的。
所以要注意,如果你在NetworkBehaviour中顯式override掉了這兩個函數,則該NetworkBehaviour的[SyncVar]修飾的變量和SyncList變量都需要你自行編寫代碼實現序列化反序列化。
deserializeCount=1, reader=NetBuf sz:87 pos:87, initialState=True,
go.instanceID=-65642,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnDeserialize()
NetworkBehaviour.PreStartClient()
public void PreStartClient();
Description
An internal method called on client objects to resolve GameObject references.
留意到經過上一步的OnDeserialize()之后,合法變量為
- netId=7。
- isClient=True。
- isServer=False。
- gameObject.name已經改變為跟server所給予的名字(NBPlayer7)。
- 事實上,所有SyncVar此時皆已合法。
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).PreStartClient()
NetworkBehaviour.OnStartClient()
public void OnStartClient();
Description
Called on every NetworkBehaviour when it is activated on a client.
Objects on the host have this function called, as there is a local client on the host. The values of SyncVars on object are guaranteed to be initialized correctly with the latest state from the server when this function is called on the client.
和PreStartClient()沒什么區別。當然,SyncVar已經合法。
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartClient()
NetworkBehaviour.OnStartLocalPlayer()
public void OnStartLocalPlayer();
Description
Called when the local player object has been set up.
This happens after OnStartClient(), as it is triggered by an ownership message from the server. This is an appropriate place to activate components or functionality that should only be active for the local player, such as cameras and input.
在一個Client運行時中,只有一個Connection,有很多個Player。眾多Player中,只有和這個Connection綁定起來的Player,才“提拔為”LocalPlayer,代表的是本Client玩家的“MyPlayer”。所以,此時合法的變量就比較好理解了。
合法變量:
- playerControllerId=0
- connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
- isLocalPlayer=True
Server發送MsgType.Owner給Client,然后Client就進行LocalPlayer的更新。
注意,從源碼看來。LocalPlayer可以有多個。
另,應注意,LocalPlayer的“Local”,和Host模式下的LocalClient的“Local”可不是同一個概念,應分清區別:
- LocalClient的Local可理解為“同機器的”:是Host模式下,和Server同處于一部物理機器上的一種特殊的Client。LocalClient的并列相反概念是RemoteClient,RemoteClient是指和Server處于不同物理機器上的常見Client。
- LocalPlayer的Local可理解為“我自己的”,不管是RemoteClient,還是LocalClient,它們都會有本客戶端的自己的LocalPlayer。LocalPlayer的并列相反概念是DumbPlayer(作者本人喜歡的叫法),DumbPlayer是指本客戶端代表其他玩家的Player。
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartLocalPlayer()
NetworkBehaviour.OnStartAuthority()
public void OnStartAuthority();
Description
This is invoked on behaviours that have authority, based on context and the LocalPlayerAuthority value on the NetworkIdentity.
This is called after OnStartServer and OnStartClient.When NetworkIdentity.AssignClientAuthority() is called on the server, this will be called on the client that owns the object. When an object is spawned with NetworkServer.SpawnWithClientAuthority(), this will be called on the client that owns the object.
當一個NetworkIdentity配置有LocalPlayerAuthority時,此NetworkIdentity認為是可以授權給Client的。只有一個Client真正有Authority的時候,才可以在該GameObject的NetworkBehaviour中發送Command給Server。
什么時候Client才真正有Authority呢?LocalPlayer都是有Authority的。另自Unity5.2開始,也允許非Player在運行時通過在Server調用NetworkIdentity.AssignClientAuthority()或NetworkServer.SpawnWithClientAuthority()將Authority賦予特定的Connection,即指定該Connection的Client也擁有該非Player的Authority,即允許該Client也可以在該非Player的NetworkBehaviour中發Command給Server。此時OnStartAuthority()這個函數是可以在那個時候再被調用到的。
Authority要么只有Server擁有,要么只有Client擁有。
合法變量:
- hasAuthority=True
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartAuthority()
NetworkBehaviour.Start()
一幀真正開始。
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).Start()
至此,NetworkBehaviour的初始化階段已結束。接下來是正常運轉階段。
Remote Client的Player運轉階段
NetworkBehaviour.FixedUpdate()(多次)
fixedUpdateCount=15, go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).FixedUpdate()
NetworkBehaviour.Update()(多次)
updateCount=4, go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).Update()
NetworkBehaviour.OnDeserialize()(多次)
deserializeCount=5, reader=NetBuf sz:28 pos:28, initialState=False, go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDeserialize()
Remote Client的Player銷毀階段
通過調用NetworkServer.Destroy(gameObject);,gameObject進入銷毀階段。
NetworkBehaviour.OnNetworkDestroy()
public void OnNetworkDestroy();
Description
This is invoked on clients when the server has caused this object to be destroyed.
This can be used as a hook to invoke effects or do client specific cleanup.
留意只有客戶端才會被調用OnNetworkDestroy()。
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnNetworkDestroy()
NetworkBehaviour.OnDisable()
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDisable()
NetworkBehaviour.OnDestroy()
go.instanceID=-65642,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=
connectionToServer=hostId: 0 connectionId: 1 isReady: True channel count: 2
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=True
isLocalPlayer=True
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDestroy()
Remote Client的Client銷毀階段
通過點擊NetworkManagerHUD的Stop按鈕,停止Remote Client。
NetworkManager.OnStopHost()
public void OnStopHost();
Description
This hook is called when a host is stopped.
NetworkManagerHUD有bug。Remote Client情況點擊它也調用StopHost(),所以OnStopHost()也會被錯誤地調用。
正式情況應忽略。
NetworkManager.OnStopClient()
public void OnStopClient();
Description
This hook is called when a client is stopped.
在NetworkManager.StopClient()調用時,先調用NetworkManager.OnStopClient(),然后再斷連接、清除GameObject、跳轉到OfflineScene。
NetworkManager (NewBorn.NBNetworkManager).OnStopClient()至此,Remote Client整個流程結束。
Host情況
在已有上面Dedicated Server、Remote Client的情況,Host情況也將適當從略。
Host初始化階段
NetworkManager.Start()
NetworkManager (NewBorn.NBNetworkManager).Start()NetworkManager.StartHost()
public Networking.NetworkClient StartHost();
Returns
NetworkClient?The client object created - this is a "local client".
Description
This starts a network "host" - a server and client in the same application.
The client returned from StartHost() is a special "local" client that communicates to the in-process server using a message queue instead of the real network. But in almost all other cases, it can be treated as a normal client.
NetworkManager.OnStartHost()
NetworkManager (NewBorn.NBNetworkManager).OnStartHost()NetworkManager.OnStartServer()
NetworkManager (NewBorn.NBNetworkManager).OnStartServer()NetworkManager.ServerChangeScene()
Battle_Demo_Official
NetworkManager (NewBorn.NBNetworkManager).ServerChangeScene()
NetworkManager.OnServerConnect()(2次)(LocalClient混雜進來的Server函數)
hostId: -1 connectionId: 0 isReady: False channel count: 0
NetworkManager (NewBorn.NBNetworkManager).OnServerConnect()
NetworkManager.OnStartClient()(LocalClient混雜進來的Client函數)
UnityEngine.Networking.LocalClient
NetworkManager (NewBorn.NBNetworkManager).OnStartClient()
NetworkManager.OnClientConnect()(LocalClient混雜進來的Client函數)
hostId: -1 connectionId: 0 isReady: False channel count: 0
NetworkManager (NewBorn.NBNetworkManager).OnClientConnect()
NetworkManager.OnServerSceneChanged()
Battle_Demo_Official
NetworkManager (NewBorn.NBNetworkManager).OnServerSceneChanged()
NetworkManager.OnClientSceneChanged()(LocalClient混雜進來的Client函數)
hostId: -1 connectionId: 0 isReady: False channel count: 0
NetworkManager (NewBorn.NBNetworkManager).OnClientSceneChanged()
NetworkManager.OnServerReady()(LocalClient混雜進來的Server函數)
hostId: -1 connectionId: 0 isReady: False channel count: 0
NetworkManager (NewBorn.NBNetworkManager).OnServerReady()
NetworkManager.OnServerAddPlayer()(LocalClient混雜進來的Server函數)
hostId: -1 connectionId: 0 isReady: True channel count: 0, 0
NetworkManager (NewBorn.NBNetworkManager).OnServerAddPlayer()
至此,Host的Server初始化邏輯(混雜著LocalClient的初始化邏輯)結束。
進入Player初始化階段。
Host情況的Player初始化階段
NetworkManager.OnServerConnect()
hostId: 0 connectionId: 1 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnServerConnect()
NetworkManager.OnServerReady()
hostId: 0 connectionId: 1 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnServerReady()
NetworkManager.OnServerAddPlayer()
hostId: 0 connectionId: 1 isReady: True channel count: 2, 0
NetworkManager (NewBorn.NBNetworkManager).OnServerAddPlayer()
至此,開始Player的Prefab在Host已被Instantiate出來。正式開始NetworkIdentity/NetworkBehaviour的函數流程。
NetworkBehaviour.Awake()
go.instanceID=-78256,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).Awake()
NetworkBehaviour.OnEnable()
go.instanceID=-78256,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=0
playerControllerId=-1
connectionToClient=
connectionToServer=
isClient=False
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnEnable()
NetworkBehaviour.OnStartServer()
合法變量:
- netId=2。由于是Host,所以不需調用OnDeserialize()。立刻確定了netId。
- isServer=True
go.instanceID=-78256,go=NBPlayer(Clone) (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=False
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer(Clone) (MoreFun.TestNetworkBehaviour).OnStartServer()
NetworkBehaviour.PreStartClient()
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).PreStartClient()
NetworkBehaviour.OnStartClient()
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnStartClient()
NetworkBehaviour.OnRebuildObservers()
observers=System.Collections.Generic.HashSet`1[UnityEngine.Networking.NetworkConnection], initialize=True,
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnRebuildObservers()
NetworkBehaviour.OnSerialize()(多次)
serializeCount=1, writer=UnityEngine.Networking.NetworkWriter, initialState=True, go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnSerialize()
NetworkBehaviour.OnSetLocalVisibility()
public void OnSetLocalVisibility(bool vis);
Parameters
vis?New visibility state.
Description
Callback used by the visibility system for objects on a host.
Objects on a host (with a local client) cannot be disabled or destroyed when they are not visibile to the local client. So this function is called to allow custom code to hide these objects. A typical implementation will disable renderer components on the object. This is only called on local clients on a host.
vis=True, go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnSetLocalVisibility()
NetworkBehaviour.Start()
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).Start()
至此,Host情況的NetworkIdentity/NetworkBehaviour的初始化階段已結束。接下來是正常運轉階段。
Host情況的Player運轉階段
NetworkBehaviour.FixedUpdate()(多次)
fixedUpdateCount=5,
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).FixedUpdate()
NetworkBehaviour.Update()(多次)
updateCount=4,
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).Update()
NetworkBehaviour.OnSerialize()(多次)
serializeCount=4, writer=UnityEngine.Networking.NetworkWriter, initialState=False, go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnSerialize()
Host情況的Player銷毀階段
通過調用NetworkServer.Destroy(gameObject);,gameObject進入銷毀階段。
NetworkBehaviour.OnNetworkDestroy()
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=True
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnNetworkDestroy()
NetworkBehaviour.OnDisable()
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=7
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDisable()
NetworkBehaviour.OnDestroy()
go.instanceID=-78256,go=NBPlayer7 (UnityEngine.GameObject)
netId=0
playerControllerId=0
connectionToClient=hostId: 0 connectionId: 1 isReady: True channel count: 2
connectionToServer=
isClient=True
isServer=False
localPlayerAuthority=True
hasAuthority=False
isLocalPlayer=False
NBPlayer7 (MoreFun.TestNetworkBehaviour).OnDestroy()
NetworkManager.OnServerDisconnect()
hostId: 0 connectionId: 1 isReady: False channel count: 2
NetworkManager (NewBorn.NBNetworkManager).OnServerDisconnect()
至此,Host情況的Player流程結束。
Host銷毀流程
NetworkManager.OnStopHost()
NetworkManager (NewBorn.NBNetworkManager).OnStopHost()NetworkManager.OnStopServer()
NetworkManager (NewBorn.NBNetworkManager).OnStopServer()NetworkManager.ServerChangeScene()
BattleOffline
NetworkManager (NewBorn.NBNetworkManager).ServerChangeScene()
NetworkManager.OnStopClient()
NetworkManager (NewBorn.NBNetworkManager).OnStopClient()至此,Host情況全部流程結束。
轉自http://www.jianshu.com/p/63bb685a36d6
總結
- 上一篇: 表达式类型出错
- 下一篇: socket网络编程udp