RepNotity的作用
把變量設置成RepNotify除了像C#語言中的"屬性"一樣,提供一個改變變量時調用一個函數的機會以外,其真正重要的作用其實是應對網通同步延遲問題。
一定要牢記網絡同步是需要時間的,不像單機編程一樣,程序的執行是同步的,即時的。
每個設置成“replicated"的屬性的同步,以及RPC都是需要時間的,即使是局域網,即使是同一台機器上多個客戶端,相互直接通信都是需要時間的-雖然時間很短,但也需要考慮到。
假設一個情形:某客戶端的某個屬性P變化以后,要讓服務器執行某個函數F(這個函數需要用到屬性P作為參數)。
錯誤的做法是在改變屬性P之后立即執行服務器的函數F,因為一般情況下,P都不可能立即同步到服務器,那么函數F調用的P是較老的,未更新的數值。
正確的做法是將該屬性設置成RepNotify,在自動生成的OnRep_P函數中調用函數F,這樣做可以保證這個函數F是在屬性P同步到服務器之后才執行。
客戶端連接后都發生了什么?
情形:服務端先開啟並創建Session,客戶端開啟並尋找Session,找到之后進行連接。
實驗如圖所示:
經過這個實驗,至少摸清楚了以下幾個過程(覆蓋並不是很全,只保證這些步驟的相對時間順序是正確的):
服務端和客戶端相同的步驟(因為客戶端在未連接服務端時候,也相當於服務端,所以是等價的)
1. GameInstance的Init函數被調用
2. GameMode中的OnPostLogin被調用,相當於自身連接到自身,所以會觸發一個OnPostLogin(注意OnPostLogin是早於任何Actor的BeginPlay的)
3. PlayerController的BeginPlay被調用(這里只測試了PlayerController的,PlayerState,GameMode,GameState等其他幾個相關類的BeginPlay也應該是這個階段,只是先后順序我並沒有測試)
4. 關卡被加載
之后服務端創建了Session,客戶端查找並連接到Session,這個步驟是人為的,不是固定的
----------------------------------------------------------------------------------------------------------
在連接到Session的瞬間發生了如下事件:(這里是重點)
1. 客戶端的PlayerController被重新生成了(老的被銷毀,生成了一個新的),並傳參給GameMode的OnPostLogin事件。此時根據邏輯來說,PlayerState應該也被重新生成了。服務端的PlayerController目測應該還是之前的那個,並沒有重新生成
2. GameMode(只存在於服務端)的OnPostLogin被調用,記錄在GameState中的PlayerArray中增加了元素
3. 客戶端的地圖又被加載,而此過程中服務端的地圖並沒有發生任何變化
(注意上面2和3的步驟順序並不一定正確)
GameState和GameMode
這里需要知道,客戶端的GameMode被無情拋棄,因為只存在於服務器上。實際上客戶端"老的”GameState也被拋棄了,現在都從服務器上同步了一個新的GameState。
GameInstance
GameInstance仍存在與每個實例中,沒有發生任何改變,沒有被銷毀或者重生,也不會被網絡同步,自己的就是自己的,所以適合於記錄一些每個客戶端特定的信息。
但是從服務器端要想訪問到某個客戶端的GameInstance,需要費點周折,要使用從客戶端運行的(Run on owning Client)函數,或者利用replicated的對象,在其藍圖中加一個Swith has authority,然后在其remote后面獲取GameInstance。
總結一下:說簡單點,客戶端連接服務端以后,實際上只"帶過來"了PlayerController(還是新生成的)和GameInstance(這個很專一,自始自終沒有變,除非客戶端程序被關閉),剩下的都要重新建立,比如地圖要加載,Pawn要重新生成(在PostLogin過程中),PlayerState要重新生成,GameMode沒了,GameState也要從服務器上同步一個過來。
----------------------------------------------------------------------------------------------------------