PART 2 · 강의 3/3

Activatable Widget

CommonUI의 핵심 위젯인 UCommonActivatableWidget의 활성화/비활성화 메커니즘, 위젯 스택, 자동 포커스 관리를 학습합니다.

01

UCommonActivatableWidget 개념

활성화 가능 위젯의 핵심 역할과 동작 원리

UCommonActivatableWidget"활성/비활성" 상태를 가지며, 활성화 시 입력 라우팅 트리에 노드로 등록되는 위젯입니다. 메뉴, 다이얼로그, 팝업 등 입력을 받아야 하는 모든 UI 화면에 사용합니다.

C++ - Activatable Widget 기본 구조
UCLASS() class USettingsMenu : public UCommonActivatableWidget { GENERATED_BODY() protected: // 위젯이 활성화될 때 호출 virtual void NativeOnActivated() override; // 위젯이 비활성화될 때 호출 virtual void NativeOnDeactivated() override; // 기본 포커스를 받을 위젯 지정 virtual UWidget* NativeGetDesiredFocusTarget() const override; UPROPERTY(meta = (BindWidget)) UCommonButtonBase* FirstOptionButton; }; void USettingsMenu::NativeOnActivated() { Super::NativeOnActivated(); // 입력 액션 등록, 애니메이션 시작 등 PlayAnimation(FadeInAnimation); // Back 액션 등록 RegisterUIActionBinding(FBindUIActionArgs( BackAction, false, FSimpleDelegate::CreateUObject(this, &USettingsMenu::HandleBack) )); } void USettingsMenu::NativeOnDeactivated() { // 등록된 액션은 자동 해제됨 Super::NativeOnDeactivated(); } UWidget* USettingsMenu::NativeGetDesiredFocusTarget() const { // 게임패드 사용 시 이 위젯에 포커스 이동 return FirstOptionButton; }
NativeGetDesiredFocusTarget

이 함수는 게임패드 사용 시 자동 포커스 대상을 지정합니다. 구현하지 않으면 CommonUI가 위젯 트리에서 포커스 가능한 첫 번째 위젯을 자동으로 찾지만, 명시적으로 지정하는 것이 의도된 UX를 보장합니다.

02

위젯 스택과 PushWidget

메뉴 스택 관리 패턴

CommonUI는 위젯 스택(Widget Stack) 개념으로 메뉴 계층을 관리합니다. PushWidget()으로 새 위젯을 스택에 추가하면 이전 위젯은 비활성화되고, DeactivateWidget()으로 현재 위젯을 제거하면 이전 위젯이 다시 활성화됩니다.

[Widget Stack]
  │
  ├─ [Main Menu] — 비활성 (스택 하단)
  ├─ [Settings] — 비활성
  └─ [Confirm Dialog]활성 (스택 최상단)

// DeactivateWidget() 호출 시:
  ├─ [Main Menu] — 비활성
  └─ [Settings]다시 활성
C++ - 위젯 스택 관리
// CommonActivatableWidgetContainerBase를 사용하여 스택 관리 UPROPERTY(meta = (BindWidget)) UCommonActivatableWidgetContainerBase* MenuStack; // 위젯을 스택에 Push void UGameUI::OpenSettings() { // PushWidget은 위젯을 생성하고 스택에 추가 // UE 5.5+: 내부적으로 ActivateWidget() 자동 호출 USettingsMenu* Settings = MenuStack->AddWidget<USettingsMenu>( SettingsMenuClass); } // 현재 최상단 위젯을 Pop (Settings에서 뒤로가기) void USettingsMenu::HandleBack() { // DeactivateWidget()은 스택에서 이 위젯을 제거 // 이전 위젯(Main Menu)이 자동으로 재활성화됨 DeactivateWidget(); } // 확인 다이얼로그 Push void USettingsMenu::ShowResetConfirm() { UConfirmDialog* Dialog = MenuStack->AddWidget<UConfirmDialog>( ConfirmDialogClass); Dialog->OnConfirmed.AddDynamic( this, &USettingsMenu::OnResetConfirmed); }
UE 5.5+ PushWidget 변경사항

UE 5.5부터 AddWidget() / PushWidget()이 위젯 활성화를 내부적으로 처리합니다. Push 후 수동으로 ActivateWidget()을 호출하면 초기화 로직이 중복 실행되어 버그가 발생할 수 있습니다. 이전 버전 코드를 마이그레이션할 때 주의하세요.

03

활성화 설정 옵션

입력 모드, 마우스 캡처, 가시성 설정

C++ - 위젯 활성화 설정
UCLASS() class UPauseMenu : public UCommonActivatableWidget { GENERATED_BODY() public: UPauseMenu() { // 활성화 시 입력 설정 // Menu 모드: 게임 입력 차단, UI만 입력 받음 SetInputMode(ECommonInputMode::Menu); // 마우스 잠금 해제 (메뉴에서 자유 이동) SetMouseCaptureMode(EMouseCaptureMode::NoCapture); // 비활성화 시 표시 상태 // Collapsed: 비활성 시 완전히 숨김 // HitTestInvisible: 표시는 하되 입력 안 받음 SetAutoActivate(false); } }; // 다른 예: 인게임 오버레이 (게임 입력도 허용) UCLASS() class UIngameOverlay : public UCommonActivatableWidget { GENERATED_BODY() public: UIngameOverlay() { // Game+UI 모드: 게임 입력도 통과시킴 SetInputMode(ECommonInputMode::All); } };
입력 모드 게임 입력 UI 입력 사용 예
ECommonInputMode::Menu 차단 허용 일시정지 메뉴, 인벤토리, 설정
ECommonInputMode::Game 허용 차단 게임플레이 전용 (UI 비활성)
ECommonInputMode::All 허용 허용 HUD, 인게임 오버레이, 빠른 슬롯
04

실전 패턴: 메뉴 시스템 구축

CommonUI로 완전한 메뉴 시스템을 구현하는 예제

C++ - 완전한 메뉴 시스템
// 루트 UI 레이아웃 위젯 UCLASS() class UGameUILayout : public UCommonUserWidget { GENERATED_BODY() protected: // 게임플레이 HUD 레이어 UPROPERTY(meta = (BindWidget)) UCommonActivatableWidgetContainerBase* GameLayer; // 메뉴 스택 레이어 UPROPERTY(meta = (BindWidget)) UCommonActivatableWidgetContainerBase* MenuLayer; // 팝업/다이얼로그 레이어 (최상위) UPROPERTY(meta = (BindWidget)) UCommonActivatableWidgetContainerBase* ModalLayer; public: // 레이어별 위젯 추가 함수 template<typename T> T* PushToGameLayer(TSubclassOf<T> WidgetClass) { return GameLayer->AddWidget<T>(WidgetClass); } template<typename T> T* PushToMenuLayer(TSubclassOf<T> WidgetClass) { return MenuLayer->AddWidget<T>(WidgetClass); } template<typename T> T* PushToModalLayer(TSubclassOf<T> WidgetClass) { return ModalLayer->AddWidget<T>(WidgetClass); } };
레이어 기반 설계

UI를 Game / Menu / Modal 레이어로 분리하면 입력 우선순위가 자연스럽게 관리됩니다. Modal 레이어에 다이얼로그가 있으면 Menu 레이어의 입력이 차단되고, Menu 레이어에 메뉴가 있으면 Game 레이어의 입력이 차단됩니다. 이 패턴은 Lyra 프로젝트에서도 사용됩니다.

SUMMARY

핵심 요약

  • UCommonActivatableWidget은 활성/비활성 상태를 가지며, 활성화 시 입력 라우팅 트리에 노드로 등록됩니다
  • NativeOnActivated()에서 입력 액션 등록, NativeGetDesiredFocusTarget()에서 자동 포커스 대상을 지정합니다
  • 위젯 스택으로 메뉴 계층을 관리하며, AddWidget()으로 Push, DeactivateWidget()으로 Pop합니다
  • ECommonInputMode로 Menu(UI 전용), Game(게임 전용), All(둘 다) 입력 모드를 설정합니다
  • Game/Menu/Modal 레이어 분리 패턴으로 입력 우선순위를 자연스럽게 관리할 수 있습니다
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: Activatable Widget 스택 메뉴 구현

UCommonActivatableWidgetContainerBase(Stack)를 사용하여 메인메뉴 -> 설정 -> 그래픽 상세설정 순으로 Push/Pop되는 메뉴 스택을 구현하세요. 뒤로가기 시 자동으로 이전 화면이 활성화되는 것을 확인합니다.

실습 2: 자동 포커스 관리 구현

GetDesiredFocusTarget()을 오버라이드하여 각 메뉴 화면이 활성화될 때 적절한 버튼에 자동 포커스가 이동하도록 구현하세요. 게임패드로 메뉴를 탐색하며 포커스 흐름이 자연스러운지 확인합니다.

심화 과제

UCommonActivatableWidgetSwitcher와 CommonUI Tab 시스템을 조합하여 '인벤토리/장비/스킬/퀘스트' 탭으로 구성된 RPG 스타일 메뉴를 구현하세요. 각 탭은 독립적인 입력 컨텍스트를 가져야 합니다.