내일배움캠프_언리얼9기

내일배움캠프 58일차 TIL 2026/07/14

begin-play 2026. 7. 14. 23:16

스킬 시스템 및 투사체 구조 구현

1. DataTable 기반 스킬 데이터 관리 구조 이해

오늘은 스킬 정보를 코드에 직접 작성하지 않고 DataTable을 통해 관리하는 구조를 구현했다.

기존 방식:

 
Damage = 10.0f;
Speed = 500.0f;
 

처럼 코드 내부에 값을 고정하면 스킬이 많아질수록 수정이 어렵다.

개선 방식:

 
FSkillData
{
    SkillName,
    Damage,
    Cooldown,
    ProjectileClass,
    ProjectileSpeed,
    ExplosionRadius,
    DestroyType
}
 

와 같은 구조체를 만들고 DataTable(DT_SkillData)에서 데이터를 가져오는 방식으로 변경했다.

장점:

  • 디자이너가 에디터에서 수치 수정 가능
  • 새로운 스킬 추가 시 코드 수정 최소화
  • 같은 스킬 로직을 다른 데이터로 재사용 가능

2. Actor와 Component 역할 분리

스킬 구현 과정에서 기능의 역할을 분리하는 구조를 학습했다.

Skill Component

스킬 실행을 담당하는 관리자 역할

예:

PlayerSkillComponent
        |
        ↓
SkillData 검색
        |
        ↓
Skill Actor 생성
 

Skill Actor

실제 게임 월드에서 동작하는 객체

예:

PlayerProjectileSkillBase
        |
        ↓
Projectile Spawn
 

Projectile Actor

날아가는 투사체 자체

예:

PlayerProjectileBase
        |
        ↓
Collision
Movement
Hit 처리
Destroy
 

각 클래스가 하나의 역할만 담당하도록 분리하는 것이 유지보수에 중요하다는 것을 배웠다.


3. 투사체 스킬 생성 흐름 분석

오늘 구현한 투사체 생성 과정:

플레이어 입력
        ↓
PlayerSkillComponent::UseSkill()
        ↓
SkillData 검색
        ↓
SkillClass 생성
        ↓
PlayerProjectileSkillBase::ExecuteSkill()
        ↓
Projectile Spawn
        ↓
PlayerProjectileBase 동작
 

즉, 입력부터 투사체가 생성될 때까지 데이터가 전달되는 흐름을 이해했다.


4. SpawnActor를 통한 런타임 객체 생성

언리얼에서는 에디터에 배치하지 않은 객체도 런타임에서 생성할 수 있다.

사용 방식:

 
GetWorld()->SpawnActor<AActor>(
    SpawnClass,
    Location,
    Rotation
);
 

중요한 점:

SpawnActor는 클래스 정보가 필요하다.

그래서:

 
TSubclassOf<APlayerProjectileBase> ProjectileClass;
 

형태로 블루프린트 클래스 정보를 받아서 생성했다.


5. TSubclassOf와 Blueprint 연동 이해

C++에서:

 
UPROPERTY(EditAnywhere)
TSubclassOf<APlayerProjectileBase> ProjectileClass;
 

를 선언하면

블루프린트에서:

ProjectileClass
    ↓
BP_SonicVoltProjectile
 

같은 방식으로 연결 가능하다.

즉:

C++:
"투사체를 생성할 수 있는 타입을 받을 공간"

Blueprint:
"실제 생성할 투사체 지정"

역할 분리가 가능하다.


6. 투사체 초기화 패턴

생성 후 필요한 데이터를 전달하기 위해:

 
InitializeProjectile(Direction);
 

같은 초기화 함수를 사용하는 방식을 배웠다.

생성자에서는:

  • 컴포넌트 생성
  • 기본 설정

초기화 함수에서는:

  • 방향
  • 속도
  • 데미지
  • 이동 정보

같은 런타임 데이터를 전달한다.

구조:

SpawnActor()
      |
      ↓
Projectile 생성
      |
      ↓
InitializeProjectile()
      |
      ↓
Movement 시작
 

7. Collision 문제 해결 과정

투사체 테스트 중 발생했던 문제:

Stack Overflow
Chaos Collision Error
 

원인은 코드가 아니라 충돌 설정이었다.

몬스터 Blueprint의 Static Mesh가:

Collision Preset = BlockAllDynamic
 

으로 설정되어 있어서 물리 충돌 계산이 과도하게 발생했다.

해결:

Static Mesh
    Collision
        ↓
    NoCollision
 

으로 변경.

배운 점:

언리얼 오류는 항상 코드 문제가 아니라

  • Collision
  • Physics
  • Blueprint 설정
  • Actor 관계

도 함께 확인해야 한다.


8. 로그를 통한 디버깅 방법

오늘 사용한 방식:

 
UE_LOG(LogTemp, Warning, TEXT("Projectile Spawn Success"));
 

로그를 통해 실행 흐름 확인.

확인했던 내용:

ProjectileClass NULL
 

→ 생성할 투사체 클래스가 지정되지 않음

Distance Mode
 

→ 투사체 제거 조건 로직 실행 확인

Projectile Data Damage: 10
Speed: 500
 

→ DataTable 데이터 정상 전달 확인

로그는 단순 출력이 아니라
데이터가 어느 단계까지 정상 전달되는지 확인하는 도구라는 것을 학습했다.


9. 스킬 확장 구조 설계

현재 구조:

SkillComponent
        |
        |
PlayerSkillBase
        |
        |
--------------------
|                  |
ProjectileSkill   AreaSkill
                     |
                     |
                  Explosion
 

처럼 부모 클래스를 기준으로 확장 가능하도록 설계했다.

앞으로 추가 가능한 스킬:

  • 투사체 공격
  • 장판 공격
  • 순간이동
  • 범위 공격
  • 버프 스킬

등을 같은 구조 안에서 추가할 수 있다.


오늘 배운 핵심 정리

1.

데이터(DataTable)와 로직(C++)을 분리하면 확장성이 좋아진다.

2.

Actor는 실제 동작하는 객체, Component는 기능 관리 역할로 분리한다.

3.

SpawnActor를 이용하면 런타임에 원하는 객체를 생성할 수 있다.

4.

TSubclassOf를 사용하면 C++과 Blueprint 사이에서 클래스를 연결할 수 있다.

5.

언리얼 디버깅은 코드뿐 아니라 Collision, Blueprint 설정까지 함께 확인해야 한다.

6.

좋은 구조는 새로운 기능을 추가할 때 기존 코드를 수정하지 않는 구조다.


오늘 구현 흐름 한 줄 정리

DataTable에서 스킬 데이터를 가져오고 → Skill Actor를 생성하고 → Skill Actor가 Projectile Actor를 Spawn하는 구조를 통해 확장 가능한 스킬 시스템의 기본 구조를 구현했다.