카테고리 없음

TIL-2nd. 팀프로젝트 DreamProtector (2/21) 업무 재분배 및 몬스터 클래스 구현

view29579 2026. 9. 8. 20:08

오늘 한 일

한분이 이탈함에 따라 업무를 재조정했고, 그에 따라 몬스터 부분을 팀원분과 함께 진행하게 되었다.

 

우선 작업 진행 전 다이어그램으로 구조와 필요 기능을 정리해 보았다.

 

 

오늘은 몬스터의 공통 기능을 담당하는 BaseMonster를 만들고, 이를 상속받는 Suicide 몬스터의 기본 구조를 구현했다.


1. BaseMonster 클래스 구현

플레이어와 몬스터의 역할을 분리하면서, 몬스터마다 공통적으로 필요한 기능을 BaseMonster에서 관리하도록 구성했다.

ACharacter
    ↓
ABaseMonster
    ↓
각종 몬스터

BaseMonster에는 모든 몬스터가 공통적으로 사용할 수 있는 기능을 넣었다.

주요 스탯

  • MaxHealth : 최대 체력
  • CurrentHealth : 현재 체력
  • AttackDamage : 공격력
  • AttackInterval : 공격 간격
  • MoveSpeed : 이동 속도
  • Target : 공격 및 추적 대상

생성자에서 기본값을 설정하고, BeginPlay()에서 현재 체력을 최대 체력으로 초기화했다.

CurrentHealth = MaxHealth;

또한 CharacterMovementComponentMaxWalkSpeedMoveSpeed를 적용하여 몬스터의 이동 속도를 스탯과 연결했다.


2. 몬스터 공통 피격 및 사망 처리

몬스터가 데미지를 받았을 때의 공통 로직을 TakeDamage()에서 처리하도록 했다.

CurrentHealth -= DamageAmount;

if (CurrentHealth <= 0.0f)
{
    Die();
}

이미 죽은 몬스터가 다시 데미지를 받는 것을 방지하기 위해 체력이 0 이하라면 바로 반환하도록 했다.

if (CurrentHealth <= 0.0f) return;

사망 시에는 이동을 정지시키고 충돌을 비활성화한 뒤 액터를 제거하도록 했다.

GetCharacterMovement()->DisableMovement();
SetActorEnableCollision(false);
Destroy();

3. Attack을 공통 인터페이스로 분리

몬스터마다 공격 방식이 다르기 때문에 공격 자체를 BaseMonster에서 완전히 구현하지 않고 Attack()을 공통 인터페이스처럼 사용하도록 했다.

UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "Monster|Damage")
void Attack();

BlueprintNativeEvent를 사용하면 C++에서 기본 구현을 제공하면서 Blueprint에서도 해당 함수를 재정의할 수 있다는 점을 배웠다.

실제 C++ 구현은 _Implementation()으로 작성한다.

void ABaseMonster::Attack_Implementation()
{
    // 기본 공격 로직
}

자식 몬스터에서는 자신의 공격 방식에 맞게 Attack_Implementation()을 재정의할 수 있도록 구성했다.


4. Suicide 몬스터 생성

SuicideBaseMonster를 부모 클래스로 가지는 자식 몬스터로 만들었다.

ABaseMonster
    ↓
ASuicide

자폭 몬스터는 일반 몬스터와 달리 플레이어에게 접근한 뒤 폭발하는 전용 공격 방식을 사용하기 때문에, 자폭에 필요한 기능은 Suicide에서 담당하도록 했다.


5. Suicide 전용 Collision 구현

자폭 범위를 감지하기 위해 USphereComponent를 추가했다.

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Suicide")
TObjectPtr<USphereComponent> ExplosionCollision;

생성자에서 Sphere Collision을 생성하고 Root Component에 붙였다.

ExplosionCollision = CreateDefaultSubobject<USphereComponent>(
    TEXT("ExplosionCollision")
);

ExplosionCollision->SetupAttachment(RootComponent);

자폭 범위는 Sphere 형태로 설정하고, Pawn과 Overlap이 발생하도록 Collision 설정을 짜보았다.

ExplosionCollision->SetSphereRadius(150.0f);

ExplosionCollision->SetCollisionEnabled(
    ECollisionEnabled::QueryOnly
);

ExplosionCollision->SetCollisionResponseToAllChannels(
    ECR_Ignore
);

ExplosionCollision->SetCollisionResponseToChannel(
    ECC_Pawn,
    ECR_Overlap
);

ExplosionCollision->SetGenerateOverlapEvents(true);

이를 통해 몬스터의 기본 몸체 충돌과 별개로 자폭 전용 감지 범위를 만들었다.


오늘 배운 점

1. 부모 클래스와 자식 클래스의 역할 분리

모든 몬스터가 공통으로 사용하는 기능은 BaseMonster에서 관리하고, 몬스터마다 다른 공격 방식이나 특수 기능은 자식 클래스에서 구현하는 것이 좋다는 것을 다시금 인지했다.

BaseMonster
 ├─ 체력
 ├─ 공격력
 ├─ 이동속도
 ├─ 피격
 ├─ 사망
 └─ Attack 인터페이스

Suicide
 ├─ 자폭 Collision
 └─ 자폭 공격 구현

2. Collision도 기능별로 분리할 수 있다

ACharacter에는 기본적으로 Capsule Collision이 존재하지만, 특정 기능을 위한 별도의 Collision Component를 추가할 수 있다는 것을 배웠다.

이번에는 몸체 충돌과 자폭 감지 범위를 분리했다.

3. C++과 Blueprint의 역할 분담

이번 구조에서는 C++에서 컴포넌트와 기본적인 구조를 만들고, 이후 Blueprint에서 BeginOverlap 이벤트를 이용해 실제 폭발 연출 및 게임플레이 로직을 연결하는 방식으로 진행한다.

즉,

C++
 ↓
ExplosionCollision 생성
 ↓
Blueprint
 ↓
BeginOverlap
 ↓
폭발 처리

와 같은 구조를 사용한다.


오늘의 한 줄 정리

몬스터의 공통 기능은 BaseMonster에 모으고, 각 몬스터만의 특수한 기능은 자식 클래스에서 확장하는 구조를 만들었다.

 

내일 할 것