언리얼엔진5 공부

[TIL] Unreal Engine 5: FDamageEvent 상속을 통한 커스텀 데미지 시스템 구현

Client Side 2026. 1. 28. 16:06

1. 개요

언리얼 엔진 5의 기본 데미지 시스템(TakeDamage)은 float 데미지와 UDamageType 클래스 정보만 전달하는 한계가 있습니다. 액션 게임에서 필수적인 '공격 강도(Hit Intensity)'와 같은 가변적인 데이터를 유실 없이 피격자에게 전달하기 위해 FDamageEvent를 상속받아 커스텀 이벤트를 구축한 과정을 정리합니다.

2. 핵심 문제 해결 과정

1) 왜 UDamageType에 변수를 넣으면 안 되는가?

  • 원인: UDamageTypeClass는 클래스 정보(설계도)일 뿐입니다. 런타임에 "이번 공격은 강(Heavy)이야"라고 클래스 내부 멤버 변수를 수정하면, 해당 클래스를 공유하는 모든 공격이 영향을 받거나 데이터가 초기화됩니다.
  • 해결: 매 공격 시 생성되는 인스턴스인 FDamageEvent를 상속받아 데이터를 실어 날라야 합니다.

2) FDamageEvent 상속 및 ID 정의

FDamageEvent를 상속받을 때는 엔진이 이 구조체가 '커스텀'임을 알 수 있도록 고유한 ClassID를 부여하고 GetTypeID()를 오버라이드해야 합니다.

C++
 
// T3DamageTypes.h
USTRUCT()
struct FT3DamageEvent : public FDamageEvent
{
    GENERATED_BODY()

    EHitIntensity HitIntensity = EHitIntensity::Light; // 우리가 전달할 핵심 데이터

    FT3DamageEvent() : FDamageEvent() {}
    FT3DamageEvent(TSubclassOf<UDamageType> InDamageTypeClass) : FDamageEvent(InDamageTypeClass) {}

    static const int32 ClassID = 777; // 커스텀 이벤트 식별용
    virtual int32 GetTypeID() const override { return ClassID; }
};

3) 데이터 전달 (공격자 측)

TakeDamage를 호출할 때 기본 FDamageEvent가 아닌 우리가 만든 FT3DamageEvent를 인자로 넘깁니다.

C++
 
// T3CombatComponent.cpp
FT3DamageEvent T3DamageEvent(DamageTypeClass);
T3DamageEvent.HitIntensity = Intensity; // 이번 공격의 강도를 주입

TargetActor->TakeDamage(DamageAmount, T3DamageEvent, OwnerPC, OwnerChar);

3. 주요 트러블슈팅 

트러블 A: 피격 시 데이터 유실 (왜 자꾸 Light만 뜨는가?)

  • 증상: 공격 시 Heavy를 보냈는데 피격 로그에는 Light만 출력됨.
  • 원인: 피격 로직(ExecuteHitLogic) 내부에서 인자로 받은 Intensity를 사용하지 않고, 다시 UDamageType에서 데이터를 꺼내려 시도했기 때문입니다.
  • 해결: TakeDamage 오버라이드 함수에서 static_cast를 통해 구조체를 복원하고, 그 안의 HitIntensity를 로직 함수에 직접 전달했습니다.

트러블 B: 피격 로직 중복 실행 (로그가 두 번 찍히는 버그)

  • 증상: 한 번 맞았는데 데미지 계산과 로그가 두 번 발생함.
  • 원인: AActor::TakeDamage 오버라이드 함수에서 로직을 직접 호출하는 동시에, 엔진 내부의 OnTakeAnyDamage 델리게이트에 연결된 핸들러까지 중복 실행된 것입니다.
  • 해결: 델리게이트 핸들러(HandleTakeAnyDamage)는 비워두고, 모든 핵심 로직은 TakeDamage 오버라이드에서 직접 제어하도록 단일화했습니다.

4. 최종 구현 로직 흐름

  1. 공격자: FT3DamageEvent에 Heavy/Light 정보를 담아 TakeDamage 호출.
  2. 피격자(Actor): TakeDamage 오버라이드 함수에서 GetTypeID() == 777인지 확인.
  3. 형변환: static_cast<const FT3DamageEvent*>(&DamageEvent)를 통해 커스텀 데이터 복구.
  4. 전달: 복구된 Intensity를 CombatComponent->ExecuteHitLogic()으로 전달하여 최종 판정.