UNREAL · UI · 게임 시스템
일시정지 메뉴를 배타적 패널 시스템에 통합하기
인벤토리·월드맵은 이미 ‘한 번에 하나만 열리는’ 배타적 패널 규칙으로 돌고 있었다. 일시정지 메뉴도 별도로 만들지 않고 이 규칙에 얹었다. 문제는 닫을 때 입력 모드 복원이었다.
ESC 진입과 위젯 구조
활성 패널이 없고 퀵슬롯 휠도 안 열린 순수 게임플레이 상태에서 ESC를 일시정지 진입점으로 잡았다.
if (!ActivePanel && !bQuickSlotWheelOpen && Params.Key == EKeys::Escape)
OpenPauseMenu();URetrievePauseMenuWidget은 BP가 아니라 URetrieveGamePanelWidget을 상속한 C++ 베이스로 두고, 버튼은 BindWidgetOptional로 찾아 NativeConstruct에서 OnClicked를 프로그래매틱하게 바인딩했다.
진짜 문제 — 하드코딩된 입력 모드 복원
패널을 닫는 RemoveActivePanelImmediately()가 무조건 FInputModeGameOnly + 커서 숨김으로 복원했다. 인게임 인벤토리엔 맞지만 메뉴(UIOnly + 커서)에선 치명적이었다. 하드코딩된 복원값이 원래 ‘인게임 인벤토리’ 맥락의 가정을 그대로 박아 놨던 것이다. 그래서 현재 세션 상태로 분기하게 고쳤다.
if (SessionState == MainMenu || SessionState == Result)
UpdateInputMode(SessionState); // UIOnly, 커서 유지
else
SetInputMode(FInputModeGameOnly()); // 인게임, 커서 숨김멀티플레이에선 SetGamePaused(true)를 클라이언트 로컬 입력 필터링으로 대체했다. 한 클라이언트가 서버 전체를 멈출 순 없어서다.
배운 점
‘닫은 뒤 상태’는 상수가 아니라 맥락이다. 하드코딩된 복원값은 그 코드가 처음 쓰인 맥락의 가정을 박아 넣는다. 새 진입점(메뉴)에서 재사용하면 그 가정이 깨진다.
이미 있는 상태 머신(세션 상태)을 단일 진실로 삼아 복원값을 파생시키면, 진입점이 늘어도 각자 맞는 모드로 돌아온다.