오늘의 목표
언리얼 엔진 5 환경에서 멀티플레이 네트워크 동기화와 메모리 최적화를 고려하여, 인벤토리 시스템과 연동되는 퀵슬롯(Active Skill) 발동 시스템 아키텍처를 설계하고 구현 방향성을 확립한다.
고민과 문제 인식
1. SRP(단일 책임 원칙) vs 멀티플레이 안정성
- 초기 구상: 인벤토리(
SMInventoryComponent)와 퀵슬롯(SMQuickSlotComponent)의 책임을 분리하여 설계하려고 했다. (SRP 준수) - 문제점 인식:
- 퀵슬롯은 본질적으로 인벤토리 아이템의 '포인터(참조)' 역할을 한다.
- 컴포넌트를 분리할 경우, 멀티플레이 환경에서 "가방에서 지워짐"과 "퀵슬롯에 등록됨" 패킷 사이의 딜레이로 인해 Race Condition(경합 조건)이 발생하여 널 포인터 크래시나 아이템 복사/증발 버그가 터질 위험이 컸다.
- 해결 및 결정: * 언리얼의 멀티플레이 환경에서는 "높은 결합도"를 가지는 두 시스템을 하나의 컴포넌트(
SMInventoryComponent)로 합쳐서 응집도(Cohesion)를 높이는 것이 훨씬 안전하다.- 컴포넌트의 책임을 '가방'이 아닌 '플레이어 아이템/스킬 상태 관리자(Item Manager)'로 확장하여 한 곳에서 통제하도록 팀원과 합의했다.
2. 아이템 객체: UObject vs UStruct
- 고민: 예전 프로젝트에서는 아이템을
UObject로 설계했는데, 이번 프로젝트에서는UStruct(구조체)와DataAsset으로 설계되었다. 이 차이가 무엇일까? - 학습 내용 (UE5 Lyra 패러다임):
UObject방식은 직관적이지만, 대규모 멀티플레이에서는 가비지 컬렉션(GC) 부하와 네트워크 동기화(Replication)의 불안정성이라는 치명적인 단점이 있다.- 현재 설계인
UStruct(런타임 동적 데이터) +DataAsset(정적 메타 데이터) 방식은 에픽게임즈의 최신 '데이터 지향 설계'이다. - 깃털처럼 가볍고, 배열 동기화(Fast Array Serialization) 기술을 통해 멀티플레이에서 압도적인 성능과 안정성을 보장한다는 것을 이해했다.
🛠️ 핵심 아키텍처 설계: 퀵슬롯 발동 (Pull 방식)
기획 요구사항: "동일한 파이어볼이라도 박혀있는 젬(보석)과 레벨에 따라 데미지가 달라야 한다. 1, 2번 키로 스킬을 장착하고 좌클릭으로 발사한다."
1. 역할 분리 (Separation of Concerns)
- 캐릭터 (
SMPlayerCharacter): 입력 라우터(Router) 역할만 수행. - 인벤토리 (
SMInventoryComponent): 데미지 계산(캐싱) 및 활성화된 스킬 정보 제공. - 어빌리티 (
UGameplayAbility): 실제 발동 시 인벤토리 데이터를 읽어와서(Pull) 적용.
2. 데이터 플로우 (Data Flow)
- 장착 (키보드 1, 2): 캐릭터가
InventoryComp->SetActiveQuickSlot(Index)호출하여 활성화 슬롯 상태만 변경. - 발사 (좌클릭): 캐릭터가
InventoryComp->GetActiveQuickSlotAbilityTag()를 통해 태그를 얻고, GAS의TryActivateAbilitiesByTag를 호출. - GAS 서버 통신:
TryActivate함수가 내부적으로 Built-in RPC를 태워 클라이언트 예측과 함께 서버에 안전하게 발동 요청을 전달. - 데미지 적용 (Pull & SetByCaller): *
GA가ActivateAbility되는 순간, 인벤토리에 접근하여 활성화된 스킬의 캐싱된 데미지 합산 값(CachedSummary)을 조회.- 가져온 수치를
Assign Tag Set By Caller기능을 통해Gameplay Effect (GE)에 주입하여 적에게 정확한 데미지를 가함.
- 가져온 수치를
오늘의 회고
- 단순히 구현 가능 여부를 떠나, 멀티플레이어 환경과 엔진의 철학(Lyra 프레임워크, GAS)을 고려하며 설계하는 시야를 넓혔다.
- "모든 기능은 컴포넌트를 잘게 쪼개는 것이 정답이다"라는 고정관념에서 벗어나, 데이터의 의존성과 네트워크 트랜잭션의 안전성을 저울질하여 합치는 것이 더 나은 아키텍처(통합 상태 관리자)일 수 있음을 배웠다.
- GAS 시스템이 제공하는 "클라이언트 예측"과 "자동 RPC 통신"의 강력함을 실감했다. 앞으로도 로직은 최대한 어빌리티 내부로 격리하고, 캐릭터는 입력만 전달하는 구조를 지향해야겠다.
'프로그래밍 > Unreal Engine 5' 카테고리의 다른 글
| [Unreal Engine5] GAS의 GameplayEffectContextHandle과 GameplayEffectSpecHandle (0) | 2026.04.20 |
|---|---|
| [Unreal Engine 5] GAS_멀티플레이에서 GameplayCue가 작동하는 방식 (1) | 2026.04.17 |
| [Unreal Engine 5] 멀티플레이어 디버깅 유틸리티 & WorldContext (0) | 2026.04.06 |
| [Unreal Engine 5] Module과 Plugin의 차이점 (0) | 2026.04.02 |
| [Unreal Engine 5] 싱글플레이어 vs 멀티플레이어(DedServer) GAS 아키텍처의 결정적 차이 (0) | 2026.03.30 |