UE Multiplayer

Taegang Yun·2023년 7월 31일
0

UE5 Documentation

목록 보기
5/5

RepNotify로 플레이어 체력 리플리케이트

플레이어에게는 게임플레이 도중 데미지를 입을 수 있는 체력 값이 필요하다. 이 값은 리플리케이트 해야하며, 모든 클라이언트는 각 플레이어의 체력에 대한 정보를 동기화했다.

ThirdPersonMPCharacter.h

protected:
	
    UPROPERTY(EditDefaultsOnly, Category = "Health")
    float MaxHealth;
    
    UPROPERTY(ReplicatedUsing = OnRep_CurrentHealth)
    float CurrentHealth;
    
    UFUNCTION()
    void OnRep_CurrentHealth();

MaxHealth는 리플리케이트되지 않으며 디폴트만 편집 가능하다.
CurrentHealth는 리플리케이트지만, 블루프린트에서 편집하거나 액세스할 수 없다.
MaxHealthCurrentHealth는 모두 protected되어 외부 C++ 클ㄹ래스로부터의 액세스를 방지한다.

Replicated 지정자는 서버에서 액터의 사본을 활성화하여 변수 값이 변경될 때마다 연결된 모든 클라이언트에 해당 변수 값을 리플리케이트한다. ReplicatedUsing도 똑같은 작업을 수행하지만 RepNotify 함수를 설정하도록 지원한다.
이 함수는 클라이언트가 리플리케이트된 데이터를 성공적으로 수신할 때 트리거된다.
OnRep_CurrentHealth를 사용하여 이 변수의 변경을 기반으로 각 클라이언트에 업데이트를 수행

  1. ThirdPersonMPCharactetr.cpp를 열고 상단에 다음 구문을 추가한다.
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
  1. 생성자 하단에 다음 코드를 추가한다.
MaxHealth = 100.0f;
CurrentHealth = MaxHealth;

이렇게 하면 플레이어의 체력이 초기화된다. 이 캐릭터의 새 사본이 생성될 때마다 현재 체력이 최대 체력 값으로 설정된다.

  1. ThirdPersonMPCharacter.h 에서 다음 public 함수 선언을 생성자 바로 뒤에 추가한다.
void GetLifttimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
  1. ThirdPersonCharacter.cpp 에서 이 함수에 다음 구현을 추가한다.
	void AThirdPersonMPCharacter::GetLifetimeReplicatedProps(TArray 	<FLifetimeProperty>& OutLifetimeProps) const
    {
        Super::GetLifetimeReplicatedProps(OutLifetimeProps);

        //현재 체력 리플리케이트
        DOREPLIFETIME(AThirdPersonMPCharacter, CurrentHealth);

GetLifetimeReplicatedProps 함수는 Replicated 지정자로 지정된 모든 프로퍼티를 리플리케이트하며, 프로퍼티의 리플리케이트 방식을 구성하도록 지원한다. 여기서는 가장 기본적인 CurrentHealth 구현을 사용한다. 리플리케이트가 필요한 프로퍼티를 추가할 때는 반드시 이 함수도 추가해야 한다.

GetLifetimeReplicatedPropsSuper 버전을 호출해야한다.

  1. ThirdPerspnMPCharacter.h에서 Protected 아래 다음의 함수 선언을 추가
protected:
    void OnHealthUpdate();
  1. 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);
    }
}
  1. ThirdPersonMPCharacter.cpp 에서 OnRep_CurrentHealth에 다음 구현 추가.
void AThirdPersonMPCharacter::OnRep_CurrentHealth()
{
	OnHealthUpdate();
}

플레이어가 데미지에 반응하게 만들기

플레이어의 체력을 구현했으니, 이 클래스 밖에서 플레이어의 체력을 수정할 방법을 만들어야 한다.

  1. 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
    

GetMaxHealthGetCurrentHealth 함수는 C++와 블루프린트에서 AThirdPersonMPCharacter 밖의 플레이어 체력 값에 액세스할 수 있는 게터를 제공한다. const함수를 사용하면 이 값의 수정을 허용하지 않으면서 안전하게 가져올 수 있다.

  1. 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를 수신하지 않기 때문에 필요하다.

  1. 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 함수를 호출
  • TakeDamageSetCurrenthealth를 호출하여 서버에서 플레이어의 현재 체력 값을 변경.
  • SetCurrentHealth가 서버에서 OnHealthUpdate를 호출하여 플레이어의 체력 변경에 반응하는 모든 함수 기능을 실행
  • CurrentHealth가 해당 캐릭터와 연결된 모든 클라이언트의 사본에 리플리케이트됨.
  • 각 클라이언트는 새 CurrentHealth 값을 서버로부터 받으면 OnRep_CurrentHealth를 호출.
  • OnRep_CurrentHealthOnHealthUpdate를 호출하여 각 클라이언트가 동일한 방식으로 새 CurrentHealth 값에 반응하게 함.
profile
언젠간 전문가가 되겠지

0개의 댓글