4:虛幻引擎網絡架構:技術要點總結篇

Replication要點數組

 
 
1.simulated function 
在網絡環境中只有exec,client , simulated 函數纔在客戶端進行調用。若是一個函數沒有任何前綴,它只會在Server中進行調用。
另外,對於一個simulated function,他要麼是被另一個simulated function 調用,要麼就是在native函數中被調用才能在客戶端執行。
 
應用舉例
simulated function PostBeginPlay()
{
`log("PostBeginPlay");
NumberOne();
}
 
simulated function NumberOne()
{
`log("Number One");
NumberTwo();
}
 
function NumberTwo()
{
    `log("Number Two");
    NumberThree();
}
 
simulatedf function NumberThree()
{
     `log("Number Three");
}
 
 
Server端的運行結果:
PostBeginPlay
NumberOne
NumberTwo
NumberThree
 
Client端的運行結果:
PostBeginPlay
NumberOne
 
結論:
因爲NumberTwo()沒有simulated前綴,咱們在前邊總結到,任何沒有前綴修飾的函數只會在Server端進行調用。
 
因爲節省帶寬的須要,咱們儘量的讓客戶端和服務器端保持同樣的運行代碼,因此可使用simulated來進行這樣的工做。
 
可是咱們又不能把所有的函數都寫成simulated,由於在客戶端不能訪問到全部的Actor。(例如一些生成器Spawner Actor,即便在其Simulated PostBeginPlay中打Log都不會有任何的輸出)
 
 
2.哪些在服務器端運行,哪些在客戶端運行
咱們知道非simulated function 是必定再也不客戶端上運行的,可是當咱們在simulated function中咱們也想有一些權限,設置一些咱們不肯意在客戶端中運行的代碼段。
還有的是,咱們怎麼知道咱們當前的遊戲是徹底運行在網絡環境的。
 
Role和RemoteRole
在Actor中有一個枚舉類型ENetRole ,定義了服務器和客戶端怎麼來對待這一個Actor
Role_None
Role_SimulatedProxy
Role_AutonomousProxy
Role_Authority
 
var ENetRole Role,RemoteRole; 
 
默認情況下,在服務器端Role=Role_Authority。這是由於,在服務器端,服務器須要維護整個世界中的全部Actor,所以他有全部Actor的控制權。
 
 
RemoteRole設置咱們在客戶端如何對待這個Actor。例如在如下RemoteRole的幾個屬性設置中:
 
Role_None: 這個Actor永遠不會被複制到客戶端。例如GameInfo永遠是在Server中運行的,其RemoteRole=Role_None,遊戲中的ActorFactor或是Spawner均可以這樣設置,由於你不須要客戶端有這些內容。
 
Role_SimulatedProxy:基本上全部須要複製的Actor都是這個屬性,例如Projectile,Vehicle,全部客戶端須要預測其物理和其餘行爲的Actor。
 
Role_AutonomousProxy:只用在兩個地方,一個是client本身的Pawn,也就是玩家Pawn,另外一個是DemoRecSpectators extends UTPlayerController。該屬性和Role_SimulatedProxy比較接近,除過他們不限於simulated
 
Role_Authority:RemoteRole不能使用,該屬性表示這個actor要麼在服務器端,要麼是單人遊戲。
 
 
運行下面的代碼
simulated function PostBeginPlay()
{
`log("PostBeginPlay");
`log("Role="@Role);
`log("RemoteRole"@RemoteRole);
}
 
在服務器端的結果:
PostBeginPlay
Role=Role_Authority;
RemoteRole=Role_SimulatedProxy;
 
在客戶端的結果:
PostBeginPlay
Role=Role_SimulatedProxy
RemoteRole=Role_Authority
 
徹底是相反的結果,這是爲何呢?
服務器端如咱們預料的,可是對於客戶端徹底相反,咱們從新作一個實驗。
 
 
simulated function PostBeignPlay()
{
`log("PostBeginPlay");
if(Role==Role_Authority)
`log("I'm on Server");
else
`log("I'm on Client");
}
 
在Server端運行的話
I'm on Server
 
在客戶端的話
I'm on Server
 
由於在服務器端運行的話,Actor的Role爲Role_Authority
在客戶端Actor的Role爲Role_SimulatedProxy
 
根據這一個特性,咱們能夠劃定有一些功能在特定的端口環境運行,例如一個濺血例子不須要在服務器端運行,因此只須要判斷當前的Role<Role_Authoriy,若是不是的話就說明其在客戶端中運行。
if(Role<Role_Authority)
Spawn(Particle);      //濺血
 
有時有一些遊戲性的代碼又只須要在服務器端,咱們能夠反着來便可。
 
 
 
3.是否運行在網絡環境NetMode
在WorldInfo中有一個枚舉ENetMode
NM_Standalone,             //單機運行: 沒有服務器
NM_DedicatedServer,    //偵測服務器: 運行在一個服務器上,其餘客戶端鏈接過去。MMO
NM_ListenServer,           //監聽服務器:一個本地客戶端主持做爲服務器,Host Game,其餘玩家鏈接過來,代替了遠程機器。
NM_Client                      //客戶端: 做爲一個客戶端鏈接到服務器。
 
 
因爲是在WorldInfo中,因此咱們能夠劃定和查詢該關卡的NetMode
simulated function PostBeginPlay()
{
`log("NetMode:"@WorldInfo.NetMode);
}
 
對於服務器端
NetMode: NM_DedicatedServer
 
對於客戶端
NetMode:NM_Client
 
既然有了Role爲何還須要NetMode這種枚舉區分?
由於NetMode在服務器端有兩種:NM_DedicatedServer或NM_ListenServer,而對於Role在服務器端永遠是Role_Authority
 
那麼何時使用NetMode呢?
前面說過不讓血粒子在服務器端進行播放,可是對於NM_ListenServer服務器,像CS中的玩家主機(其實就是客戶端在作HostGame主機),固然也要播放血粒子,所以能夠用NM_ListenServer作出區別。
 
 
 
 
 
4.Replication變量複製
變量複製任何狀態下都是Reliable。
 
數組變量複製除外。static數組能夠複製,動態數組不能。除非你將其每個單獨值傳遞給中間變量,但最好不要。
 
作一個實例:
var int TestNum;
 
replication
{
 if(bNetDirty)
 TestNum;
}
 
 
simulated function PostBeginPlay()
{
    if(Role=Authority)    //在服務器端執行
    SetTimer(1,true,'ServerTest');          
    else                            //在客戶端執行
    SetTimer(1,true,'ClientTest');
}
 
function ServerTest()
{
   TestNum++;
  `log("Server:"@TestNum);
}
 
 
function ClientTest()
{
   `log("Client:"@TestNum);
}
 
 
結果分析:
在Server端遊
Server: 1
Server: 2
Server:3
...
 
 
在Client端有
Client: 1
Client: 2
Client: 3
...
 
由於Server端不斷地變化,bNetDirty==true,將會一直將值傳遞給Client
 
 
變量複製的一些關鍵詞:
NetPriority: 優先級,注意提升這個變量的優先級,複製能夠比別的變量優先,可是不能使其複製變得更快,由於還存在別的Actor變量優先級。
 
bNetDirty:變量值發生了變化。
 
bNetInitial: true,若是全部變量初始化複製完成。
 
bNetOwner: 若是本地PlayerController擁有這個變量則爲true
 
bAlwaysRelevant: 爲true的話,這個變量將會針對全部client進行更新。注意一些狀況,例如別的玩家根本看不見這個變量相關引發的內容就應該不要使其爲true,以節省帶寬。
 
bReplicateInstigator: 若是這個變量有instigator,將其複製給Clients,例如給這個actor形成傷害的pawn。
 
bReplicateMovement:複製位置和運動變量,例如velocity。
 
bSkipActorPropertyReplication:不要複製這個Actor的任何屬性
 
NetUpdateFrequency:更新複製的頻率,數值越低優先級就越低
 
if ( (!bSkipActorPropertyReplication || bNetInitial) &&
(Role==ROLE_Authority)
&& bNetDirty && bReplicateInstigator )
Instigator;
Reading it, this tells us that if we're not skipping property replication or we're still initializing,
and we're the server, and a replicated property has changed, and we want to replicate the
instigator, then replicate the Instigator variable. These replication statements can seem
confusing at first, but examining what each variable does and taking a look at other examples
in the source code will give you an understanding of when they should be used.
 
 
 
5.ReplicateEvent
在一些變量前加修飾符 Repnotify,當這個變量被Replicate的時候會引發RelicateEvent被調用。
 
實例:
結果是在服務器端:
ServerTest:1
 
在客戶端:
This Replicated Event is Working
 
 
 
6.對Actor的一些設置
通常網絡Actor的出現
若是Actor只在Server顯示而沒有在Client上存在,設置其Actor屬性
RemoteRole=Role_SimulatedProxy                     //告知客戶端如何對待Actor,大部分的Projectile和Vehicle,能夠被擊碎的箱子均可以這樣設置
bAlwaysRelevant=true                                         //這個也能夠隨着距離設置以便節省帶寬
 
RemoteRole=Role_SimulatedProxy- 告知遊戲的客戶端怎麼處理這個Actor,意思是:這個Client有一個複製於Server的本地copy表示這個Actor。
bAlwaysRelevant=true 針對於玩家這個是相關的
 
 
獲取本地PlayerController
GetALocalPlayerController()和Foreach LocalPlayerControllers是永遠失效的,所以必須使用別的遍歷方式例如DynamicActors等
 
 
 
7.GameReplicationInfo
GameInfo不存在於Client端,所以咱們建立了GameReplicationInfo來給Client端傳遞GameInfo中的信息
 
在GameInfo的DefaultProperties中有
 
defaultproperties
{
   GameReplicationInfoClass=class'ArtGameReplicationInfo'
}
 
下面舉一個示例:
若是GameInfo中有一些變量須要玩家知道,例如當前場景中還有多少敵人
var int EnemyNumLeft;
 
在ArtGame中有
ArtGameReplicationInfo(ArtGameReplicationInfo).EnemyNumLeft=EnemyNumLeft;
 
而後在class ArtGameReplication中
var int EnemyNumLeft;
 
replication
{
    if(bNetDirty)
    EnemyNumLeft;
}
 
一樣的道理,大部分在PlayerController中和Pawn中的數據咱們能夠搬到ArtPlayerReplicationInfo中去處理HUD顯示,例如玩家當前的經驗和等級
 
 
 
8.關於function的修飾符
Reliabale VS Unreliable
 
Reliable老是可靠地傳輸,基於TCP傳輸模式即便在網絡環境糟糕的狀況下也會最終以從新發送的方式到達目的地。
Unreliable基於UDP方式,雖然效率高,可是有可能會傳輸丟失,一些不重要的執行能夠用這種方式,例如濺血等。
 
 
 
client function:
當服務器想在客戶端上調用一個函數,這個函數永遠不會在服務器端而在只在客戶端執行。例如在PlayerController中有一個GivePawn函數,該函數負責給客戶端設置一個Pawn,而服務器端不關心,僅僅是對客戶端說:「嘿,這是你的責任,趕快去處理吧!」。
reliable client function GivePawn(Pawn NewPawn)
 
作一個示例:
unreliable client function PlayTeleportEffect()
{
    //在客戶端播放一個傳送效果的粒子,
}
 
固然client function只在被這個客戶端擁有的Actor上執行,什麼Actor一般被這個Client擁有呢?
通常狀況下,像PlayerController,Pawn,Weapon,Inventory,PlayerReplicationInfo等被這個client擁有
大部分在場景中放置的Actor都不被Client擁有,Spawner每每也不被client擁有。
 
作一個實例:
在場景中放置一個Actor,而後PostBeginPlay中調用
 
 
//只在客戶端執行的函數
reliable client function ClientCall()
{
  `log("Client Function Called by the Server");
}
 
發如今服務器端和客戶端都不會出現該Log.
 
再在ArtPawn和ArtPlayerController的客戶端中會出現.
 
 
 
Server function:
Server funcionn是從客戶端發送給扶我端的調用,例如客戶端會表達:「我要執行一個動做,你也得保證你在服務器端對這個事件作出對應的響應。」
例如一個exec函數只在本地客戶端執行,而服務器端不會發生相應的動做,這個時候就得通知。
 
進行一個實例:
exec function use()
{
  `log("I'm using the trigger!") ;       
}
 
在客戶端運行將會執行log,可是服務器端不會進行動做。這個時候只須要
 
exec function use()
{
   `log("I'm using the trigger!") ; 
   ServerUse();     
}
 
reliable server function ServerUse()
{
  `log("Server using the trigger");
}
 
這時候服務器端將會執行對應的動做而客戶端不會
服務器端輸出:
Server using the trigger
 
 
固然咱們也能夠在客戶端傳遞參數給服務器端:
reliable server function ServerUse(int Num)
{
  `log("Server using the trigger:"@num);
}
 
在use()中
exec function use()
{
   `log("I'm using the trigger!") ; 
   ServerUse(3);  
}
 
最後服務器端會輸出
Server using the trigger 3
相關文章
相關標籤/搜索