언리얼 코딩 일기 (8) - UI Navigation

美夜·2025년 10월 16일

언리얼 코딩 일기

목록 보기
8/9

UI Navigation 이해하기

UI Navigation은 언리얼에서 UI입력을 처리하기 위해 설계된 기능입니다. UI에 포커싱되어 있으면 그 UI의 Navigation 동작을 실행합니다.

아래의 기본 동작이 미리 정의되어 있습니다.

Action키보드게임패드
UpUpGamepad_DPad_Up
DownDownGamepad_DPad_Down
LeftLeftGamepad_DPad_Left
RightRightGamepad_DPad_Right
Prev상황별로 다름
- Up
- Shift + Tab
상황별로 다름
Next상황별로 다름
- Down
- Tab
상황별로 다름
AcceptEnter
SpaceBar
Virtual_Accept
BackEscape(ESC)Virtual_Back

목록을 보면 UI안에서의 포커스 이동, 확인, 취소로 구성되어있는 것을 알 수 있습니다. 이 동작을 통해서 열려있는 UI간에 포커스를 이동하고, 확인(UButton::OnClicked) 및 취소 처리를 할 수 있습니다.

이동은 언리얼에서 미리 정의하고 계산한 정렬에 의존해서 이루어집니다. 예를들어서, GridBox에 3x3으로 버튼 9개가 있다면 4개의 방향 이동으로 버튼간 포커스 이동이 가능합니다. 그리고 서로 다른 2개의 UI가 띄워져있다면, 해당 UI의 위치에 따라 방향키 혹은 Prev/Next를 통해서 UI간 포커스 이동도 가능합니다. 여기서, Disable된 UI와 명시적으로 Navigation에서 제외하겠다고 세팅한 UI들은 제외됩니다.

입력 버그?

InputAction에서 위에서 정의된 키를 사용하는 경우, 특정 UI에 포커싱되어 있다면 Navigation이 키 이벤트를 가져가버리기 때문에 실행이 되지 않을 수 있습니다. UI동작을 위한 키와 게임 플레이를 위한 키를 완전히 분리하거나, 포커싱을 잘 조절해서 쓰거나, InputMode를 잘 변경해가면서 써서 회피할 수 있습니다.

위젯별 Navigation 동작 커스텀하기

1. 에디터

User Widget 블루프린트에서 편집이 가능합니다. Navigation세팅과 Interaction(포커스)세팅을 조정할 수 있습니다.
언리얼 에디터 내 UI 네비게이션 세팅 화면 캡처

Navigation Control OptionDescription
Escape기본 동작을 수행합니다.
공식 문서 표현으로는, "해당 방향으로 계속 이동하면서 자동으로 다음 탐색 가능한 위젯을 찾습니다."
Explicit특정 위젯으로 이동합니다.
Wrap바깥으로 포커스가 이탈하는 것을 방지합니다.
공식 문서 표현으로는, "이 컨테이너 내부에 움직임을 감싸면, 네비게이션이 탈출하려는 경우 반대쪽에서 움직임이 순환합니다."
Stop움직임을 정지합니다.
Custom사용자 코드로 처리됩니다.
CustomBoundary경계에서 포커스가 다른 UI로 탈출하려는 경우에만 사용자 코드로 처리됩니다.

포커스는 2가지 옵션으로 이루어져있습니다. 이 위젯에 포커스를 가능하게 만들 것인지, 이 위젯 대신 다른 위젯에 포커스를 가도록 만들 것인지 조정 가능합니다.

언리얼 에디터 내 UI 포커스 세팅 화면 캡처

Desired Focus Widget는 예를 들면, 아래 이미지와 같은 상황에서 사용할 수 있습니다. 아래처럼 위젯을 구성한 경우 Widget에서 유저의 관심사는 사실상 버튼 하나 뿐인데, 불필요하게 BP_ItemSlotWidget이라는 위젯 자체에 포커스가 한번 이동하게 됩니다. 이 경우에 바로 버튼으로 포커스가 이동할 수 있도록 세팅이 가능합니다.
포커스 이동을 사용한 예시 화면

2. C++코드

Navigation의 동작 세팅 코드 예제입니다.

// Custom Boundary 동작 세팅
FCustomWidgetNavigationDelegate NavigationDelegate;
NavigationDelegate.BindDynamic(this, &UInventoryWidget::OnNavigateBoundary);
InventoryBox->SetNavigationRuleCustomBoundary(EUINavigation::Right, NavigationDelegate);

// Navigation 동작 세팅 변경
InventoryBox->SetNavigationRuleBase(EUINavigation::Previous, EUINavigationRule::Stop);

Custom, CustomBoundary로 세팅하는 경우 함수는 아래와 같이 정의되어야 합니다.

// Focus해줘야 하는 대상을 반환
UFUNCTION()
UWidget* OnNavigateBoundary(EUINavigation InNavigation);

특정 키를 누른 경우에 쓸 수 있는 코드 예제입니다.

FReply UCustomWidget::NativeOnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
{
	// Back 액션이 호출된 경우, 커스텀 함수인 OnCancel 함수를 호출
	if (FSlateApplication::Get().GetNavigationActionFromKey(InKeyEvent) == EUINavigationAction::Back)
	{
		OnCancel();
		return FReply::Handled();
	}
	
    // Back 액션이 아닌 경우 기존 동작 유지
	return Super::NativeOnKeyDown(MyGeometry, InKeyEvent);
}

포커스는 SetFocus함수를 사용하여 수동으로 이동시켜 줄 수 있으며, SetIsFocusableSetDesiredFocusWidget함수로 에디터 수정과 동일한 조정이 가능합니다.

추가로 포커스의 변경 감지도 가능합니다.

// .h
void UCustomWidget::OnFocusChanging(
	const FFocusEvent& FocusEvent,
	const FWeakWidgetPath& OldFocusedWidgetPath,
	const TSharedPtr<SWidget>& OldFocusedWidget,
	const FWidgetPath& NewFocusedWidgetPath,
	const TSharedPtr<SWidget>& NewFocusedWidget)
    
// .cpp
FSlateApplication::Get().OnFocusChanging().AddUObject(this, &UCustomWidget::OnFocusChanging);

기존의 키 매핑이 마음에 안드는 경우, 조정이 가능합니다. 예를들어 게임의 동작이 WASD키로 이루어지는 경우, 방향키 대신 WASD로 네비게이션을 동작하도록 아래와 같이 변경할 수 있습니다.

  1. 덮어 씌울 Config 정의
#pragma once

#include "CustomNavigationConfig.h"
#include "CoreMinimal.h"
#include "Framework/Application/NavigationConfig.h"

class FCustomNavigationConfig : public FNavigationConfig
{
public:
	FCustomNavigationConfig()
	{
		// 내 키 추가
		KeyEventRules.Emplace(EKeys::W, EUINavigation::Up);
		KeyEventRules.Emplace(EKeys::S, EUINavigation::Down);
		KeyEventRules.Emplace(EKeys::A, EUINavigation::Left);
		KeyEventRules.Emplace(EKeys::D, EUINavigation::Right);

		// 기존 키 삭제
		KeyEventRules.Remove(EKeys::Left);
		KeyEventRules.Remove(EKeys::Right);
		KeyEventRules.Remove(EKeys::Up);
		KeyEventRules.Remove(EKeys::Down);
	}
};
  1. 새 Config를 적용
void ACustomGameMode::BeginPlay()
{
	Super::BeginPlay();

	// Navigation 설정 덮어쓰기
	const auto CustomNavConfig = MakeShared<FCustomNavigationConfig>();
	FSlateApplication::Get().SetNavigationConfig(CustomNavConfig);
}

접근성

UI 네비게이션은 접근성 지원 측면에서도 아주 중요한 기능인데요. 텍스트에 포커스가 가면 해당 텍스트를 읽어주는 등의 동작을 할 수 있게 만들어줍니다. 시각 장애인 접근성 기능 개요 문서를 보시면 추가 플러그인 설치를 통해 스크린 리더 기능을 지원할 수 있습니다. 이 플러그인을 활성화하면, 윈도우의 내레이터, NVDA등 프로그램을 통해 게임 내 텍스트를 음성으로 출력할 수 있게 됩니다. 키 입력을 통한 네비게이션과 조합해서 Focus를 이동하면서 텍스트를 읽도록 만들어줄 수 있습니다.

관련 설정은 에디터에서 위젯 블루프린트 Detail창의 Accessibility에서 조정할 수 있습니다.
언리얼 에디터 내 위젯 접근성 세팅 화면 캡처

스크린 리더 설정 방법

  1. 에디터에서 Edit-Plugins로 들어가 ScreenReader를 활성화해줍니다.
    스크린 리더 플러그인 설치 창

  2. 엔진 설치 경로에서 \Config\ConsoleVariables.ini파일에 Accessibility.Enable=1을 추가해줍니다. 제 경우에 전체 경로는 ..\Epic Games\UE_5.6\Engine\Config\ConsoleVariables.ini 였습니다.

    • 이 단계에서 정상적으로 적용되지 않으면, 코드상에서 GAllowAccessibility변수가 false로 세팅되어 스크린 리더가 아무것도 읽지 못합니다.
  3. NVDA, 윈도우 내레이터 혹은 다른 스크린 리더 프로그램을 사용해서, 정상적으로 적용되었는지 확인합니다.

참고 링크


간만의 포스팅입니다. 이전 글을 쓰는 시점에 이미 구현은 다 끝난 상태였는데 글 쓰는 걸 미루다보니 이제야 쓰게 되었습니다. 거의 매일 그날그날 작업한 걸 올리겠다는 건 무모한 목표였나봅니다. 앞으로는 쓸만한 게 생기는 경우에만, 적당히 시간을 들여서 쓰려고 합니다.

언리얼 코딩을 조금이나마 체험해봤고, 이 엔진을 제대로 쓰기 위해서, 이걸로 게임 하나를 만들기 위해서 얼마나 많은 코드와 에디터 작업이 필요한지 조금 알게되었습니다. 뭔가를 완성한다는 목표로 달리기에는 너무 까마득한 작업이고, 지금 배우고 싶은 건 언리얼 뿐만이 아니라서 언리얼 공부는 이 시점에서 한번 정리하고 마무리하려고 합니다. 그래서 앞으로 한두개 정도의 포스팅을 마지막으로 언리얼 관련 글은 당분간 쓰지 않을 것 같습니다.

0개의 댓글