TIL - Unreal Engine Skill System 구조 설계 및 DataTable 기반 스킬 구현
1. ActorComponent를 활용한 기능 분리 설계
학습 내용
언리얼에서는 하나의 Actor 클래스에 모든 기능을 넣기보다 ActorComponent를 활용하여 기능을 분리하는 방식이 중요하다는 것을 학습했다.
기존 방식:
PlayerCharacter
└─ 스킬 사용 코드
└─ 투사체 생성 코드
└─ 데이터 검색 코드
└─ 쿨타임 관리
변경한 구조:
PlayerCharacter
└─ PlayerSkillComponent
└─ Skill Data 검색
└─ Skill 실행 요청
Skill Actor
└─ 실제 스킬 동작
Projectile Actor
└─ 투사체 이동
└─ 충돌 처리
핵심 코드 패턴
void APlayerCharacter::ActivateSkill(FName SkillID)
{
if (PlayerSkillComponent)
{
PlayerSkillComponent->UseSkillByID(SkillID);
}
}
캐릭터는 "스킬을 어떻게 실행하는지" 알 필요 없이 컴포넌트에게 요청만 한다.
장점:
- PlayerCharacter 코드가 간결해짐
- 스킬 시스템 재사용 가능
- 다른 캐릭터에도 쉽게 추가 가능
2. DataTable 기반 데이터 관리
학습 내용
스킬 정보를 코드에 직접 작성하지 않고 DataTable에서 관리하는 구조를 구현했다.
기존:
Damage = 10;
Speed = 500;
Cooldown = 3;
변경:
DT_SkillData
SkillName | Damage | Speed | ProjectileClass
---------------------------------------------
FireBall | 10 | 500 | BP_FireBall
코드는 데이터를 읽어서 사용하는 역할만 담당한다.
FindRow를 이용한 데이터 검색
사용한 패턴:
const FSkillData* SkillData =
SkillDataTable->FindRow<FSkillData>(
SkillID,
TEXT("Skill Search")
);
구조:
FindRow<찾을 구조체 타입>(
찾을 Row 이름,
디버그용 이름
)
예:
FindRow<FSkillData>("IronBody")
→ DT_SkillData에서 IronBody라는 Row 검색
3. DataTable Row Type 오류 해결
발생한 오류:
UDataTable::FindRow :
specified incorrect type for DataTable
원인:
DataTable이 가지고 있는 Row 구조체와 코드에서 검색하는 구조체가 다름.
예:
DataTable:
Row Type:
HOTRELOAD_SkillData_0
코드:
FindRow<FSkillData>()
서로 다른 타입이라 검색 실패.
해결 방법:
- DataTable Row Type 확인
- 구조체 변경 후 Hot Reload 문제가 발생하면
- 에디터 재시작
- DataTable 재생성
- 구조체 재지정
4. Skill → Projectile 구조 설계
스킬 실행 흐름:
PlayerCharacter
|
v
PlayerSkillComponent
|
v
SkillData 검색
|
v
PlayerProjectileSkillBase
|
v
SpawnActor()
|
v
PlayerProjectileBase
역할 분리:
Skill Actor
담당:
- 스킬 실행 요청
- 투사체 생성
Projectile Actor
담당:
- 이동
- 충돌
- 삭제 조건
5. 가상 함수(Virtual Function)를 활용한 확장 구조
부모 클래스:
virtual void ExecuteSkill();
자식 클래스:
override
void ExecuteSkill();
구조:
PlayerSkillBase
|
|
PlayerProjectileSkillBase
|
|
FireBallSkill
DashSkill
ExplosionSkill
부모는 공통 인터페이스 제공.
자식은 각자의 방식으로 구현.
장점:
- 새로운 스킬 추가 시 기존 코드 수정 최소화
- 다형성 활용 가능
6. Projectile 초기화 방식
생성 후 필요한 데이터를 전달하는 방식 학습.
예:
Projectile->InitializeProjectile(Direction);
투사체는 생성될 때 기본 상태만 가지고 있고,
스킬에서:
- 방향
- 데미지
- 속도
- 최대 거리
- 충돌 타입
등을 전달한다.
구조:
SpawnActor()
|
v
InitializeProjectile()
|
v
Movement 시작
7. Collision 문제 해결 경험
발생 문제:
EXCEPTION_STACK_OVERFLOW
Chaos Collision
원인:
몬스터 Static Mesh Collision이 Block 상태라서 예상하지 않은 충돌 발생.
해결:
Static Mesh Collision:
Collision Enabled
↓
No Collision
중요한 점:
언리얼에서는 Mesh Collision과 Component Collision이 별도로 존재하기 때문에 둘 다 확인해야 한다.
8. Monster Skill 시스템 설계 방향
몬스터 스킬은 AI와 분리하는 방향으로 설계.
목표 구조:
Monster
|
+-- BossSkillManagerComponent
|
|
+-- FireBallSkillComponent
+-- DashSkillComponent
+-- SlamSkillComponent
역할:
AI
담당:
- 언제 사용할지 결정
- 공격 판단
- Behavior Tree 관리
Skill Component
담당:
- 실제 공격 실행
즉:
AI
|
"FireBall 사용"
|
Skill Component 실행
9. 오늘 배운 핵심 설계 원칙
1) 데이터와 로직 분리
DataTable:
수치 관리
C++:
동작 관리
2) 책임 분리
좋은 구조:
Character
→ 요청
Component
→ 관리
Actor
→ 실행
3) 확장 가능한 구조 만들기
새로운 스킬 추가:
기존 방식:
PlayerCharacter 수정
PlayerSkillComponent 수정
Projectile 수정
개선 방식:
새 Skill 클래스 추가
DataTable Row 추가
오늘의 정리
오늘은 단순히 "투사체를 발사하는 기능"을 만든 것이 아니라,
DataTable 기반 데이터 관리 + Component 기반 기능 분리 + 상속과 가상 함수 활용을 통해 확장 가능한 스킬 시스템 구조를 설계하는 방법을 학습했다.
'내일배움캠프_언리얼9기' 카테고리의 다른 글
| 내일배움캠프 61일차 TIL 2026/07/20 (0) | 2026.07.20 |
|---|---|
| 내일배움캠프 60일차 TIL 2026/07/16 (0) | 2026.07.16 |
| 내일배움캠프 58일차 TIL 2026/07/14 (0) | 2026.07.14 |
| 내일배움캠프 56일차 TIL 2026/07/10 (0) | 2026.07.10 |
| 내일배움캠프 55일차 TIL 2026/07/09 (0) | 2026.07.09 |