본문으로 건너뛰기
Unreal Engine C++ 기초집LESSON 16

Actor Component로 체력 분리

난이도초급
예상 시간50분
선수지식15강의 EnemyBase

16강. Actor Component로 체력 분리

1. 이번 강의에서 해결할 문제

플레이어와 적 클래스에 체력 감소, Clamp, 사망 판정을 각각 복사했습니다. 피해 수신과 체력 이벤트를 독립 Component로 완성해 두 Actor에서 재사용합니다.

2. 학습 목표

3. 핵심 개념

Actor Component는 World 위치 없이 Actor에 붙는 재사용 기능에 적합합니다. HealthComponent는 피해 원인이 플레이어인지 적인지 몰라도 ApplyDamage 경로를 통해 Damage 값만 받습니다. 사망 결과는 Delegate로 외부에 알립니다.

모든 기능을 Character에 몰아넣으면 적, 파괴 가능한 상자, 보스가 체력 코드를 복사하게 됩니다. Component는 “체력이 있는 대상”이라는 기능 축으로 재사용 경계를 만듭니다.

4. 단계별 실습

파일 경로: Source/TopDownSurvival/Components/SurvivalHealthComponent.h · 완성

SurvivalHealthComponent.h
#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SurvivalHealthComponent.generated.h"

DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(
FOnHealthChanged, float, CurrentHealth, float, MaxHealth);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDeath);

UCLASS(ClassGroup = "Survival", meta = (BlueprintSpawnableComponent))
class TOPDOWNSURVIVAL_API USurvivalHealthComponent : public UActorComponent
{
GENERATED_BODY()

public:
USurvivalHealthComponent();

UFUNCTION(BlueprintCallable, Category = "Health")
void ConfigureHealth(float NewMaxHealth);

UFUNCTION(BlueprintPure, Category = "Health")
float GetCurrentHealth() const { return CurrentHealth; }

UFUNCTION(BlueprintPure, Category = "Health")
float GetMaxHealth() const { return MaxHealth; }

UFUNCTION(BlueprintPure, Category = "Health")
bool IsDead() const { return CurrentHealth <= 0.0f; }

UPROPERTY(BlueprintAssignable, Category = "Health")
FOnHealthChanged OnHealthChanged;

UPROPERTY(BlueprintAssignable, Category = "Health")
FOnDeath OnDeath;

protected:
virtual void BeginPlay() override;

UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Health",
meta = (ClampMin = "1.0"))
float MaxHealth = 100.0f;

UPROPERTY(VisibleInstanceOnly, BlueprintReadOnly, Category = "Health")
float CurrentHealth = 100.0f;

private:
UFUNCTION()
void HandleOwnerDamaged(
AActor* DamagedActor,
float Damage,
const UDamageType* DamageType,
AController* InstigatedBy,
AActor* DamageCauser);

void SetCurrentHealth(float NewHealth);
};

파일 경로: Source/TopDownSurvival/Components/SurvivalHealthComponent.cpp

SurvivalHealthComponent.cpp
#include "Components/SurvivalHealthComponent.h"

#include "GameFramework/Actor.h"

USurvivalHealthComponent::USurvivalHealthComponent()
{
PrimaryComponentTick.bCanEverTick = false;
}

void USurvivalHealthComponent::BeginPlay()
{
Super::BeginPlay();
CurrentHealth = MaxHealth;

if (AActor* Owner = GetOwner())
{
Owner->OnTakeAnyDamage.AddDynamic(
this, &USurvivalHealthComponent::HandleOwnerDamaged);
}
OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
}

void USurvivalHealthComponent::ConfigureHealth(float NewMaxHealth)
{
MaxHealth = FMath::Max(1.0f, NewMaxHealth);
SetCurrentHealth(MaxHealth);
}

void USurvivalHealthComponent::HandleOwnerDamaged(
AActor* DamagedActor,
float Damage,
const UDamageType* DamageType,
AController* InstigatedBy,
AActor* DamageCauser)
{
if (Damage > 0.0f && !IsDead())
{
SetCurrentHealth(CurrentHealth - Damage);
}
}

void USurvivalHealthComponent::SetCurrentHealth(float NewHealth)
{
const float Previous = CurrentHealth;
CurrentHealth = FMath::Clamp(NewHealth, 0.0f, MaxHealth);

if (!FMath::IsNearlyEqual(Previous, CurrentHealth))
{
OnHealthChanged.Broadcast(CurrentHealth, MaxHealth);
}
if (Previous > 0.0f && IsDead())
{
OnDeath.Broadcast();
}
}

Character와 EnemyBase 생성자에 추가

각 클래스 헤더
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
TObjectPtr<USurvivalHealthComponent> HealthComponent;
각 클래스 생성자
HealthComponent = CreateDefaultSubobject<USurvivalHealthComponent>(TEXT("HealthComponent"));

5. 코드가 동작하는 이유

ApplyDamage는 대상 Actor의 OnTakeAnyDamage를 발생시키고 HealthComponent가 Owner 이벤트를 받아 체력을 변경합니다. Actor 클래스는 피해 계산을 복사하지 않습니다. CurrentHealth가 양수에서 0으로 전이할 때만 OnDeath를 Broadcast해 사망 처리가 한 번만 실행됩니다.

6. 자주 하는 실수와 해결법

7. 직접 실습

8. 이해 점검 질문 3개

9. 핵심 요약

MINI QUIZ

Actor Component로 체력 분리 미니 퀴즈

선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.

0 / 2
  1. 문제 1“Actor Component로 체력 분리” 기능을 확장하기 좋은 구조로 설명한 것은 무엇인가요?
  2. 문제 2‘BeginPlay Broadcast를 UI가 바인딩하기 전에 실행’ 상태에 관한 “Actor Component로 체력 분리” 본문의 설명으로 가장 알맞은 것은 무엇인가요?
LESSON STATUS

학습을 마쳤나요?

직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.

16강. Actor Component로 체력 분리 미완료 상태