UE5 CommonUI的使用
注:此处使用版本是UE5.4,不爱看源码的朋友跳过往后翻就行,不影响使用
前言
为啥UE5要推CommonUI?
用过原生的UserWidget的朋友,应该都清楚,UE的UI输入都来自于Focus的UI,当遇到主机游戏上,要频繁切UI,切输入的时候,老会发现当前的UI没有Focus,导致界面按钮没输入,然后卡死。又或者鼠标上切到外部,再切回来的时候,会发现Focus又丢失掉了,当你点了Viewport时候,会发现输入跑到了GameViewPort上,整体UI也就丢失了输入。包括它的输入,蓝图WidgetTree里面的UI接收输入时候,它的WidgetTree拥有者,也会接收到输入,没有PlayerController里面的输入的Consume操作。当然,还有很多风格化(样式),手柄导航等诸多考校,大家可以去阅读官方文档:
快速配置
配置Game Viewport Client Class
Project Setting->All Settings下搜索Game Viewport Client Class,将视口切换成CommonGameViewportClient(CommonGameViewportClient包含了有关输入内容)
CommonGameViewportClient源代码
.h
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Engine/GameViewportClient.h"
#include "CommonGameViewportClient.generated.h"
class FReply;
DECLARE_DELEGATE_FourParams(FOnRerouteInputDelegate, FInputDeviceId /* InputDeviceId */, FKey /* Key */, EInputEvent /* EventType */, FReply& /* Reply */);
DECLARE_DELEGATE_FourParams(FOnRerouteAxisDelegate, FInputDeviceId /* InputDeviceId */, FKey /* Key */, float /* Delta */, FReply& /* Reply */);
DECLARE_DELEGATE_FiveParams(FOnRerouteTouchDelegate, int32 /* ControllerId */, uint32 /* TouchId */, ETouchType::Type /* TouchType */, const FVector2D& /* TouchLocation */, FReply& /* Reply */);
/**
* CommonUI Viewport to reroute input to UI first. Needed to allow CommonUI to route / handle inputs.
*/
UCLASS(Within = Engine, transient, config = Engine)
class COMMONUI_API UCommonGameViewportClient : public UGameViewportClient
{
GENERATED_BODY()
public:
UCommonGameViewportClient(FVTableHelper& Helper);
virtual ~UCommonGameViewportClient();
// UGameViewportClient interface begin
virtual bool InputKey(const FInputKeyEventArgs& EventArgs) override;
virtual bool InputAxis(FViewport* InViewport, FInputDeviceId InputDevice, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad) override;
virtual bool InputTouch(FViewport* InViewport, int32 ControllerId, uint32 Handle, ETouchType::Type Type, const FVector2D& TouchLocation, float Force, FDateTime DeviceTimestamp, uint32 TouchpadIndex) override;
// UGameViewportClient interface end
FOnRerouteInputDelegate& OnRerouteInput() {
return RerouteInput; }
FOnRerouteAxisDelegate& OnRerouteAxis() {
return RerouteAxis; }
FOnRerouteTouchDelegate& OnRerouteTouch() {
return RerouteTouch; }
FOnRerouteInputDelegate& OnRerouteBlockedInput() {
return RerouteBlockedInput; }
/** Default Handler for Key input. */
UE_DEPRECATED(5.1, "This version of HandleRerouteInput has been deprecated. Please use the version that takes an FInputDeviceId instead")
virtual void HandleRerouteInput(int32 ControllerId, FKey Key, EInputEvent EventType, FReply& Reply);
/** Default Handler for Key input. */
virtual void HandleRerouteInput(FInputDeviceId DeviceId, FKey Key, EInputEvent EventType, FReply& Reply);
/** Default Handler for Axis input. */
UE_DEPRECATED(5.1, "This version of HandleRerouteAxis has been deprecated. Please use the version that takes an FInputDeviceId instead")
virtual void HandleRerouteAxis(int32 ControllerId, FKey Key, float Delta, FReply& Reply);
/** Default Handler for Axis input. */
virtual void HandleRerouteAxis(FInputDeviceId DeviceId, FKey Key, float Delta, FReply& Reply);
/** Default Handler for Touch input. */
virtual void HandleRerouteTouch(int32 ControllerId, uint32 TouchId, ETouchType::Type TouchType, const FVector2D& TouchLocation, FReply& Reply);
protected:
/** Console window & fullscreen shortcut have higher priority than UI */
virtual bool IsKeyPriorityAboveUI(const FInputKeyEventArgs& EventArgs);
FOnRerouteInputDelegate RerouteInput;
FOnRerouteAxisDelegate RerouteAxis;
FOnRerouteTouchDelegate RerouteTouch;
FOnRerouteInputDelegate RerouteBlockedInput;
};
#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#include "CoreMinimal.h"
#include "Input/Reply.h"
#include "InputCoreTypes.h"
#include "InputKeyEventArgs.h"
#include "UObject/ObjectMacros.h"
#endif
.cpp
// Copyright Epic Games, Inc. All Rights Reserved.
#include "CommonGameViewportClient.h"
#include "Engine/Console.h"
#include "Engine/GameInstance.h"
#include "Engine/LocalPlayer.h"
#include "InputKeyEventArgs.h"
#if WITH_EDITOR
#endif // WITH_EDITOR
#include "Input/CommonUIActionRouterBase.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonGameViewportClient)
#define LOCTEXT_NAMESPACE ""
static const FName NAME_Typing = FName(TEXT("Typing"));
static const FName NAME_Open = FName(TEXT("Open"));
UCommonGameViewportClient::UCommonGameViewportClient(FVTableHelper& Helper) : Super(Helper)
{
}
UCommonGameViewportClient::~UCommonGameViewportClient()
{
}
bool UCommonGameViewportClient::InputKey(const FInputKeyEventArgs& InEventArgs)
{
FInputKeyEventArgs EventArgs = InEventArgs;
if (IsKeyPriorityAboveUI(EventArgs))
{
return true;
}
// Check override before UI
if (OnOverrideInputKey().IsBound())
{
if (OnOverrideInputKey().Execute(EventArgs))
{
return true;
}
}
// The input is fair game for handling - the UI gets first dibs
#if !UE_BUILD_SHIPPING
if (ViewportConsole && !ViewportConsole->ConsoleState.IsEqual(NAME_Typing) && !ViewportConsole->ConsoleState.IsEqual(NAME_Open))
#endif
{
FReply Result = FReply::Unhandled();
if (!OnRerouteInput().ExecuteIfBound(EventArgs.InputDevice, EventArgs.Key, EventArgs.Event, Result))
{
HandleRerouteInput(EventArgs.InputDevice, EventArgs.Key, EventArgs.Event, Result);
}
if (Result.IsEventHandled())
{
return true;
}
}
return Super::InputKey(EventArgs);
}
bool UCommonGameViewportClient::InputAxis(FViewport* InViewport, FInputDeviceId InputDevice, FKey Key, float Delta, float DeltaTime, int32 NumSamples, bool bGamepad)
{
FReply RerouteResult = FReply::Unhandled();
if (!OnRerouteAxis().ExecuteIfBound(InputDevice, Key, Delta, RerouteResult))
{
HandleRerouteAxis(InputDevice, Key, Delta, RerouteResult);
}
if (RerouteResult.IsEventHandled())
{
return true;
}
return Super::InputAxis(InViewport, InputDevice, Key, Delta, DeltaTime, NumSamples, bGamepad);
}
bool UCommonGameViewportClient::InputTouch(FViewport* InViewport, int32 ControllerId, uint32 Handle, ETouchType::Type Type, const FVector2D& TouchLocation, float Force, FDateTime DeviceTimestamp, uint32 TouchpadIndex)
{
#if !UE_BUILD_SHIPPING
if (ViewportConsole != NULL && (ViewportConsole->ConsoleState != NAME_Typing) && (ViewportConsole->ConsoleState != NAME_Open))
#endif
{
FReply Result = FReply::Unhandled();
if (!OnRerouteTouch().ExecuteIfBound(ControllerId, Handle, Type, TouchLocation, Result))
{
HandleRerouteTouch(ControllerId, Handle, Type, TouchLocation, Result);
}
if (Result.IsEventHandled())
{
return true;
}
}
return Super::InputTouch(InViewport, ControllerId