플레이어에게는 게임플레이 도중 데미지를 입을 수 있는 체력 값이 필요하다. 이 값은 리플리케이트 해야하며, 모든 클라이언트는 각 플레이어의 체력에 대한 정보를 동기화했다.
ThirdPersonMPCharacter.h
protected:
UPROPERTY(EditDefaultsOnly, Category = "Health")
float MaxHealth;
UPROPERTY(ReplicatedUsing = OnRep_CurrentHealth)
float CurrentHealth;
UFUNCTION()
void OnRep_CurrentHealth();
MaxHealth
는 리플리케이트되지 않으며 디폴트만 편집 가능하다.
CurrentHealth
는 리플리케이트지만, 블루프린트에서 편집하거나 액세스할 수 없다.
MaxHealth
와 CurrentHealth
는 모두 protected
되어 외부 C++ 클ㄹ래스로부터의 액세스를 방지한다.
Replicated
지정자는 서버에서 액터의 사본을 활성화하여 변수 값이 변경될 때마다 연결된 모든 클라이언트에 해당 변수 값을 리플리케이트한다. ReplicatedUsing
도 똑같은 작업을 수행하지만 RepNotify 함수를 설정하도록 지원한다.
이 함수는 클라이언트가 리플리케이트된 데이터를 성공적으로 수신할 때 트리거된다.
OnRep_CurrentHealth
를 사용하여 이 변수의 변경을 기반으로 각 클라이언트에 업데이트를 수행
ThirdPersonMPCharactetr.cpp
를 열고 상단에 다음 구문을 추가한다.#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
MaxHealth = 100.0f;
CurrentHealth = MaxHealth;
이렇게 하면 플레이어의 체력이 초기화된다. 이 캐릭터의 새 사본이 생성될 때마다 현재 체력이 최대 체력 값으로 설정된다.
ThirdPersonMPCharacter.h
에서 다음 public 함수 선언을 생성자 바로 뒤에 추가한다.void GetLifttimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
ThirdPersonCharacter.cpp
에서 이 함수에 다음 구현을 추가한다. void AThirdPersonMPCharacter::GetLifetimeReplicatedProps(TArray <FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
//현재 체력 리플리케이트
DOREPLIFETIME(AThirdPersonMPCharacter, CurrentHealth);
GetLifetimeReplicatedProps
함수는 Replicated
지정자로 지정된 모든 프로퍼티를 리플리케이트하며, 프로퍼티의 리플리케이트 방식을 구성하도록 지원한다. 여기서는 가장 기본적인 CurrentHealth
구현을 사용한다. 리플리케이트가 필요한 프로퍼티를 추가할 때는 반드시 이 함수도 추가해야 한다.
GetLifetimeReplicatedProps
의Super
버전을 호출해야한다.
ThirdPerspnMPCharacter.h
에서 Protected
아래 다음의 함수 선언을 추가protected:
void OnHealthUpdate();
ThirdPersonMPCharacter.cpp
에 다음 구현 추가void AThirdPersonMPCharacter::OnHealthUpdate(){
//클라이언트 전용 함수 기능
if (IsLocallyControlled())
{
FString healthMessage = FString::Printf(TEXT("You now have %f health remaining"), CurrentHealth);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, healthMessage);
if(CurrentHealth <=0)
{
FString deathMessage = FString::Printf(TEXT("You have been killed."));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, deathMessage;
}
}
//서버 전용 함수 기능
if(GetLocalRole() == ROLE_Authority)
{
FString healthMessage = FString::Printf(TEXT("%s now has %f health remaining."), *GetFName().ToString(), CurrentHealth);
}
}
ThirdPersonMPCharacter.cpp
에서 OnRep_CurrentHealth
에 다음 구현 추가.void AThirdPersonMPCharacter::OnRep_CurrentHealth()
{
OnHealthUpdate();
}
플레이어의 체력을 구현했으니, 이 클래스 밖에서 플레이어의 체력을 수정할 방법을 만들어야 한다.
ThirdPersonMPCharacter.h
에서 Public
아래 다음 함수 선언을 추가한다.public:
/* 최대 체력 게터 */
UFUNCTION(BlueprintPure, Category = "Health")
FORCEINLINE float GetMaxHealth() const { return MaxHealth; }
/* 현재 체력 게터 */
UFUNCTION(BlueprintPure, Category = "Health")
FORCEINLINE float GetCurrentHealth() const { return CurrentHealth; }
/* 현재 체력 세터. 값을 0과 MaxHealth 사이로 범위 제한하고 OnHealthUpdate를 호출. 서버에서만 호출되어야 한다.*/
UFUNCTION(BlueprintCallable, Category = "Health")
void SetCurrentHealth(float healthValue);
/* 데미지를 받는 이벤트. APawn에서 오버라이드 */
UFUNCTION(Blueprintcallable, Category = "Health)
float TakeDamage(float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) overridel
GetMaxHealth
및 GetCurrentHealth
함수는 C++와 블루프린트에서 AThirdPersonMPCharacter
밖의 플레이어 체력 값에 액세스할 수 있는 게터를 제공한다. const
함수를 사용하면 이 값의 수정을 허용하지 않으면서 안전하게 가져올 수 있다.
ThirdPersonMPCharacter.cpp
에서 SetCurrentHealth
에 다음 구현을 추가한다.void AThirdPersonMPCharacter::SetCurrentHealth(float healthValue){
if(GetLocalRole() == ROLE_Authority){
CurrentHealth = FMath::Clamp(healthValue, 0.f, MaxHealth);
OnHealthUpdate();
}
}
SetCurrentHealth
를 사용해 AThirdPersonMPCharacter
외부에서 통제된 방식으로 플레이어의 CurrentHealth
를 수정할 수 있다. 리플리케이트 되는 함수는 아니지만, 액터의 네트워크 역할이 ROLE_Authority
임을 확인하여 이 함수가 호스팅된 게임 서버에서 호출될 때만 실행되도록 제한한다. 이 제한은 CurrentHealth
를 0과 플레이어의 MaxHealth
사이 값으로 범위 제한하여 CurrentHealth
를 유효하지 않은 값으로 설정할 수 없게 한다. 또한 OnHealthUpdate
를 호출하여 서버와 클라이언트 모두 이 함수에 대해 병렬 호출을 갖게 한다. 이 부분은 서버가 RepNotify를 수신하지 않기 때문에 필요하다.
ThirdPersonMPCharacter.cpp
에서 TakeDamage
에 다음 구현을 추가한다.
float AThirdPersonMPCharacter::TakeDamage(float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
float damageApplied = CurrentHealth - DamageTaken;
SetCurrentHealth(damageApplied);
return damageApplied;
}
액터에 데미지를 적용하는 플로는 다음과 같다
CauseDamage
를 호출하고, 캐릭터는 TakeDamage
함수를 호출TakeDamage
가 SetCurrenthealth
를 호출하여 서버에서 플레이어의 현재 체력 값을 변경.SetCurrentHealth
가 서버에서 OnHealthUpdate
를 호출하여 플레이어의 체력 변경에 반응하는 모든 함수 기능을 실행CurrentHealth
가 해당 캐릭터와 연결된 모든 클라이언트의 사본에 리플리케이트됨.CurrentHealth
값을 서버로부터 받으면 OnRep_CurrentHealth
를 호출.OnRep_CurrentHealth
가 OnHealthUpdate
를 호출하여 각 클라이언트가 동일한 방식으로 새 CurrentHealth
값에 반응하게 함.