接續昨天的部分,今天要實現傷害的部分,雖然這部分要使用到的程式碼不多但是因為牽扯到網路連線的部分會讓它變得稍嫌複雜,筆者也花了一段時間才慢慢搞懂箇中原理,這邊就來分享給大家。
首先是今天要加在標頭檔的程式碼
// NetworkDemoCharacter.h
// 在public下加入
public:
UFUNCTION(BlueprintCallable, Category = "Health")
float GetMaxHealth() const { return MaxHealth; }
UFUNCTION(BlueprintCallable, Category = "Health")
float GetCurrentHealth() const { return CurrentHealth; }
UFUNCTION(BlueprintCallable, Category = "Health")
void SetCurrentHealth(float HealthValue);
UFUNCTION(BlueprintCallable, Category = "Health")
float TakeDamage(float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
為了要運算傷害的部分換言之是與變數互動都會去寫Get/Set函式來幫助整理運算邏輯與限制更動變數的權力,這是在開發上常用的技巧。
這邊在UFUNCTION裡面都有加上BlueprintCallable的指令目的是為了能夠在BP裡面能夠呼叫此函式,例如下圖:
可以看到都在分類Health裡面有我們所宣告所有函式。
UE也很貼心的寫好了TakeDamage這個函式讓我們可以直接呼叫並且改成自己的版本,這是一個事件函式與他配合的是CauseDamage的函式,只要CauseDamage觸發就會轉換成腳色裡的TakeDamage函式觸發。
接者是在.cpp裡要添加的程式碼
// NetworkDemoCharacter.cpp
void ANetworkDemoCharacter::SetCurrentHealth(float HealthValue)
{
if(GetLocalRole() == ROLE_Authority)
{
CurrentHealth = FMath::Clamp(HealthValue, 0.f, MaxHealth);
OnHealthUpdate();
}
}
float ANetworkDemoCharacter::TakeDamage(float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
float new_health = CurrentHealth - DamageTaken;
SetCurrentHealth(new_health);
return new_health;
}
Set函式主要是讓其他物件能夠改變此物件變數的窗口,而這裡最主要的是判斷式
if(GetLocalRole() == ROLE_Authority)
這條判斷限制了要執行的邏輯必須要在伺服端上面才行,並在裡面也需要呼叫一次OnHealthUpdate()才行因為伺服端不會收到RepNotify自然不會去執行這項函式,所以必須要在這邊呼叫才能執行血量更新時要的程式碼,那麼他是怎麼實現的呢?稍後會詳細的解釋給讀者。
TakeDamage內的程式碼就如同剛剛提到過的會在CauseDamage呼叫之後執行,這裡面會把計算好的生命值在伺服端上面更新然後複製給所有的客戶端。
在官方文件中的Role & Authority中雖然寫的很詳盡但還是令人一頭霧水,所以這邊簡單的整理他們的核心重點:
/** The network role of an actor on a local/remote network context */
UENUM()
enum ENetRole
{
/** No role at all. */
ROLE_None,
/** Locally simulated proxy of this actor. */
ROLE_SimulatedProxy,
/** Locally autonomous proxy of this actor. */
ROLE_AutonomousProxy,
/** Authoritative control over the actor. */
ROLE_Authority,
ROLE_MAX,
};
從上面可以得知Authority的權限是在伺服端上面,因此上面那條判斷式就能有效的去限制呼叫的時候會在伺服端上面。
今天的流程圖如下: