언리얼 엔진/프로젝트

[언리얼 엔진] 팀 프로젝트 기록 04 - 전투 컴포넌트 설계하기

dhlee-dev 2026. 5. 15. 22:04

이번에는 팀 프로젝트에서 전투 로직을 구현하기 전에, 전투 관련 컴포넌트를 어떻게 나눌지 고민했던 과정을 정리해보려고 한다.

처음에는 전투 시스템 전체를 하나의 컴포넌트로 만들 수도 있다고 생각했다.
하지만 프로젝트를 진행하면서 체력, 공격력, 방어력, 무기 장착, 근접 공격,
원거리 공격, 스킬, 회피, 재장전 같은 기능들이 모두 전투에 포함된다는 것을 알게 되었다.

이 기능들을 하나의 CombatComponent에 모두 넣으면 클래스가 너무 커지고,
나중에 기능을 수정하거나 확장하기 어려울 것 같았다.

그래서 전투 시스템을 여러 컴포넌트로 나누고, 각 컴포넌트가 어떤 책임을 가져야 하는지 먼저 정리했다.


전투 컴포넌트를 나누려 한 이유

이번 프로젝트의 전투는 단순히 공격 버튼을 누르면 데미지를 주는 구조만으로 끝나지 않는다.

플레이어는 근접 공격을 할 수도 있고, 원거리 공격을 할 수도 있으며, 이후에는 스킬도 사용할 수 있다.
반대로 특정 캐릭터나 무기 구성에 따라 근접 공격만 사용하거나, 원거리 공격만 사용하는 경우도 있을 수 있다.

그래서 모든 전투 기능을 하나의 컴포넌트에 넣기보다는, 필요한 기능만 조합해서 사용할 수 있는 구조가 더 적절하다고 생각했다.

즉, 이번 설계의 방향은 다음과 같았다.

공통 수치와 자원은 StatComponent에서 관리한다.
무기 장착과 관리는 WeaponComponent에서 담당한다.
현재 전투 상태와 행동 가능 여부는 CombatComponent에서 판단한다.
근접 공격과 원거리 공격은 각각 별도의 컴포넌트에서 실행한다.

StatComponent는 이전 글에서 정리한 것처럼 체력, 공격력, 방어력, 이동속도,
스태미너처럼 전투에 필요한 수치와 자원을 관리하는 역할을 한다.

이번 글에서 더 고민한 부분은 실제 전투 행동을 담당하는 컴포넌트들을 어떻게 나눌 것인가였다.


CombatComponent 하나에 전부 넣지 않은 이유

처음에는 CombatComponent 하나에 공격, 회피, 재장전, 스킬, 무기 처리까지 모두 넣을 수도 있다고 생각했다.
하지만 이렇게 하면 CombatComponent가 전투와 관련된 거의 모든 일을 담당하게 된다.

예를 들어 근접 공격 판정, 원거리 사격, 재장전, 무기 장착, 스태미너 소모,
행동 상태 관리까지 모두 하나의 클래스에서 처리하면 나중에 기능이 늘어날수록 구조가 복잡해질 수 있다.

그래서 CombatComponent는 실제 공격을 직접 실행하기보다는,
현재 전투 상태를 관리하고 행동을 시작할 수 있는지 판단하는 역할로 두는 것이 좋다고 생각했다.

실제 근접 공격은 MeleeCombatComponent, 원거리 공격은 RangedCombatComponent,
무기 관리는 WeaponComponent가 담당하는 식으로 역할을 나누었다.

이렇게 나누면 각 컴포넌트가 맡은 역할이 비교적 명확해진다.


WeaponComponent의 역할

WeaponComponent는 전투 자체를 실행하는 컴포넌트가 아니라, 무기를 관리하는 컴포넌트로 생각했다.

처음에는 1인칭 템플릿의 무기 구조도 참고했다.
1인칭 템플릿에서는 무기 컴포넌트가 장착, 입력, 발사체 생성, 사운드, 애니메이션까지 함께 담당하고 있었다.

간단한 슈팅 게임이라면 이 구조도 괜찮지만,
우리 프로젝트는 근접 무기와 원거리 무기가 함께 존재하고, 전투 방식도 3인칭 액션에 가깝다.

그래서 무기 컴포넌트가 공격 실행까지 모두 담당하게 만들기보다는,
무기는 “무엇을 들고 있는가”를 관리하는 쪽에 가깝게 두었다.

WeaponComponent
- 무기 장착
- 무기 해제
- 현재 무기 조회
- 무기 슬롯 관리

실제 공격 흐름은 CombatComponent가 판단하고,
근접 공격이나 원거리 공격 실행은 각각의 전투 컴포넌트가 담당하는 구조로 생각했다.


CombatComponent의 역할

CombatComponent가 필요한 이유는 전투 행동의 상태를 한 곳에서 관리하기 위해서이다.

전투 중에는 단순히 공격만 하는 것이 아니라 여러 상태를 고려해야 한다.

공격 중인지
재장전 중인지
회피 중인지
피격 경직 상태인지
스킬 사용 중인지
상호작용 중인지
사망 상태인지

이런 상태 판단을 근접 공격 컴포넌트, 원거리 공격 컴포넌트,
스킬 컴포넌트가 각각 따로 하게 되면 중복이 많아지고, 서로 충돌하는 상황도 생길 수 있다.

그래서 현재 전투 행동 상태는 CombatComponent에서 관리하는 것이 좋다고 판단했다.

예를 들어 공격 중일 때는 재장전을 막고, 회피 중일 때는 공격 입력을 막는 식의 판단을 CombatComponent에서 처리하는 구조이다.

즉, CombatComponent는 “지금 어떤 행동을 할 수 있는가?”를 판단하는 컴포넌트라고 볼 수 있다.


MeleeCombatComponent와 RangedCombatComponent

근접 공격과 원거리 공격은 실행 방식이 다르기 때문에 별도의 컴포넌트로 나누는 것이 좋다고 생각했다.

근접 공격은 애니메이션, 무기 위치, 공격 판정이 중요하다. 반면 원거리 공격은 사격, 탄약, 재장전 같은 흐름이 중요하다.
두 기능을 하나의 컴포넌트에서 모두 처리하면 조건문이 많아지고, 근접 전투와 원거리 전투가 서로 섞일 수 있다.

그래서 역할을 다음처럼 나누었다.

MeleeCombatComponent
- 근접 공격 실행
- 근접 공격 애니메이션 처리
- 무기 기반 공격 판정

RangedCombatComponent
- 원거리 공격 실행
- 사격 처리
- 재장전 처리

이렇게 하면 근접 전투만 필요한 캐릭터는 MeleeCombatComponent만 사용하고,
원거리 전투가 필요한 캐릭터는 RangedCombatComponent를 함께 사용할 수 있다.

이후 스킬 기능이 필요해지면 SkillComponent를 추가해서 같은 방식으로 확장할 수 있을 것 같다.


공격력과 스태미너는 어디서 처리할까?

공격 실행은 MeleeCombatComponentRangedCombatComponent에서 하더라도,
공격력 계산이나 스태미너 소모는 한 곳에서 관리하는 것이 좋다고 생각했다.

만약 근접 공격, 원거리 공격, 스킬 컴포넌트가 각각 StatComponent를 직접 확인해서
스태미너를 소모하고 공격력을 계산한다면 중복이 많아질 수 있다.

그래서 전투 행동을 시작하기 전에 CombatComponentStatComponent를 확인하는 방향으로 생각했다.

CombatComponent
- 현재 행동이 가능한지 확인
- 필요한 스태미너가 있는지 확인
- 공격에 사용할 수치를 가져옴
- 세부 전투 컴포넌트에 실행 요청

이렇게 하면 스태미너 소모 규칙이나 공격력 계산 방식이 바뀌더라도 CombatComponent를 중심으로 관리할 수 있다.


근접 공격 판정 방향

근접 공격 판정은 처음에는 단순한 전방 범위 판정도 생각했다.

하지만 무기마다 길이와 범위가 다르고, 공격 애니메이션에 따라 실제 타격 위치도 달라질 수 있다.

그래서 근접 공격은 무기 기준으로 판정하는 방향이 더 자연스럽다고 생각했다.

예를 들어 단검은 짧은 공격 범위를 가지고, 대검은 더 긴 공격 범위를 가지는 식이다.

현재는 무기별로 Trace 시작 지점, 끝 지점, 판정 반지름을 두고, 공격 중에 해당 구간을 검사하는 방식으로 생각하고 있다.

아직 실제 구현 단계에서 세부 방식은 바뀔 수 있지만,
큰 방향은 “캐릭터 전방 고정 범위”보다는 “무기 위치와 무기 데이터 기반 판정”이 더 적절하다고 판단했다.


최종적으로 나눈 구조 정리

최종적으로 전투 관련 컴포넌트를 정리하면 다음과 같다.

WeaponComponent는 “무엇을 들고 있는가”를 관리한다.
CombatComponent는 “지금 어떤 행동을 할 수 있는가”를 판단한다.
MeleeCombatComponent와 RangedCombatComponent는 “그 행동을 실제로 어떻게 실행하는가”를 담당한다.
StatComponent는 “행동에 필요한 수치와 자원”을 관리한다.

마무리

이번 작업에서는 실제 전투 로직을 구현하기 전에, 전투 시스템을 어떤 컴포넌트들로 나눌지 먼저 고민했다.
처음에는 전투 기능을 하나의 CombatComponent에 모두 넣을 수도 있다고 생각했다.

하지만 무기 관리, 전투 상태 판단, 근접 공격 실행, 원거리 공격 실행, 수치 관리는 서로 다른 책임이라고 느꼈다.

그래서 StatComponent, WeaponComponent, CombatComponent,
MeleeCombatComponent, RangedCombatComponent처럼 역할을 나누는 방향으로 정리했다.

아직 실제 구현 과정에서 구조가 더 바뀔 수는 있지만,
지금 기준에서는 각 컴포넌트가 맡아야 할 책임이 어느 정도 명확해졌다고 생각한다.

이번 설계를 통해 전투 시스템을 구현할 때 중요한 것은 단순히 기능을 많이 넣는 것이 아니라,
각 기능의 책임을 어디까지 나눌 것인지 정하는 것이라는 점을 느꼈다.