
?如果你在測驗的時候發現Client和Server中的子彈沒有同步,or你扔一枚炸彈卻聽到重疊的爆炸聲…
撞了空氣?
剛剛接觸網路同步的時候,很有可能會出現這樣的現象,開了一個Listen Server,然后在Client中跑來跑去,撞撞箱子,打打箱子…
然后發現人物撞著撞著撞不到箱子了,像是被什么看不見的東西擋住了,然后試著開槍打,發現箱子被你打飛了,但是在Server中箱子竟然還在原地,
這是因為你的拋射物子彈沒有同步到Server,Server并不知道你用projectile將箱子打飛了,這時,你需要使用遠程程序呼叫(RPC)進行Client和Server之間的通信,
RPC的宣告有三種:
- 將某個函式宣告為在服務器上呼叫,但在客戶端執行
- 將某個函式宣告為在客戶端上呼叫,但在服務器執行
- 從服務器呼叫,在服務器和當前所有連接的n個客戶端上執行(共n+1)
分別對應在函式宣告前添加
UFUNCTION(Client)
UFUNCTION(Server)
UFUNCTION(NetMulticast)
RPC默認為不可靠,如果要在遠端保證呼叫,則添加關鍵字Reliable
UFUNCTION(Server, Reliable)
如果需要添加驗證,添加關鍵字WithValidation:
UFUNCTION(Server, Reliable, WithValidation)
讓拋射物在Server同樣顯示出來
對于剛才提出的問題:Client的projectile沒有同步到Server具體該如何解決呢?
在角色頭檔案MultiplayerCharacter.h中,添加函式的Server RPC宣告,
void OnFire(); //原本的OnFire()宣告
?UFUNCTION(Server, Reliable, WithValidation)
void Server_OnFire(FVector Location, FRotator Rotation);
// bool Server_OnFire_Validate(FVector Location, FRotator Rotation);
void Server_OnFire_Implementation(FVector Location, FRotator Rotation);
在Server中生成projectile的任務交給Server_OnFire_Implementation完成,在OnFire()函式中呼叫Server_OnFire(),這樣當Client執行OnFire()時,也會通過RPC使Server同樣完成OnFire(),
void MultiplayerCharacter::OnFire()
{
if(ProjectileClass != NULL)
{
if(GetWorld())
{
// 宣告位置,旋轉,ActorSpawnParams...
// Spawn一個projectile
GetWorld()->SpawnActor<AMultiplayerProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, ActorSpawnParams)
// 在OnFire()函式中呼叫Server_OnFire()
Server_OnFire(SpawnLocation, SpawnRotation);
}
}
}
?void MultiplayerCharacter::Server_OnFire_Implementation(FVector Location, FRotator Rotation)
{
// 設定ActorSpawnParams...?
// Implementation中同樣Spawn一個projectile,在服務端顯示
GetWorld()->SpawnActor<AMultiplayerProjectile>(ProjectileClass, Location, Rotation, ActorSpawnParams)
}
但是這時編譯運行會發現,在Client中開了一槍,Server上確實是顯示出來了,但是Client中卻產生了兩個子彈,這是因為Client呼叫OnFire()時,不僅OnFire()本身會Spawn一個projectile,其中呼叫的Server_OnFire()會在Server中也同樣Spawn一個projectile,這個projectile會通過我們原本勾選的replicates復制一份回Client,
所以要記得把OnFire()原本Spawn projectile的邏輯刪掉,此任務交給Server_OnFire_Implementation(),
防止Server端呼叫Server_OnFire()
然后進行測驗會發現,不僅在Client開一槍會呼叫Server_OnFire_Implementation(),在Server開一槍,也會呼叫Server_OnFire_Implementation()…
解決這個問題的方法就是在執行Server_OnFire()之前進行判斷,判斷是在客戶端還是在服務端,如果確定是在客戶端,才繼續呼叫Server_OnFire(),

圖片來源:頁游http://www.hp91.cn/頁游
判斷方式有三種:
- 進行權威(Authority)判斷,在UE4中,對Actor的擁有權限分為三種:權威、主控、模擬;比如現有客戶端A,客戶端B,和一個服務器,服務器擁有最高權限Authority,那么對于服務器來說,其權限為“權威A”,“權威B”,對于A和B來說,它們對自己的權限為“主控”,對另一方的權限為“模擬”,據此可進行這樣的判斷,保證只有Client會呼叫到Server_OnFire():
if(!HasAuthority())
{
Server_OnFire(SpawnLocation, SpawnRotation);
}
- 利用GetWorld()->IsServer():
if(!GetWorld()->IsServer())
{
Server_OnFire(SpawnLocation, SpawnRotation);
}
- 利用Role和RemoteRole的特點,因為只有服務器能夠向已連接的客戶端同步Actor,而客戶端不能夠向服務器同步,所以只有服務器才能看到
Role ==
ROLE_Authority,并且在UE4中GetLocalRole()回傳的列舉型別中ROLE_Authority為最高值,利用此特點可進行判斷:
if(GetLocalRole() < ROLE_Authority)
{
Server_OnFire(SpawnLocation, SpawnRotation);
}
此三種方式均能區分當前執行位置為Client還是Server,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/252034.html
標籤:其他
上一篇:加一計時器——每隔1s六位數碼管顯示數字加1,直至999999,之后歸零,重新開始。
下一篇:杭電oj2021 C++
