World War Z와 같은 캐릭터 클래스 중심의 좀비 게임을 개발할 때 Lyra를 참고해 설계해 보려고 한다.
Lyra의 게임 장르 변경을 위한 Experience 시스템을 캐릭터 클래스 변경 중심의 시스템으로 재설계 했다.
좀비 게임이라는 장르로 고정했을 때 Lyra의 Experience 는 필요하지 않다.
그래서 CharacterDefinition 으로 바꾸고 런타임에 캐릭터 클래스를 변경할 수 있게 하는게 좋아보인다.


기존 Lyra 시스템과의 차이점

Lyra는 하나의 프로젝트에서 여러 게임 장르를 구현할 수 있도록 설계되었다. 예를 들어, 일반적인 FPS 슈터와 탑다운 뷰 게임을 Experience라는 단위로 전환할 수 있다. 하지만 장르가 고정되어 있으며, 대신 플레이어가 선택할 수 있는 다양한 캐릭터 클래스가 존재하도록 했다.

따라서 Experience → CharacterDefinition으로, 게임 장르 변경 → 캐릭터 클래스 변경
으로 개념을 전환했다.


폴더 구조

Source 폴더 (핵심 시스템)

Source 폴더에는 게임의 뼈대가 되는 핵심 시스템들을 배치한다. 이곳에는 모든 시스템이 의존하는 기본 클래스들과 게임 없이는 작동할 수 없는 필수 요소들을 포함하면 된다. 플레이어어가 캐릭터 클래스를 선택하기 전에 기본으로 적용되는 DefaultCharacterDefinition 도 포함된다.

Plugins 폴더 (독립 기능 모듈)

Plugins 폴더에는 독립적으로 개발하고 테스트할 수 있는 기능들을 배치한다. 각각 켜고 끌 수 있는 모듈형 기능들로, 예를 들어 워킹 데드의 Carl이나 Rick 같은 특정 캐릭터의 CharacterDefinition들이 여기에 들어간다 (워킹 데드를 좋아해서 등장인물로 이름을 선정해봤다). 이렇게 분리함으로써 팀 단위 개발 시 충돌을 최소화하고, 필요에 따라 특정 캐릭터 기능을 쉽게 추가하거나 제거할 수 있다.

결국 테스트에서 가장 많은 시간을 보내게 될 텐데, 이 때 모듈 단위로 로딩, 언로딩할 수 있도록 해서 직관적이며 협업에서도 모듈 단위로 분담할 수 있고, Slack 에서도 모듈 이름으로 소통해 직관적이다. 당연히 버젼 관리도 좋을 것 같다.

단점

계속 장점만 이야기 했지만 단점도 존재한다.

  • 초기 설정 비용이 정말 많이 든다. 간단한 프로토타입을 만들고 싶어도 모든 모듈 구조와 의존성을 먼저 설계해야 하므로, 개발 초기 단계에서 오버헤드가 크다고 느낀다. Lyra의 Experience 관련 매니저, 컴포넌트만 봐도 복잡하고 클래스들이 많다는 걸 알 수 있다.
  • Lyra 는 테크 데모지, 실제 프로젝트에 적용하기에는 오버 엔지니어링이라고 생각한다.
  • 다음은 의존성 문제이다. 모듈끼리 최대한 커플링을 피하는 것이 옳지만, 개발하다보면 시간에 쫓기고 마감이 닥치면 실수하게 된다. 그 이후는… 유지 보수가 점점 어려워 진다.

CharacterDefinition

이는 크게 2가지로 구성된다.

  • 플레이어의 캐릭터 정보
  • 플레이어에게 적용될 게임 시스템
    • 플레이어 or 캐릭터에게 적용되는 기본 시스템
    • 해당 플레이어 or 캐릭터 만 적용되는 시스템
DefaultCharacterDefinition

* Default Pawn Data
   * Pawn Class
   * InputConfig
      * InputAction 과 GamePlayTag 바인딩
   * AbilitySet
      * GamePlayAbility 와 GamePlayTag(InputTag) 바인딩
      * GrantedGamePlayEffect
      * GrantedGamePlayAttributes
* Default Game Feature (이 캐릭터가 로드할 기능들)
   * *전체 게임 시스템 ex)*
      * WeaponSystem (무기 관리 전체)
      * InventorySystem (인벤토리 UI + 로직)
      * QuestSystem (퀘스트 UI + 데이터)
      * ShopSystem (상점 기능 전체)

예시
CarlCharacterDefinition

* PawnData ├── Pawn Class: BP_Carl
├── Ability Set: AbilitySet_Carl (기본 능력들)
│ ├── GA_Move (이동) → InputTag.Move 
│ ├── GA_Jump (점프) → InputTag.Jump
│ ├── GA_Death (죽음 처리) 
│ ├── GA_WeaponFire (무기 발사) → InputTag.WeaponFire
│ ├── GE_IsPlayer
│ ├── CarlAttributesSet

└── Input Config: InputData_Carl
├── WASD → InputTag.Move
├── Mouse → InputTag.Weapon.Fire
├── Space → InputTag.Ability.Jump
├── 1~9 → InputTag.Weapon.Select
└── R → InputTag.Weapon.Reload

* Game Feature Actions
├── Add Component: SaveGameComponent (세이브/로드)
├── Add Component: ZombieModeComponent(기본좀비모드)
├── Add Component: DialogueComponent (컷신/대화)
├── Add Ability: GA_EagleEye (Carl 전용 Ability)
├── Add Ability: GA_SuperJump (Carl 전용 Ability)
├── Add UI: Widget_SinglePlayerHUD

Ability Set 와 Game Feature 차이는

  • AbilitySet은 "개인 능력"
  • Game Feature 는 “시스템/기능 모듈"
Character Definition 의 데이터 중심 구조의 장점은 다음과 같다.
  1. 런타임에 캐릭터 변경이 쉬워진다.
  2. 모듈화의 장점을 포함한다.
  3. 코드를 수정하지 않고도 데이터 에셋만으로 캐릭터의 특성을 조정할 수 있다

결국 유지 보수와 업데이트가 쉬워질 수 있다는 것이다.

언리얼에서 모듈화 기능의 핵심인 Game Feature 를 사용하는 이유는 다음과 같다.
  1. 메모리 효율성. Carl을 플레이할 때는 Rick 관련 기능들이 메모리에 로드되지 않는다. 불필요한 시스템들을 메모리에서 제외할 수 있다.
  2. 똑같은 얘기지만 각 기능별로 독립적으로 개발하고 테스트할 수 있어서, 팀의 여러 개발자가 동시에 작업할 때 충돌이 줄어든다. Carl 전담 개발자는 Carl 관련 시스템만, Rick 전담 개발자는 Rick 관련 시스템만 개발하는 식이 가능하다.