범용 UI(CommonUI) 의 입력 시스템은 크로스 플랫폼 입력 지원, 특히 복합 또는 다중 레이어 메뉴를 관리합니다. 이 가이드는 다음을 포함하여 범용 UI의 입력 시스템 작동 방식에 관한 자세한 정보를 제공합니다.
-
합성 커서(Synthetic Cursor) 를 사용하는 내비게이션 처리.
-
뷰포트에서 입력 캡처.
-
입력 라우터에서 입력을 수신할 위젯을 결정하는 방법.
-
개별 위젯이 입력 라우팅 프로세스에 영향을 미치고 반응하는 방법.
-
위젯이 입력 라우팅에 반응하는 방식 변경.
프로젝트에 입력 라우팅 지원을 설정하려면 범용 UI 퀵스타트 가이드에 있는 지침을 참조하세요.
합성 커서를 사용하는 게임패드 내비게이션
범용 UI에서 여러 게임패드 상호작용은 보이지 않는 합성 커서를 기반으로 합니다. 따라서 마우스를 사용하도록 UI를 설정한 경우 범용 UI가 올바르게 작동하는 데 있어 필요한 것은 보이지 않는 커서가 올바른 위치에 있는 것과 마우스와 같은 클릭뿐입니다. 이 설정은 모두 하나의 입력 경로에 제공하여 크로스 플랫폼 입력에 대한 흐름을 간소화합니다.
이 섹션에서는 합성 커서와 기본 입력의 작동 방식을 자세히 설명합니다. 일반 언리얼 모션 그래픽 (UMG) 과 슬레이트(Slate) 기반 입력 흐름에서 합성 커서에 클릭을 등록하는 방법의 기초부터 시작합니다. 그런 다음 범용 UI의 구현이 이와 어떻게 다른지 구체적으로 살펴봅니다.
합성 커서/게임패드를 사용하는 클릭
이 섹션에서는 합성 커서의 작동 방식을 보여줍니다. 예시로서 게임패드의 '수락(Accept)' 또는 '기본 클릭(Default Click)' 액션의 입력 흐름을 사용합니다.
대체로 가상 수락 키는 EKeys::Virtual_Accept 로 매핑됩니다. 내부적으로 입력 흐름은 GenericApplication 에서 파생된 플랫폼 특정 애플리케이션에서 시작됩니다. 예를 들어 Windows는 FWindowsApplication 을 사용합니다.
이 입력은 FSlateApplication::ProcessKeyDownEvent 를 사용하는 FSlateApplication 으로 처리됩니다. 이 메서드는 프로세싱을 위해 IInputProcessor 인터페이스를 구현하고, 가능하면 입력을 처리하는 입력 프로세서를 사용합니다. 입력이 처리되면 추가 처리가 방지됩니다. FCommonAnalogCursor 는 부모 클래스 FAnalogCursor 와 마찬가지로 IInputProcessor 입니다. FCommonAnalogCursor::HandleKeyDownEvent 의 키 누름 입력 처리를 시도합니다.
FCommonAnalogCursor는 현재 위젯의 바운드 액션(Bound Actions) 으로 캡처되지 않은 게임패드의 표준 '수락' 액션에 대한 입력을 처리하지 않습니다. 대신 입력을 FAnalogCursor::HandleKeyDownEvent 로 전달합니다. 이후 FAnalogCursor 는 FSlateApplication 에서 처리할 합성 마우스 클릭 이벤트를 생성합니다.
이 시점에서 마우스 이벤트는 일반 클릭과 유사한 입력 처리 프로세스를 거치고, 최종 클릭을 트리거합니다.
일반 클릭 입력 흐름을 조사하려면 SButton:OnMouseButtonDown 에 중단점을 추가합니다.
합성 커서 클릭 문제 해결
범용 UI의 합성 커서 클릭 동작이 예상과 다른 경우 문제의 원인은 FPointerEvent 의 프로세스 중 하나일 가능성이 높습니다.
다음은 몇 가지 발생할 수 있는 문제입니다.
-
FPointerEvent에서 올바른 사용자의 입력을 처리하지 않습니다.
-
합성 커서가 예상 위치와 가까이 있지 않습니다.
-
클릭이
FSlateApplication::ProcessMouseButtonDownEvent에서 처리될 때 캡처가 있는 다른 위젯이FWidgetPath에 영향을 미칩니다. 이는 입력 구성의MouseCaptureMode를 기반으로 발생할 수 있습니다. -
FWidgetPath가FSlateApplication::LocateWindowUnderMouse를 사용하는 위치를 기반으로 자연 생성됩니다.
FWidgetPath 에는 입력이 라우팅될 수 있는 위젯 목록이 포함되어 있습니다.
합성 커서와 게임패드의 내비게이션 및 포커스 방법
순전히 내비게이션 측면에서 범용 UI는 UMG의 기본 구현과 다르게 동작하지 않습니다. 이 섹션에서는 편의를 위해 이 프로세스를 자세히 설명합니다.
이전 섹션에서와 마찬가지로 입력은 플랫폼 특정 애플리케이션에서 시작됩니다. 이 섹션은 화살표 키 또는 아날로그 이동 예시를 사용합니다. 입력 라우팅 시스템은 이 UI 내비게이션 입력을 UI 내 위젯으로 라우팅합니다.
이 예시에서 내비게이션이 발생하면 위젯이 이 특정 내비게이션 입력을 처리하는 데 특화되지 않은 것으로 가정합니다.
내비게이션 입력이 발생할 때 입력은 기본 SWidget::OnKeyDown 또는 SWidget::OnAnalogValueChanged 메서드에 의해 처리됩니다. 하지만 이러한 기본 메서드는 위젯 포커스를 직접 변경하지 않습니다. 대신 다음 상황이 발생합니다.
-
이 내비게이션 메서드는
FSlateApplication::GetNavigationDirectionFromKey또는FSlateApplication::GetNavigationDirectionFromAnalog를 사용하여 입력을 내비게이션 방향으로 변환합니다. 이 변환을 실행할 때 위젯에 대한 내비게이션 구성을 고려합니다. -
내비게이션 방향은 캡처되고
FReply::SetNavigation을 통해 전송되는FReply::Handled응답에 포함됩니다.FReply는 많은 컨텍스트 정보를 포함할 수 있습니다. 아래에 있는 입력 라우팅 섹션에서 FReply 관련 정보를 참조하세요. -
슬레이트가
FSlateApplication::ProcessReply를 사용하여FReply처리를 시작하고, 이로 인해 내비게이션이 발생합니다. 내비게이션 이벤트가 방향에 의해 대략적으로 정의된 경우_FSlateApplication::AttemptNavigation_는 내비게이션할 올바른 위젯을 찾으려 합니다. -
가능한 경우
FSlateApplication::ExecuteNavigation은 대상 위젯으로 내비게이션합니다. -
대상 위젯이 유효한 경우
FSlateApplication::SetUserFocus가 해당 위젯에 호출됩니다. 이는 대상 위젯이 직접 지정되었거나 이전에 대상 위젯을 찾았는지 여부와 무관하게 이루어집니다. -
슬레이트 포커스 내비게이션이 발생한 이후
FCommonAnalogCursor::Tick은 자동적으로 다음 틱 도중 합성 커서를 포커스된 위젯으로 이동하고 중앙에 위치합니다.
이를 통해 게임패드를 사용할 때 호버 이펙트를 사용할 수 있습니다.
범용 UI에서 합성 커서 동작 커스터마이징
범용 UI에서 고유한 아날로그 커서 또는 합성 커서를 제공할 수 있습니다. 이렇게 하는 것이 좋은 이유가 몇 가지 있습니다. 예를 들어 게임패드와 유사하게 작동하는 키보드 내비게이션을 만들려 하는 경우 게임패드를 사용하지 않을 때에도 FCommonAnalogCursor::Tick 이 위젯의 중앙에 스냅되도록 할 수 있습니다. 또는 합성 마우스를 보이도록 하고, 포커스 변경 사이에 몇 가지 종류의 트윈을 구현할 수 있습니다.
커스텀 커서 만들기:
-
UCommonUIActionRouterBase에서 파생합니다. -
UCommonUIActionRouterBase::MakeAnalogCursor를 오버라이드하여 커스텀 아날로그 커서 클래스를 반환합니다.
UCommonUIActionRouterBase::ShouldCreateSubsystem 은 고유한 액션 라우터 클래스로 오버라이드하는 경우 인스턴스를 생성하지 않습니다.
입력 프로세서는 모든 입력에서 실행 된다는 점을 명심하세요. 여기에는 언리얼 에디터의 입력이 포함됩니다. FCommonAnalogCursor::IsGameViewportInFocusPathWithoutCapture 를 사용하면 애플리케이션과 그 밖의 것들을 구분하는 데 도움이 될 수 있습니다.
범용 UI 입력 라우팅
다음은 입력 라우팅 프로세스의 간략한 개요입니다.
-
범용 UI는 활성화 가능 위젯을 내비게이션을 처리하는 노드 트리로 구성합니다. 비활성화된 위젯은 노드 목록에 추가되지 않고, 그에 따라 입력 라우팅에 고려되지 않습니다.
-
CommonGameViewportClient는 입력을 캡처하고, 계층구조의 최상단에 표시된 노드를 찾습니다.
-
노드는 사용 가능한 입력 핸들러 중 하나를 사용하여 입력을 처리할 수 있는지 확인합니다. 플레이어의 입력과 일치하는 입력 핸들러가 없는 경우 입력을 최상단 자손 노드로 전달하고 이 단계를 반복합니다.
이 프로세스는 모든 노드가 확인될 때까지 반복됩니다. 아래의 섹션은 이러한 단계에 관한 상세 정보와 이 시스템의 컴포넌트를 제공합니다.
입력 라우팅 실행 흐름
합성 커서를 사용하는 클릭 관련 섹션에서 설명한 것처럼 입력 프로세서는 입력 라우팅 발생 전에 입력 이벤트를 처리할 수 있습니다. 입력 프로세서에서 입력 이벤트를 처리하지 않는 경우 입력 이벤트는 범용 UI의 입력 라우팅 흐름을 거칩니다.
이벤트 프로세스 클릭
플랫폼 특정 애플리케이션이 FSlateApplication::ProcessKeyDownEvent를 트리거할 때 입력이 시작됩니다.
슬레이트 애플리케이션은 현재 포커스 경로를 기반으로 위젯에 입력 이벤트를 전달합니다. 게임에서 이 입력 이벤트는 대체로 SViewport::OnKeyDown 으로, 키 누름을 현재 뷰포트 인터페이스를 구현하는 클래스로 전달합니다. 키 누름을 전달하면 대체로 FSceneViewport::OnKeyDown 이 트리거됩니다.
마지막으로 씬 뷰포트는 입력을 현재 게임 뷰포트 클라이언트의 InputKey 메서드로 전달합니다. 범용 UI의 경우 이는 UCommonGameViewportClient::InputKey 입니다. 그래서 범용 UI가 제대로 작동하도록 게임 뷰포트 클래스가 CommonViewportClient 로 설정되어야 합니다.
뷰포트 클라이언트 클래스 설정에 관한 추가 정보는 범용 UI 퀵스타트 가이드를 참조하세요.
액션 라우터 프로세스
범용 UI의 특정 입력 라우팅 구현은 입력이 게임 뷰포트 클라이언트로 전달되면 시작합니다. UCommonGameViewportClient::InputKey _ _는 액션 라우터 에 UCommonGameViewportClient::HandleRerouteInput 의 입력을 처리할 기회를 제공합니다. 성공하면 UCommonUIActionRouterBase::ProcessInput을 호출합니다.
입력이 처리되지 않은 경우 게임은 일반 게임 뷰포트 클라이언트가 Super::InputKey 를 호출할 때 노멀 메서드를 통해 입력 처리를 시도합니다.
활성화 가능 위젯은 FActivatableRootNode 를 사용하는 트리의 노드로 추상화됩니다. 노드는 UI의 위젯 트리에 있는 활성화 가능 위젯 계층구조를 기반으로 하는 계층구조에 배열됩니다. 부모 노드는 루트 노드 역할을 하는 반면 그 자손 노드는 자손 또는 리프 노드 로 간주됩니다. 추가 정보는 범용 UI 개요를 참조하세요.
액션 라우터**는 활성화 가능 위젯의 트리에서 현재 활성 상태인 루트 노드를 유지합니다. UCommonUIActionRouterBase::ProcessInput 은 루트 노드에 FActivatableTreeNode::ProcessNormalInput 호출을 통해 입력 처리 시도를 지시합니다. 반복적으로 모든 자손 노드에 입력 처리 시도를 지시합니다.
FActivatableTreeNode 는 FActionRouterBindingCollection 으로, 노드의 활성화 가능 위젯에 모든 액션 바인딩 목록을 유지합니다. 현재 노드 내의 모든 자손 노드에서 입력을 처리하지 못하면 현재 노드는 FActionRouterBindingCollection::ProcessNormalInput 을 호출합니다. 바인딩 컬렉션으로서 현재 노드는 위젯의 모든 액션 바인딩을 확인합니다. 액션 바인딩이 해당 키와 일치하는 경우 관련 동작이 실행되고 입력이 처리된 것으로 간주됩니다.
입력 라우팅 시스템 수정
다음은 입력 라우팅 시스템 변경에 사용해야 할 주요 메서드입니다.
-
UCommonGameViewportClient에서 새 뷰포트 클래스를 파생하고 모든 입력 처리 메서드를 오버라이드합니다. 이후 프로젝트 세팅(Project Settings)에서 게임 뷰포트를 파생된 클래스로 설정합니다. -
UCommonUIActionRouterBase에서 새 클래스를 파생하고 모든 가상 함수를 오버라이드합니다. 예를 들어 커스텀 입력 구성 세팅에UCommonUIActionRouterBase::ApplyUIInputConfig를 오버라이드할 수 있습니다.
UCommonUIActionRouterBase::ShouldCreateSubsystem 은 고유한 액션 라우터 클래스로 오버라이드하는 경우 기본 액션 라우터의 인스턴스를 생성하지 않습니다.