UE5 CommonUI的使用(附源码版)

UE5 CommonUI的使用

  • 前言
  • 快速配置
    • 配置Game Viewport Client Class
      • CommonGameViewportClient源代码
    • 创建CommonInputAction表
    • 默认导航Action设置
      • CommonUIInputData源码
    • Bind CommonInputBaseControllerData
      • CommonInputBaseControllerData源码
    • Common UI控件库和控件样式
      • CommonUserWidget 源码
    • 小结

注:此处使用版本是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 beginvirtual 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 endFOnRerouteInputDelegate& 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 UIif (OnOverrideInputKey().IsBound()){if (OnOverrideInputKey().Execute(EventArgs)){return true;}}// The input is fair game for handling - the UI gets first dibs
#if !UE_BUILD_SHIPPINGif (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_SHIPPINGif (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, Handle, Type, TouchLocation, Force, DeviceTimestamp, TouchpadIndex);
}void UCommonGameViewportClient::HandleRerouteInput(FInputDeviceId DeviceId, FKey Key, EInputEvent EventType, FReply& Reply)
{FPlatformUserId OwningPlatformUser = IPlatformInputDeviceMapper::Get().GetUserForInputDevice(DeviceId);ULocalPlayer* LocalPlayer = GameInstance->FindLocalPlayerFromPlatformUserId(OwningPlatformUser);Reply = FReply::Unhandled();if (LocalPlayer){UCommonUIActionRouterBase* ActionRouter = LocalPlayer->GetSubsystem<UCommonUIActionRouterBase>();if (ensure(ActionRouter)){ERouteUIInputResult InputResult = ActionRouter->ProcessInput(Key, EventType);if (InputResult == ERouteUIInputResult::BlockGameInput){// We need to set the reply as handled otherwise the input won't actually be blocked from reaching the viewport.Reply = FReply::Handled();// Notify interested parties that we blocked the input.OnRerouteBlockedInput().ExecuteIfBound(DeviceId, Key, EventType, Reply);}else if (InputResult == ERouteUIInputResult::Handled){Reply = FReply::Handled();}}}	
}void UCommonGameViewportClient::HandleRerouteInput(int32 ControllerId, FKey Key, EInputEvent EventType, FReply& Reply)
{// Remap the old int32 ControllerId to the new platform user and input device IDFPlatformUserId UserId = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);FInputDeviceId DeviceID = INPUTDEVICEID_NONE;IPlatformInputDeviceMapper::Get().RemapControllerIdToPlatformUserAndDevice(ControllerId, UserId, DeviceID);return HandleRerouteInput(DeviceID, Key, EventType, Reply);
}void UCommonGameViewportClient::HandleRerouteAxis(FInputDeviceId DeviceId, FKey Key, float Delta, FReply& Reply)
{// Get the ownign platform user for this input device and their local playerFPlatformUserId OwningPlatformUser = IPlatformInputDeviceMapper::Get().GetUserForInputDevice(DeviceId);ULocalPlayer* LocalPlayer = GameInstance->FindLocalPlayerFromPlatformUserId(OwningPlatformUser);Reply = FReply::Unhandled();if (LocalPlayer){UCommonUIActionRouterBase* ActionRouter = LocalPlayer->GetSubsystem<UCommonUIActionRouterBase>();if (ensure(ActionRouter)){// We don't actually use axis inputs that reach the game viewport UI land for anything, we just want block them reaching the game when they shouldn'tif (!ActionRouter->CanProcessNormalGameInput()){Reply = FReply::Handled();}}}
}void UCommonGameViewportClient::HandleRerouteAxis(int32 ControllerId, FKey Key, float Delta, FReply& Reply)
{// Remap the old int32 ControllerId to the new platform user and input device IDFPlatformUserId UserId = FGenericPlatformMisc::GetPlatformUserForUserIndex(ControllerId);FInputDeviceId DeviceID = INPUTDEVICEID_NONE;IPlatformInputDeviceMapper::Get().RemapControllerIdToPlatformUserAndDevice(ControllerId, UserId, DeviceID);return HandleRerouteAxis(DeviceID, Key, Delta, Reply);
}void UCommonGameViewportClient::HandleRerouteTouch(int32 ControllerId, uint32 TouchId, ETouchType::Type TouchType, const FVector2D& TouchLocation, FReply& Reply)
{ULocalPlayer* LocalPlayer = GameInstance->FindLocalPlayerFromControllerId(ControllerId);Reply = FReply::Unhandled();if (LocalPlayer && TouchId < EKeys::NUM_TOUCH_KEYS){FKey KeyPressed = EKeys::TouchKeys[TouchId];if (KeyPressed.IsValid()){UCommonUIActionRouterBase* ActionRouter = LocalPlayer->GetSubsystem<UCommonUIActionRouterBase>();if (ensure(ActionRouter)){EInputEvent SimilarInputEvent = IE_MAX;switch (TouchType){case ETouchType::Began:SimilarInputEvent = IE_Pressed;break;case ETouchType::Ended:SimilarInputEvent = IE_Released;break;default:SimilarInputEvent = IE_Repeat;break;}if (ActionRouter->ProcessInput(KeyPressed, SimilarInputEvent) != ERouteUIInputResult::Unhandled){Reply = FReply::Handled();}}}}
}bool UCommonGameViewportClient::IsKeyPriorityAboveUI(const FInputKeyEventArgs& EventArgs)
{
#if !UE_BUILD_SHIPPING// First priority goes to the viewport console regardless any state or settingif (ViewportConsole && ViewportConsole->InputKey(EventArgs.InputDevice, EventArgs.Key, EventArgs.Event, EventArgs.AmountDepressed, EventArgs.IsGamepad())){return true;}
#endif// We'll also treat toggling fullscreen as a system-level sort of input that isn't affected by input filteringif (TryToggleFullscreenOnInputKey(EventArgs.Key, EventArgs.Event)){return true;}return false;
}#undef LOCTEXT_NAMESPACE

创建CommonInputAction表

Common UI使用输入InputAction表来创建能够与各种平台的输入所关联的Action。

选用CommonInputActionDataBase 结构体来创建DataTable:

在这里插入图片描述
在这里插入图片描述
打开新建的DataTable,进行简单的输入配置
在这里插入图片描述
Common UI控件将这些抽象的Action映射到实际的输入。比如,你可以将数据表和RowName名称参考添加到 CommonButtonBase 控件中的 触发InputAction。之后,按下该Action所关联的按钮会触发Common UI按钮。

默认导航Action设置

虚幻引擎原生支持指向导航。但是这里,Common UI使用 CommonUIInputData来定义所有平台通用的 点击(Click) 和 返回(Back) 输Input Action。
(简单来说,就是实现多个界面打开关闭时候使用的,比如按A键打开某个界面,按B键关闭某个界面)

创建一个CommonUIInputData。
在这里插入图片描述

打开其ClassDefault,配置默认的点击与返回Action
在这里插入图片描述
此前我在DataTable里创建了俩测试按键配置
在这里插入图片描述
ProjectSetting下搜索InputData,并配置自己的刚创建的测试的CommonUIInputData
在这里插入图片描述

CommonUIInputData源码

跳过
.h

UCLASS(Abstract, Blueprintable, ClassGroup = Input, meta = (Category = "Common Input"))
class COMMONINPUT_API UCommonUIInputData : public UObject
{GENERATED_BODY()public:virtual bool NeedsLoadForServer() const override;public:UPROPERTY(EditDefaultsOnly, Category = "Properties", meta = (RowType = "/Script/CommonUI.CommonInputActionDataBase"))FDataTableRowHandle DefaultClickAction;UPROPERTY(EditDefaultsOnly, Category = "Properties", meta = (RowType = "/Script/CommonUI.CommonInputActionDataBase"))FDataTableRowHandle DefaultBackAction;/*** Newly created CommonButton widgets will use these hold values by default if bRequiresHold is true.* Inherits from UCommonUIHoldData.*/UPROPERTY(EditDefaultsOnly, Category = "Properties")TSoftClassPtr<UCommonUIHoldData> DefaultHoldData;UPROPERTY(EditDefaultsOnly, Category = "Properties", meta = (EditCondition = "CommonInput.CommonInputSettings.IsEnhancedInputSupportEnabled", EditConditionHides))TObjectPtr<UInputAction> EnhancedInputClickAction;UPROPERTY(EditDefaultsOnly, Category = "Properties", meta = (EditCondition = "CommonInput.CommonInputSettings.IsEnhancedInputSupportEnabled", EditConditionHides))TObjectPtr<UInputAction> EnhancedInputBackAction;
};

.cpp

bool UCommonUIInputData::NeedsLoadForServer() const
{const UUserInterfaceSettings* UISettings = GetDefault<UUserInterfaceSettings>();return UISettings->bLoadWidgetsOnDedicatedServer;
}

Bind CommonInputBaseControllerData

新建CommonInputBaseControllerData
在这里插入图片描述
在这里插入图片描述
将你的资产配置到Platform Input里面,对应平台配置对应平台内容
在这里插入图片描述
很可惜,我只看到Android、IOS、Linux、LinuxArm64、Mac、TVOS、Windows,其他平台还是需要自己写(比如Ps5、XBox,看了一圈源代码,也没找到有哪可以新增平台,虚幻会通过FProjectManager查询支持注册好的平台,如果有哪位朋友找到添加平台的方式,可以分享一波)。

查引用时候,会发现就是获取InputInfo时候会使用它,通俗来说,就是切平台时候,我们有按键提示,不同平台里面的按键不同,意味着显示不同的按键提醒图片,这时候,我们就可以考虑用它来处理。

bool FCommonInputActionDataBase::IsKeyBoundToInputActionData(const FKey& Key) const
{if (Key == KeyboardInputTypeInfo.GetKey() || Key == TouchInputTypeInfo.GetKey()){return true;}for (const FName& GamepadName : UCommonInputBaseControllerData::GetRegisteredGamepads()){const FCommonInputTypeInfo& TypeInfo = GetInputTypeInfo(ECommonInputType::Gamepad, GamepadName);if (Key == TypeInfo.GetKey()){return true;}}return false;
}

CommonInputBaseControllerData源码

跳过
.h

UCLASS(Abstract, Blueprintable, ClassGroup = Input, meta = (Category = "Common Input"))
class COMMONINPUT_API UCommonInputBaseControllerData : public UObject
{GENERATED_BODY()public:virtual bool NeedsLoadForServer() const override;virtual bool TryGetInputBrush(FSlateBrush& OutBrush, const FKey& Key) const;virtual bool TryGetInputBrush(FSlateBrush& OutBrush, const TArray<FKey>& Keys) const;virtual void PreSave(FObjectPreSaveContext ObjectSaveContext) override;virtual void PostLoad() override;private:
#if WITH_EDITORONLY_DATAUPROPERTY(Transient, EditAnywhere, Category = "Editor")int32 SetButtonImageHeightTo = 0;
#endifpublic:UPROPERTY(EditDefaultsOnly, Category = "Default")ECommonInputType InputType;UPROPERTY(EditDefaultsOnly, Category = "Gamepad", meta=(EditCondition="InputType == ECommonInputType::Gamepad", GetOptions = GetRegisteredGamepads))FName GamepadName;UPROPERTY(EditDefaultsOnly, Category = "Gamepad", meta = (EditCondition = "InputType == ECommonInputType::Gamepad"))FText GamepadDisplayName;UPROPERTY(EditDefaultsOnly, Category = "Gamepad", meta=(EditCondition="InputType == ECommonInputType::Gamepad"))FText GamepadCategory;UPROPERTY(EditDefaultsOnly, Category = "Gamepad", meta = (EditCondition = "InputType == ECommonInputType::Gamepad"))FText GamepadPlatformName;UPROPERTY(EditDefaultsOnly, Category = "Gamepad", meta=(EditCondition="InputType == ECommonInputType::Gamepad"))TArray<FInputDeviceIdentifierPair> GamepadHardwareIdMapping;UPROPERTY(EditDefaultsOnly, Category = "Display")TSoftObjectPtr<UTexture2D> ControllerTexture;UPROPERTY(EditDefaultsOnly, Category = "Display")TSoftObjectPtr<UTexture2D> ControllerButtonMaskTexture;UPROPERTY(EditDefaultsOnly, Category = "Display", Meta = (TitleProperty = "Key"))TArray<FCommonInputKeyBrushConfiguration> InputBrushDataMap;UPROPERTY(EditDefaultsOnly, Category = "Display", Meta = (TitleProperty = "Keys"))TArray<FCommonInputKeySetBrushConfiguration> InputBrushKeySets;UFUNCTION()static const TArray<FName>& GetRegisteredGamepads();private:
#if WITH_EDITORvirtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
#endif
};

.cpp

 bool UCommonInputBaseControllerData::NeedsLoadForServer() const
{const UUserInterfaceSettings* UISettings = GetDefault<UUserInterfaceSettings>();return UISettings->bLoadWidgetsOnDedicatedServer;
}bool UCommonInputBaseControllerData::TryGetInputBrush(FSlateBrush& OutBrush, const FKey& Key) const
{const FCommonInputKeyBrushConfiguration* DisplayConfig = InputBrushDataMap.FindByPredicate([&Key](const FCommonInputKeyBrushConfiguration& KeyBrushPair) -> bool{return KeyBrushPair.Key == Key;});if (DisplayConfig){OutBrush = DisplayConfig->GetInputBrush();return true;}return false;
}bool UCommonInputBaseControllerData::TryGetInputBrush(FSlateBrush& OutBrush, const TArray<FKey>& Keys) const
{if (Keys.Num() == 0){return false;}if (Keys.Num() == 1){return TryGetInputBrush(OutBrush, Keys[0]);}const FCommonInputKeySetBrushConfiguration* DisplayConfig = InputBrushKeySets.FindByPredicate([&Keys](const FCommonInputKeySetBrushConfiguration& KeyBrushPair) -> bool{if (KeyBrushPair.Keys.Num() < 2){return false;}if (Keys.Num() == KeyBrushPair.Keys.Num()){for (const FKey& Key : Keys){if (!KeyBrushPair.Keys.Contains(Key)){return false;}}return true;}return false;});if (DisplayConfig){OutBrush = DisplayConfig->GetInputBrush();return true;}return false;
}void UCommonInputBaseControllerData::PreSave(FObjectPreSaveContext ObjectSaveContext)
{Super::PreSave(ObjectSaveContext);if (!ObjectSaveContext.IsProceduralSave()){// These have been organized by a human already, better to sort using this array.TArray<FKey> AllKeys;EKeys::GetAllKeys(AllKeys);// Organize the keys so they're nice and cleanInputBrushDataMap.Sort([&AllKeys](const FCommonInputKeyBrushConfiguration& A, const FCommonInputKeyBrushConfiguration& B) {return AllKeys.IndexOfByKey(A.Key) < AllKeys.IndexOfByKey(B.Key);});// Delete any brush data where we have no image assignedInputBrushDataMap.RemoveAll([](const FCommonInputKeyBrushConfiguration& A) {return A.GetInputBrush().GetResourceObject() == nullptr;});}
}void UCommonInputBaseControllerData::PostLoad()
{Super::PostLoad();#if WITH_EDITOR// Have to clear it even though it's transient because it's saved into the CDO.SetButtonImageHeightTo = 0;
#endif
}const TArray<FName>& UCommonInputBaseControllerData::GetRegisteredGamepads()
{auto GenerateRegisteredGamepads = [](){TArray<FName> RegisteredGamepads;RegisteredGamepads.Add(FCommonInputDefaults::GamepadGeneric);for (const TPair<FName, FDataDrivenPlatformInfo>& Platform : FDataDrivenPlatformInfoRegistry::GetAllPlatformInfos()){const FName PlatformName = Platform.Key;const FDataDrivenPlatformInfo& PlatformInfo = Platform.Value;// Don't add fake platforms that are used to group real platforms to make configuration for groups of platforms// simpler.if (PlatformInfo.bIsFakePlatform){continue;}// If the platform uses the standard keyboard for default input, ignore it, all of those platforms will use "PC"// as their target, so Windows, Linux, but not Mac.if (PlatformInfo.bDefaultInputStandardKeyboard){continue;}// Only add platforms with dedicated gamepads.if (PlatformInfo.bHasDedicatedGamepad){RegisteredGamepads.Add(PlatformName);}}return RegisteredGamepads;};static TArray<FName> RegisteredGamepads = GenerateRegisteredGamepads();return RegisteredGamepads;
}#if WITH_EDITOR
void UCommonInputBaseControllerData::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{Super::PostEditChangeProperty(PropertyChangedEvent);if (PropertyChangedEvent.ChangeType == EPropertyChangeType::ValueSet){if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(UCommonInputBaseControllerData, SetButtonImageHeightTo)){if (SetButtonImageHeightTo != 0){for (FCommonInputKeyBrushConfiguration& BrushConfig : InputBrushDataMap){FVector2D NewBrushSize = BrushConfig.KeyBrush.GetImageSize();if (NewBrushSize.X != 0 && NewBrushSize.Y != 0){NewBrushSize.X = FMath::RoundToInt(SetButtonImageHeightTo * (NewBrushSize.X / NewBrushSize.Y));NewBrushSize.Y = SetButtonImageHeightTo;BrushConfig.KeyBrush.SetImageSize(NewBrushSize);}}for (FCommonInputKeySetBrushConfiguration& BrushConfig : InputBrushKeySets){FVector2D NewBrushSize = BrushConfig.KeyBrush.GetImageSize();if (NewBrushSize.X != 0 && NewBrushSize.Y != 0){NewBrushSize.X = FMath::RoundToInt(SetButtonImageHeightTo * (NewBrushSize.X / NewBrushSize.Y));NewBrushSize.Y = SetButtonImageHeightTo;BrushConfig.KeyBrush.SetImageSize(NewBrushSize);}}}SetButtonImageHeightTo = 0;}}
}
#endif

Common UI控件库和控件样式

CommonUI带了一些Style,在它自己的控件里面可以使用这些
在这里插入图片描述
一样的ProjectSetting里面配置Style
在这里插入图片描述

支持的控件类型:
在这里插入图片描述
CommonUIl有俩种主要有UserWdiget,一个CommonActivatableWidget,一个CommonUserWidget 。CommonUserWidget接管了原生虚幻的输入(也像PlayerController一样的方式,采用Consume的方式)。CommonActivatableWidget是继承自CommonUserWidget,它比起原生的CommonUserWidget添加了,激活的一些内容。CommonUserWidget接管了原生虚幻的输入(也像PlayerController一样的方式,采用Consume的方式)
当然,还有其他子控件:
在这里插入图片描述
CommonActivatableWidget里面带是带堆栈的,方便用于新旧界面之间的交互
在这里插入图片描述
之前的Button,也得继承CommonButtonBase使用
在这里插入图片描述

CommonUserWidget 源码

// Copyright Epic Games, Inc. All Rights Reserved.#pragma once#include "Blueprint/UserWidget.h"
#include "Input/UIActionBindingHandle.h"#include "CommonUserWidget.generated.h"class UCommonInputSubsystem;
class UCommonUISubsystemBase;
class FSlateUser;struct FUIActionTag;
struct FBindUIActionArgs;
enum class ECommonInputMode : uint8;UCLASS(ClassGroup = UI, meta = (Category = "Common UI", DisableNativeTick))
class COMMONUI_API UCommonUserWidget : public UUserWidget
{GENERATED_UCLASS_BODY()public:/** Sets whether or not this widget will consume ALL pointer input that reaches it */UFUNCTION(BlueprintCallable, Category = CommonUserWidget)void SetConsumePointerInput(bool bInConsumePointerInput);/** Add a widget to the list of widgets to get scroll events for this input root node */UFUNCTION(BlueprintCallable, Category = CommonUserWidget)void RegisterScrollRecipientExternal(const UWidget* AnalogScrollRecipient);/** Remove a widget from the list of widgets to get scroll events for this input root node */UFUNCTION(BlueprintCallable, Category = CommonUserWidget)void UnregisterScrollRecipientExternal(const UWidget* AnalogScrollRecipient);public:const TArray<FUIActionBindingHandle>& GetActionBindings() const { return ActionBindings; }const TArray<TWeakObjectPtr<const UWidget>> GetScrollRecipients() const { return ScrollRecipients; }/*** Convenience methods for menu action registrations (any UWidget can register via FCommonUIActionRouter directly, though generally that shouldn't be needed).* Persistent bindings are *always* listening for input while registered, while normal bindings are only listening when all of this widget's activatable parents are activated.*/FUIActionBindingHandle RegisterUIActionBinding(const FBindUIActionArgs& BindActionArgs);void RemoveActionBinding(FUIActionBindingHandle ActionBinding);void AddActionBinding(FUIActionBindingHandle ActionBinding);protected:virtual void OnWidgetRebuilt() override;virtual void NativeDestruct() override;virtual FReply NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;virtual FReply NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;virtual FReply NativeOnMouseWheel(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;virtual FReply NativeOnMouseButtonDoubleClick(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent) override;virtual FReply NativeOnTouchGesture(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent) override;virtual FReply NativeOnTouchStarted(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent) override;virtual FReply NativeOnTouchMoved(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent) override;virtual FReply NativeOnTouchEnded(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent) override;UCommonInputSubsystem* GetInputSubsystem() const;UCommonUISubsystemBase* GetUISubsystem() const;TSharedPtr<FSlateUser> GetOwnerSlateUser() const;template <typename GameInstanceT = UGameInstance>GameInstanceT& GetGameInstanceChecked() const{GameInstanceT* GameInstance = GetGameInstance<GameInstanceT>();check(GameInstance);return *GameInstance;}template <typename PlayerControllerT = APlayerController>PlayerControllerT& GetOwningPlayerChecked() const{PlayerControllerT* PC = GetOwningPlayer<PlayerControllerT>();check(PC);return *PC;}void RegisterScrollRecipient(const UWidget& AnalogScrollRecipient);void UnregisterScrollRecipient(const UWidget& AnalogScrollRecipient);/** True to generally display this widget's actions in the action bar, assuming it has actions. */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = true))bool bDisplayInActionBar = false;private:/** Set this to true if you don't want any pointer (mouse and touch) input to bubble past this widget */UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = true))bool bConsumePointerInput = false;private:TArray<FUIActionBindingHandle> ActionBindings;TArray<TWeakObjectPtr<const UWidget>> ScrollRecipients;
};#if UE_ENABLE_INCLUDE_ORDER_DEPRECATED_IN_5_2
#include "CommonUITypes.h"
#endif
// Copyright Epic Games, Inc. All Rights Reserved.#include "CommonUserWidget.h"#include "Engine/GameInstance.h"
#include "CommonInputSubsystem.h"
#include "CommonUISubsystemBase.h"
#include "Input/CommonUIActionRouterBase.h"
#include "Input/CommonUIInputTypes.h"#include UE_INLINE_GENERATED_CPP_BY_NAME(CommonUserWidget)UCommonUserWidget::UCommonUserWidget(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
{	
#if WITH_EDITORONLY_DATAPaletteCategory = FText::FromString(TEXT("Common UI"));
#endif
}void UCommonUserWidget::SetConsumePointerInput(bool bInConsumePointerInput)
{bConsumePointerInput = bInConsumePointerInput;
}UCommonInputSubsystem* UCommonUserWidget::GetInputSubsystem() const
{return UCommonInputSubsystem::Get(GetOwningLocalPlayer());
}UCommonUISubsystemBase* UCommonUserWidget::GetUISubsystem() const
{return UGameInstance::GetSubsystem<UCommonUISubsystemBase>(GetGameInstance());
}TSharedPtr<FSlateUser> UCommonUserWidget::GetOwnerSlateUser() const
{ULocalPlayer* LocalPlayer = GetOwningLocalPlayer();return LocalPlayer ? LocalPlayer->GetSlateUser() : nullptr;
}FReply UCommonUserWidget::NativeOnMouseButtonDown(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnMouseButtonDown(InGeometry, InMouseEvent);
}FReply UCommonUserWidget::NativeOnMouseButtonUp(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnMouseButtonUp(InGeometry, InMouseEvent);
}FReply UCommonUserWidget::NativeOnMouseWheel(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnMouseWheel(InGeometry, InMouseEvent);
}FReply UCommonUserWidget::NativeOnMouseButtonDoubleClick(const FGeometry& InGeometry, const FPointerEvent& InMouseEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnMouseButtonDoubleClick(InGeometry, InMouseEvent);
}FReply UCommonUserWidget::NativeOnTouchGesture(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnTouchGesture(InGeometry, InGestureEvent);
}FReply UCommonUserWidget::NativeOnTouchStarted(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnTouchStarted(InGeometry, InGestureEvent);
}FReply UCommonUserWidget::NativeOnTouchMoved(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnTouchMoved(InGeometry, InGestureEvent);
}FReply UCommonUserWidget::NativeOnTouchEnded(const FGeometry& InGeometry, const FPointerEvent& InGestureEvent)
{return bConsumePointerInput ? FReply::Handled() : Super::NativeOnTouchEnded(InGeometry, InGestureEvent);
}FUIActionBindingHandle UCommonUserWidget::RegisterUIActionBinding(const FBindUIActionArgs& BindActionArgs)
{if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){FBindUIActionArgs FinalBindActionArgs = BindActionArgs;if (bDisplayInActionBar && !BindActionArgs.bDisplayInActionBar){FinalBindActionArgs.bDisplayInActionBar = true;}FUIActionBindingHandle BindingHandle = ActionRouter->RegisterUIActionBinding(*this, FinalBindActionArgs);ActionBindings.Add(BindingHandle);return BindingHandle;}return FUIActionBindingHandle();
}void UCommonUserWidget::RemoveActionBinding(FUIActionBindingHandle ActionBinding)
{ActionBindings.Remove(ActionBinding);if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){ActionRouter->RemoveBinding(ActionBinding);}
}void UCommonUserWidget::AddActionBinding(FUIActionBindingHandle ActionBinding)
{ActionBindings.Add(ActionBinding);if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){ActionRouter->AddBinding(ActionBinding);}
}void UCommonUserWidget::RegisterScrollRecipient(const UWidget& AnalogScrollRecipient)
{if (!ScrollRecipients.Contains(&AnalogScrollRecipient)){ScrollRecipients.Add(&AnalogScrollRecipient);if (GetCachedWidget()){if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){ActionRouter->RegisterScrollRecipient(AnalogScrollRecipient);}}}
}void UCommonUserWidget::UnregisterScrollRecipient(const UWidget& AnalogScrollRecipient)
{if (ScrollRecipients.Remove(&AnalogScrollRecipient) && GetCachedWidget()){if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){ActionRouter->UnregisterScrollRecipient(AnalogScrollRecipient);}}
}void UCommonUserWidget::RegisterScrollRecipientExternal(const UWidget* AnalogScrollRecipient)
{if (AnalogScrollRecipient != nullptr){RegisterScrollRecipient(*AnalogScrollRecipient);}
}void UCommonUserWidget::UnregisterScrollRecipientExternal(const UWidget* AnalogScrollRecipient)
{if (AnalogScrollRecipient != nullptr){UnregisterScrollRecipient(*AnalogScrollRecipient);}
}void UCommonUserWidget::OnWidgetRebuilt()
{// Using OnWidgetRebuilt instead of NativeConstruct to ensure we register ourselves with the ActionRouter before the child receives NativeConstructif (!IsDesignTime()){// Clear out any invalid bindings before we bother trying to register themfor (int32 BindingIdx = ActionBindings.Num() - 1; BindingIdx >= 0; --BindingIdx){if (!ActionBindings[BindingIdx].IsValid()){ActionBindings.RemoveAt(BindingIdx);}}if (ActionBindings.Num() > 0){if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){ActionRouter->NotifyUserWidgetConstructed(*this);}}}Super::OnWidgetRebuilt();if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){for (const TWeakObjectPtr<const UWidget>& WidgetPtr : GetScrollRecipients()){if (const UWidget* Widget = WidgetPtr.Get()){ActionRouter->RegisterScrollRecipient(*Widget);}}}
}void UCommonUserWidget::NativeDestruct()
{if (ActionBindings.Num() > 0){if (UCommonUIActionRouterBase* ActionRouter = UCommonUIActionRouterBase::Get(*this)){ActionRouter->NotifyUserWidgetDestructed(*this);}}Super::NativeDestruct();
}

小结

总体来说,CommonUI的使用不是很理想,相关联的配置内容也比较繁琐,使用的成本较高,支持的也不够全面,大家根据自己的需求来看把。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/842916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

U8G2移植到STM32,SSD13XXXOLED(硬件SPI DMA通讯)

文章目录 一、前言1.1 U8g2的特点1.2 U8G2的优势1.3 U8G2的下载地址1.4 U8g2支持的显示控制器 二、STM32Cubexm SPI DMA配置2.1 SPI设置为半双工模式2.2 SPI DMA设置2.3 oled其他引脚配置 三、移植U8G2框架3.1 精简U8G2库文件3.2 去掉csrc文件夹中无用的驱动文件3.3 文件移动到…

庆余年2火了,却把热爱开源的程序员给坑了

庆余年 2 终于开播了&#xff0c;作为一名剧粉&#xff0c;苦等了五年终于盼来了。开播即爆火&#xff0c;虽然首播的几集剧情有些拖沓&#xff0c;不过也不影响这是一部好剧。 然而&#xff0c;庆余年 2 的爆火&#xff0c;却把 npmmirror 镜像站给坑惨了。npmmirror 镜像站&…

gem5模拟器入门(二)——创建一个简单的配置脚本

本章教程将引导您设置一个简单的 gem5 仿真脚本&#xff0c;并首次运行 gem5。假设您已经完成了gem5模拟器入门&#xff08;一&#xff09;——环境配置-CSDN博客&#xff0c;并成功构建了带有可执行文件 build/X86/gem5.opt 的 gem5。 配置脚本将模拟一个非常简单的系统&#…

LangChain实战 | 3分钟学会SequentialChain怎么传多个参数

SequentialChain参数传递&#xff0c;总结了以下四种类型 参数传递入参出参一 对 一11一 对 多1n多 对 一n1多 对 多nn 0.连接大模型 先选一个llm&#xff0c;参考这篇博客选择一个国内大模型 LangChain连接国内大模型测试|智谱ai、讯飞星火、通义千问 from langchain_comm…

网络侦察技术

网络侦察技术 收集的信息网络侦察步骤搜索引擎检索命令bing搜索引擎Baidu搜索引擎Shodan钟馗之眼(zoomeye) whois数据库&#xff1a;信息宝库查询注册资料 域名系统网络拓扑社交网络跨域拓展攻击 其它侦察手段社会工程学社会工程学常见形式Web网站查询 其它非技术侦察手段总结网…

巅峰对决:OpenAI与Google如何用大模型开创未来

2024年&#xff0c;人工智能领域正引领着一场波澜壮阔的全球技术革命。 5月14日&#xff0c;OpenAI揭开了其新一代多模态人工智能大模型GPT4系列的神秘面纱&#xff0c;其中GPT-4o不仅拥有流畅迷人的嗓音&#xff0c;还展现出幽默、机智和深刻的洞察力……紧接着&#xff0c;在…

linux下cp和mv命令显示进度条

1.查看当前系统下coreutils工具包的版本号&#xff1a; [rootk8s-master ~]# rpm -qa | grep -w coreutils coreutils-8.22-24.el7_9.2.x86_64当前版本为8.22。 因为cp 和 mv 命令由 coreutils 软件包提供&#xff0c;所以需要重新下载 coreutils 软件包配置补丁 2.下载core…

数据结构——顺序表基本操作的实现(初始化、取值、查找、插入)

一、线性表与顺序表的概述 线性表的数据元素的逻辑特征是线性的&#xff0c;是一种典型的线性结构。这样的结构可以借鉴数组&#xff0c;如数组a[10]中&#xff0c;a[1]前一定是a[0]&#xff0c;a[1]后一定是a[2]&#xff0c;首结点&#xff08;a[0]&#xff09;前面无元素&am…

java医院管理系统源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的医院管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 医院管理系统的主要使用者分…

VR法治教育展厅互动体验突破了地域限制

VR全景互动展厅搭建编辑器以其卓越的特点、强大的功能及实际应用中的显著优势&#xff0c;成为企业级VR应用的得力助手。这款软件不仅能够制作逼真的虚拟场景&#xff0c;更能让用户沉浸其中&#xff0c;体验前所未有的真实感。 它拥有高度逼真的视觉效果&#xff0c;采用先进的…

智能单款计划助力品牌利润增长

零售品牌若要在激烈的市场竞争中胜出&#xff0c;季中单款的管理无疑是商品生命周期管理的核心环节之一。而单款计划的制定&#xff0c;首先依赖于对爆款、平销及滞销产品的敏锐洞察。一个利润现象不得不引起我们的关注&#xff1a;爆款产品的销售&#xff0c;往往成为拉动品牌…

svix - webhooks

文章目录 一、关于 SvixClient Library Overview与 Svix 托管服务的区别 二、运行服务器1、部署1.1 Docker1) 使用 Docker Compose2) 独立容器 1.2 预编译的二进制文件1.3 从源代码构建 2、运行时依赖项3、Redis/Valkey 注意事项持久性 Eviction 政策4、服务器配置4.1 配置文件…

express.js--生成token(二)

主要作用是访问路由接口时&#xff0c;生成身份权限信息 下载依赖 npm i express-jwt npm i jsonwebtoken 配置管理 config/index.js module.exports {app: {port: process.PORT || 3000,//jwt密钥jwtSecret: jwtSecret,//token过期时间expiresIn: 60 * 60 * 24,} } con…

从零开始:如何集成美颜SDK和优化美颜接口

今天&#xff0c;小编将从零开始&#xff0c;详细讲解如何集成SDK并优化美颜接口。 一、选择合适的美颜SDK 评估SDK的功能 在评估过程中&#xff0c;可以通过阅读官方文档、查看示例代码以及实际测试来确定SDK是否符合需求。 兼容性和性能 确保其支持你开发的应用平台&…

Android15 Beta更新速览

Android15 Beta更新速览 前台服务变更 前台服务使应用保持活动状态&#xff0c;以便它们可以执行关键且对用户可见的操作&#xff0c;通常以牺牲电池寿命为代价。在 Android 15 Beta 2 中&#xff0c;dataSync 和 mediaProcessing 前台服务类型现在具有约 6 小时的超时时间&a…

内网安全--隧道技术-CS上线本地

免责声明:本文仅做技术交流与学习...请勿非法搞破坏... ---隧道技术:硬刚网络协议,(你不让我走我偏走!) 解决不出网协议上线的问题&#xff08;利用出网协议进行封装出网&#xff09; 代理协议&#xff1a; SOCKS4/5 代理软件&#xff1a; SocksCap Proxifier ProxyChains(…

磁盘管理以及文件系统08

1、为什么要对磁盘进行分区&#xff1f; 业务层面&#xff1a;为满足一定的需求所是做的特定操作。 2、硬盘是什么&#xff0c;以及硬盘的作用 硬盘&#xff1a;计算机的存储设备&#xff0c;一个或者多个带磁性的盘组成的&#xff0c;可以在盘片上进行数据的读写。硬盘的最…

LVGL圆弧、线条、图片、色环、按钮矩阵、文本区域、键盘部件

目录 LVGL圆弧部件 LVGL线条部件 LVGL图片部件 LVGL色环部件 LVGL按钮矩阵部件 LVGL文本区域部件 LVGL键盘部件 LVGL圆弧部件 圆弧部件以弧形滑动的形式来调节、显示某个参数的值。 圆弧部件组成部分&#xff1a; 背景弧&#xff08;LV_PART_MAIN&#xff09; 前景弧&am…

浏览器提示网站不安全怎么办?有什么解决办法吗?

当你在浏览器中访问一个网站时&#xff0c;如果看到提示说该网站不安全&#xff0c;这通常是由于网站没有使用SSL证书或者SSL证书存在问题。SSL证书在这里扮演着非常关键的角色&#xff0c;下面我会详细解释它的作用以及如何解决这类不安全提示。 SSL证书的作用&#xff1a; 1…

【CTF Web】CTFShow web11 Writeup(RCE+PHP+代码审计)

web11 1 阿呆听完自己菜死了&#xff0c;自己呆了。决定修好漏洞&#xff0c;绝对不能让自己再菜死了。 解法 可知 flag 在 config.php。 <?php # flag in config.php include("config.php"); if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/system…