c4.4Subsystem|EnhancedInput利用路由显示鼠标
大约 7 分钟
前置条件
安装插件CommonUI
实践
日常心血来潮,想给我的游戏菜单背景加一个角色查看功能,即:可以操作UI的同时控制游戏模型(比如旋转、缩放等)。
悲剧发生了!
可咱用的是CommonUI啊!
Lyra的实现
后来我发现Lyra通过封装InputConfig和GameMouseCaptureMod两个核心模块完美解决了上面的问题。
1. 核心配置 InputConfig:
enum class EWidgetInputMode : uint8
{
Default,
GameAndMenu,
Game,
Menu
};
- Default & Menu 鼠标只能操作UI
- GameAndMenu :鼠标能操作UI,也能移动游戏视口
- Game:鼠标无法操作UI,只能移动游戏视口。
2.核心配置 Game Mouse Capture Mode :
enum class EMouseCaptureMode : uint8
{
/** 不捕获鼠标输入 */
NoCapture,
/** 当视口被点击时,永久捕获鼠标,并消耗引起捕获的初始鼠标按下事件,以防止被玩家输入处理 */
CapturePermanently,
/** 当视口被点击时,永久捕获鼠标,并允许玩家输入处理引起捕获的初始鼠标按下事件 */
CapturePermanently_IncludingInitialMouseDown,
/** 在鼠标按下期间捕获鼠标,鼠标抬起时释放 */
CaptureDuringMouseDown,
/** 仅在右鼠标按钮按下时捕获,而不捕获其他任何鼠标按钮 */
CaptureDuringRightMouseDown,
};
3. 其他配置 Consume Pointer Input:
很多人应该和我一样,一开始压根不明白这个节点的意思。
4.其他配置 Action Domain Override:
- Display in ActionBar :简单来说就是对应的按键UI显示在指定的动作栏中。类似下面的效果:
源码剖析
ActivatableWidget.h
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CommonActivatableWidget.h"//引入CommonActivatableWidget
#include "ExorcistActivatableWidget.generated.h"//UHT生成
//InputConfig结构体
struct FUIInputConfig;
UENUM(BlueprintType)
enum class EExorcistWidgetInputMode : uint8
{
Default, // 默认模式
GameAndMenu, // 游戏和菜单模式
Game, // 游戏模式
Menu // 菜单模式
};
// An activatable widget that automatically drives the desired input config when activated
// 一个可激活的小部件,在激活时会自动驱动所需的输入配置
UCLASS(Abstract, Blueprintable)
class UExorcistActivatableWidget : public UCommonActivatableWidget//继承自UCommonActivatableWidget
{
GENERATED_BODY()
public:
UExorcistActivatableWidget(const FObjectInitializer& ObjectInitializer);//构造函数,
public:
//~UCommonActivatableWidget interface
//~UCommonActivatableWidget 接口
virtual TOptional<FUIInputConfig> GetDesiredInputConfig() const override;// 获取所需的输入配置
//~End of UCommonActivatableWidget interface
//~End of UCommonActivatableWidget 接口
#if WITH_EDITOR
virtual void ValidateCompiledWidgetTree(const UWidgetTree& BlueprintWidgetTree, class IWidgetCompilerLog& CompileLog) const override;
#endif
protected:
/** 激活此 UI 时要使用的所需输入模式,例如您是否希望按键仍然传递到游戏/玩家控制器? */
/** The desired input mode to use while this UI is activated, for example do you want key presses to still reach the game/player controller? */
UPROPERTY(EditDefaultsOnly, Category = Input)
EExorcistWidgetInputMode InputConfig = EExorcistWidgetInputMode::Default;
/** 游戏接收输入时所需的鼠标行为。 */
/** The desired mouse behavior when the game gets input. */
UPROPERTY(EditDefaultsOnly, Category = Input)
EMouseCaptureMode GameMouseCaptureMode = EMouseCaptureMode::CapturePermanently;
};
ActivatableWidget.cpp
// 版权所有 Epic Games, Inc. 保留所有权利。
#include "UI/ExorcistActivatableWidget.h" // 引入ExorcistActivatableWidget头文件
#include "Editor/WidgetCompilerLog.h" // 引入WidgetCompilerLog头文件
#include UE_INLINE_GENERATED_CPP_BY_NAME(ExorcistActivatableWidget) // 使用宏生成内联的C++代码
#define LOCTEXT_NAMESPACE "Exorcist" // 定义命名空间 "Exorcist"
UExorcistActivatableWidget::UExorcistActivatableWidget(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
TOptional<FUIInputConfig> UExorcistActivatableWidget::GetDesiredInputConfig() const
{
switch (InputConfig) // 根据InputConfig的值进行不同的处理
{
case EExorcistWidgetInputMode::GameAndMenu: // 如果InputConfig是GameAndMenu模式
return FUIInputConfig(ECommonInputMode::All, GameMouseCaptureMode); // 返回一个包含All输入模式和GameMouseCaptureMode的FUIInputConfig
case EExorcistWidgetInputMode::Game: // 如果InputConfig是Game模式
return FUIInputConfig(ECommonInputMode::Game, GameMouseCaptureMode); // 返回一个包含Game输入模式和GameMouseCaptureMode的FUIInputConfig
case EExorcistWidgetInputMode::Menu: // 如果InputConfig是Menu模式
return FUIInputConfig(ECommonInputMode::Menu, EMouseCaptureMode::NoCapture); // 返回一个包含Menu输入模式和NoCapture的FUIInputConfig
case EExorcistWidgetInputMode::Default: // 默认情况
default:
return TOptional<FUIInputConfig>(); // 返回一个空的TOptional<FUIInputConfig>
}
}
#if WITH_EDITOR
void UExorcistActivatableWidget::ValidateCompiledWidgetTree(const UWidgetTree& BlueprintWidgetTree, class IWidgetCompilerLog& CompileLog) const
{
Super::ValidateCompiledWidgetTree(BlueprintWidgetTree, CompileLog); // 调用父类的ValidateCompiledWidgetTree函数
if (!GetClass()->IsFunctionImplementedInScript(GET_FUNCTION_NAME_CHECKED(UExorcistActivatableWidget, BP_GetDesiredFocusTarget)))
{
if (GetParentNativeClass(GetClass()) == UExorcistActivatableWidget::StaticClass())
{
CompileLog.Warning(LOCTEXT("ValidateGetDesiredFocusTarget_Warning", "GetDesiredFocusTarget wasn't implemented, you're going to have trouble using gamepads on this screen."));
}
else
{
//TODO - Note for now, because we can't guarantee it isn't implemented in a native subclass of this one.
CompileLog.Note(LOCTEXT("ValidateGetDesiredFocusTarget_Note", "GetDesiredFocusTarget wasn't implemented, you're going to have trouble using gamepads on this screen. If it was implemented in the native base class you can ignore this message."));
}
}
}
#endif
#undef LOCTEXT_NAMESPACE // 取消命名空间 "Exorcist"
Route|输入路由子系统
UCommonUIActionRouterBase : public ULocalPlayerSubsystem
FUIInputConfig::FUIInputConfig(ECommonInputMode InInputMode, EMouseCaptureMode InMouseCaptureMode, bool bInHideCursorDuringViewportCapture)
if (UCommonUIActionRouterBase* ActionRouter = LocalPlayer->GetSubsystem<UCommonUIActionRouterBase>())
{
ActionRouter->SetActiveUIInputConfig(FUIInputConfig(ECommonInputMode::Game, EMouseCaptureMode::CapturePermanently));
}
ECommonInputMode 源码
UENUM(BlueprintType)
enum class ECommonInputMode : uint8
{
Menu UMETA(Tooltip = "Input is received by the UI only"),
Game UMETA(Tooltip = "Input is received by the Game only"),
All UMETA(Tooltip = "Input is received by UI and the Game"),
MAX UMETA(Hidden)
};
EMouseCaptureMode 源码
UENUM()
enum class EMouseCaptureMode : uint8
{
/** Do not capture the mouse at all */
NoCapture,
/** Capture the mouse permanently when the viewport is clicked, and consume the initial mouse down that caused the capture so it isn't processed by player input */
CapturePermanently,
/** Capture the mouse permanently when the viewport is clicked, and allow player input to process the mouse down that caused the capture */
CapturePermanently_IncludingInitialMouseDown,
/** Capture the mouse during a mouse down, releases on mouse up */
CaptureDuringMouseDown,
/** Capture only when the right mouse button is down, not any of the other mouse buttons */
CaptureDuringRightMouseDown,
};