티스토리 뷰
🟩 학습 목표
- 기존 애니메이션 샘플 기반의 하드코딩 변수 구조를 탈피하고 확장성 높은 Gameplay Tag 아키텍처 수립
- 기획 및 밸런싱 수정에 유연하게 대응할 수 있도록 Data-Driven Design(데이터 주도 설계) 구조 구현
- 데디케이티드 및 리슨 서버 환경에서 디싱크를 방지하는 Replication 및 Server RPC 동기화 파이프라인 구축
🟧 1. 게임플레이 태그(Gameplay Tag) 아키텍처로의 전환
🟦 복잡한 불변수(bool) 구조의 탈피
기존에 캐릭터의 상태(걷기, 뛰기, 앉기 등)를 수많은 bool 변수로 제어하던 방식을 전면 폐기하였다. 상태가 추가될수록 스파게티 코드가 양산되고 애니메이션 블루프린트(ABP)의 조건 분기문이 비대해지는 문제를 해결하기 위해, 엔진 내장 유연성이 높은 Gameplay Tag 시스템으로 교체하였다.
- CurrentGaitTag: 이동 상태 전이 관리 (Walk / Jog / Sprint)
- CurrentStanceTag: 자세 상태 전이 관리 (Stand / Crouch)
- CurrentActionTag: 단발성 이벤트 제어 관리 (Landed 등)
🟦 모션 매칭 엔진 계산 오차 제어 (Landed 버그 해결)
착지 시 모션 매칭 엔진의 프레임 계산 오차로 인해 bJustLanded 상태가 무한 루프에 빠지며 다리가 꼬이는 현상(Thrashing)이 발생하였다. 이를 해결하기 위해 착지 시 즉시 Character.Event.Landed 태그를 부여하고, C++ FTimerHandle을 연동하여 정확히 0.15초 뒤에 공백 태그로 초기화하는 지연 리셋 로직을 구현하여 버그를 제어하였다.
🟧 2. 데이터 주도 설계(Data-Driven Design) 도입
🟦 하드코딩 배제를 통한 기획 친화적 환경 구축
C++ 코드 내부에 최대 이동 속도 수치(150, 500, 800)를 직접 기입하던 하드코딩 구조를 제거하였다. 기획팀의 밸런싱 수치 변경 시 매번 소스코드를 열어 재컴파일해야 하는 메모리 비용 및 프로세스 비효율을 방지하기 위함이다.
🟦 구현 메커니즘
FTableRowBase를 상속받는 FNCPlayerMovementData 구조체를 C++ 레벨에서 선언하고, 이를 에디터와 연동하여 DT_PlayerMovementData 데이터 테이블을 구축하였다. 현재 캐릭터의 Gameplay Tag(Row Name)를 Key값으로 삼아 런타임에 데이터 테이블을 룩업(Lookup)하고, 속도와 가속도를 자동 대입하는 ApplyMovementData() 헬퍼 함수를 구현하였다.
🟧 3. 멀티플레이어 서버 동기화 (Replication & Server RPC)
🟦 러버밴딩(Rubber-banding) 디싱크 현상 해결
로컬 클라이언트 환경에서만 속도와 태그를 변경할 경우, 서버가 인지하는 자이로 물리 위치와 어긋나 캐릭터가 강제로 뒤로 튕겨 나가는 러버밴딩 현상이 발생한다. 이를 방지하기 위해 서버와 클라이언트 간의 엄격한 데이터 동기화 파이프라인을 구축하였다.
🟦 복제(Replication) 파이프라인 설계
- 상태 제어에 관여하는 태그 변수들에 Replicated 속성을 부여하고, GetLifetimeReplicatedProps 함수 내부에 DOREPLIFETIME 매크로를 선언하여 서버 ➔ 클라이언트 방향의 변수 복제 구조를 활성화하였다.
- 클라이언트의 키 입력(Input)이 트리거되면 Server_SetGait, Server_SetStance 등의 Server RPC를 호출하도록 바인딩하였다. 호출을 받은 서버 권한(Authority)이 직접 데이터 테이블을 조회하여 물리적 컴포넌트 스탯을 변경한 뒤, 이를 다시 전방 프록시(Proxy) 클라이언트들에게 복제하도록 설계하였다.
기술적 성찰: 데디케이티드 서버(Dedicated Server) 구조에 맞춰 정석대로 구현한 RPC 동기화 코드는, 방장이 클라이언트와 서버 역할을 동시에 수행하는 리슨 서버(Listen Server) 환경으로 전환하더라도 단 한 줄의 코드 수정 없이 완벽하게 상호 호환되어 작동함을 검증하였다.
🟧 4. 트러블슈팅 (Troubleshooting)
🟦 이슈 1: 앉기(Crouch) 상태 진입 시 이동 속도 변조 실패
- 원인: 데이터 테이블에서 물리 수치를 정상 추출하여 MaxWalkSpeed 변수만 갱신하고 있었다. 그러나 언리얼 엔진 캐릭터 무브먼트 컴포넌트는 캐릭터가 앉은 상태(bIsCrouched = true)일 때 내부적으로 MaxWalkSpeedCrouched 변수를 우선적으로 참조하는 구조적 특징이 누락되었다.
- 해결: ApplyMovementData() 내부 시퀀스에서 앉기 태그 감지 시 MaxWalkSpeedCrouched 변수 값도 데이터 테이블의 MovementSpeed 데이터와 1:1 일치하도록 업데이트 로직을 추가하여 걷기와 앉기 속도를 모두 150으로 제어하였다.
🟦 이슈 2: 일어서기(Stand) 동작 수행 시 애니메이션 프레임 디싱크 발생
- 원인 1: 캐릭터가 일어설 때 데이터 테이블에 매핑되지 않은 Stand 태그 자체를 Key값으로 넘겨 룩업에 실패하는 논리적 오류가 있었다. 이로 인해 속도가 초기 값인 150에 묶이는 현상이 발생하였다.
- 해결 1: 일어설 때는 독립적인 스탠스 데이터가 아닌, 현재 캐릭터의 이동 상태 태그(Walk/Jog)를 재참조하여 속도를 복구하도록 데이터 매핑 로직을 수정하였다.
- 원인 2: ToggleCrouch 함수의 서버 요청 구문에서 단순 오타성 버그가 발견되었다. 캐릭터의 자세(Stance)를 변경하는 입력 시점에 서버 측 함수를 호출하면서 실수로 Server_SetGait(CurrentStanceTag)를 넘겨주어, 서버의 가이트 데이터베이스를 오염시키고 상태 동기화를 깨뜨렸다.
- 해결 2: 잘못 연결된 RPC 구문을 원래 의도했던 Server_SetStance(CurrentStanceTag)로 정정하여 로컬과 서버 간의 패킷 데이터 불일치 문제를 해결하였다.
🟧 핵심 요약
- 상태 변수의 결합도를 낮추고 소스코드의 확장성을 확보하기 위해 Gameplay Tag 제어 아키텍처 전면 정립
- FTableRowBase와 데이터 테이블을 연동하여 런타임 컴파일 없는 Data-Driven 물리 변조 구조 완성
- Server RPC 호출을 통한 서버 측 데이터 테이블 조회 및 DOREPLIFETIME 복제 파이프라인을 구축하여 러버밴딩 현상 전면 제거
- 엔진 내부 무브먼트의 변수 참조 특성(MaxWalkSpeedCrouched)과 RPC 인자 매핑 오류를 정확히 디버깅하여 동기화의 정밀도 향상
'내일배움캠프 Unreal_7기 > 본캠프' 카테고리의 다른 글
| 인벤토리 UI 최적화, 협업 아키텍처 및 MVP 구현 전략 (1) | 2026.05.28 |
|---|---|
| 게임플레이 태그 기반 인벤토리 C++ 아키텍처 및 코어 로직 구현 (0) | 2026.05.27 |
| 언리얼 엔진 5 AI 기초 시스템 및 경로 탐색 아키텍처 정리 (1) | 2026.05.22 |
| 캐릭터 이동 메커니즘 개선 및 모션 매칭 적용 (0) | 2026.05.21 |
| 나이아가라 공격 궤적 이펙트 만들기 (0) | 2026.04.14 |

