언리얼 - 엔진 19 : 캐릭터 체력, 플레이어 점수

김정환·2025년 4월 14일
0

Unreal Engine

목록 보기
20/24

PlayerState

  • 게임모드에 있는 플레이어의 데이터를 사용하는 관리자
  • UE에서 PlayerState의 용도는 주로 멀티플레이 환경에서 플레이어 간 데이터 동기화를 위해 사용됨.
    • ex) 점수, 킬/데스 카운트처럼 서버와 클라이언트 모두가 공통으로 확인해야하는 정보 저장
  • 싱글 플레이에서는 동기화 개념이 필요없기 때문에 캐릭터 클래스 자체에 점수나 체력 변수를 만들어 관리해도 됨.

체력

  • 캐릭터 클래스에서 구현할 것
  • Health : 현재 체력
  • MaxHealth : 최대 체력

UE 데미지 시스템

  • UE에는 데미지 시스템을 간편하게 사용할 수 있는 UGamePlayStatics::ApplyDamage() (데미지 입히기)와 AActor:TakeDamage() (데미지 처리) 함수가 제공됨
  • 데미지 처리 흐름 ApplyDamage() -> TakeDamage()
    1. UGamePlayStatics::ApplyDamage()
      • 공격자(또는 폭발물 등)가 데미지를 줄 대상 액터와 데미지 양, 데미지를 유발한 주체 등을 인자로 넘겨 호출
      • 내부적으로 대상 액터의 TakeDamage() 함수를 호출하려 시도
    2. AActor::TakeDamage()
      • 액터의 가상 함수.
        • 모든 액터가 기본적으로 이 함수를 갖고 있음.
      • 필요 시 자식 클래스에서 오버라이드할 수 있음.
      • 실제로 체력 감소 또는 특수한 데미지 처리 로직을 이 안에서 구현

ApplyDamage()

  • ApplyDamage() 함수를 호출해서 TakeDamage()가 실행되도록 하면 됨.
// MineItem.cpp

// 지뢰 아이템 폭발 함수
void AMineItem::Explode()
{
	TArray<AActor*> OverlappingActors;
	ExplosionCollision->GetOverlappingActors(OverlappingActors);

	for (AActor* Actor : OverlappingActors)
	{
		if (Actor && Actor->ActorHasTag("Player"))
		{
        	// 데미지를 발생시켜 Actor->TakeDamage()가 실행되도록 함
			UGameplayStatics::ApplyDamage(
				Actor,						// 데미지를 맞을 주체
				ExplosionDamage,			// 데미지 양
				nullptr,					// 데미지 유발 주체 - TakeDamge와 동일
				this,						// 데미지를 준 객체
				UDamageType::StaticClass()	// 기본 데미지
			);
		}
	}

	DestroyItem();
}
  • AActor* DamagedActor : 데미지를 받을 대상 액터
  • float BaseDamage : 데미지 양
  • AController* EventInstigator : 데미지를 유발한 주체
    • 지뢰의 경우 설치한 캐릭터나 Controller가 없어서 nullptr을 넣어줌.
  • AActor* DamageCauser : 데미지를 유발한 오브젝트/액터 (지뢰)
  • TSubclassOf<UDamageType> DamageTypeClass : 기본 데미지 유형

동작 과정

  1. 대상 액터가 존재하는 확인
  2. 대상 액터의 TakeDamage() 함수를 호출
  3. DamageType은 여러가지 파생 클래스를 만들어 물리/화염/독 등 다양한 데미지 유형을 정의할 수 있음.
  • 지금은 기본값을 사용

TakeDamage()

// Cp2Character.cpp
// 데미지 처리 함수
float Cp2Character::TakeDamage(
	float DamageAmount, 
    FDamageEvent const& DamageEvent, 
    AController* EventInstigator, 
    AActor* DamageCauser)
{
    // 기본 데미지 처리 로직 호출 (필수는 아님)
    float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

    // 체력을 데미지만큼 감소시키고, 0 이하로 떨어지지 않도록 Clamp
    Health = FMath::Clamp(Health - DamageAmount, 0.0f, MaxHealth);

    // 체력이 0 이하가 되면 사망 처리
    if (Health <= 0.0f)
    {
        OnDeath();
    }
    
    return ActualDamage;	// 실제 적용된 데미지를 반환
}
  • UE의 기본 데미지 시스템을 사용하는 대표적인 함수
  • float DamageAmount : 데미지 값
  • FDamageEvent const& DamageEvent : 데미지를 받은 유형.
    • 데미지 이벤트에 대한 추가 정보를 받을 수 있음.
    • 스킬 구현 등에 따라 속성 데미지를 주는 등등에 사용할 수 있음.
  • AController* EventInstigator : 데미지를 유발한 주체(Controller)
  • AActor* DamageCauser : 데미지를 직접적으로 발생시킨 오브젝트/액터 (무기, 폭발물 등등)
  • return float : 실제 적용된 데미지
    • 기본 로직에서는 DamageAmount와 동일한 경우가 많으나, 실제 게임 상황에 따라
      방어력, 저항 등에 의해 감소되거나 크리티컬과 같은 요소로 증폭될 수 있음.
    • 이러한 처리가 반영된 결과값을 반환할 수 있음.

GameMode와 GameState 연계

  • UE에서 GameModeGameState는 게임의 전역 정보를 유지.
  • 필요한 경우 멀티 플레이 환경에서 해당 정보를 서버와 클라이언트 간에 동기화하는 역할을 수행

GameMode

  • 게임의 규칙을 정의하고 관리
  • 어떤 캐릭터를 스폰할지, 플레이어가 사망했을 때 어떻게 처리할지 등등
  • 멀티플레이에서는 서버 전용으로 동작. (클라이언트에는 존재하지 않음)

GameState

  • 게임 플레이 전반에서 공유되어야 하는 전역 상태를 저장.
  • 기본적으로 하나의 레벨에 하나의 GameState가 존재.
    • 엔진 내부에서 데이터 동기화를 고려해 설계됨.
    • 전역 데이터 관리용으로 적합
  • 타이머(경과 시간), 스폰된 아이템의 갯수, 점수 관리 등등
  • 멀티플레이에서 서버가 관리하고 클라이언트는 이를 자동으로 동기화 받음.

  • 싱글 플레이에서도 GameState가 굳이 필요없을 것 같지만,
    전역적으로 공유되는 데이터를 한 곳에서 관리하면 유지보수가 편해짐.
  • 예시 시나리오) 현재 게임에서 스폰된 아이템이 30개를 넘으면, 더 이상 아이템을 스폰하지 않는다.
    1. GameState에 스폰된 아이템 갯수 변수를 관리
    2. 스폰이 일어날 때마다 변수를 1씩 증가하고 30을 넘어가는지 체크.
    3. 30 초과하면 GameMode 또는 스폰 시스템에서 더 이상 스폰하지 않도록 로직 적용.

이처럼 전역적으로 공유되어야 하는 데이터는 GameState에 넣고
게임 규칙이나 흐름을 제어하는 로직은 GameMode 또는 별도의 매니저 클래스로 분리하는 것이 일반적.

profile
만성피로 개발자

0개의 댓글