UE Lyra
UI 框架
前言
Lyra项目中的UI功能主要由游戏UI管理子系统(GameUIManagerSubsystem),游戏UI策略(GameUIPolicy),游戏布局UI([PrimaryGameLayout]三个部分构成UI框架,CommonUI插件以及UIExtension插件负责UI层面的使用。
在插件中主要有三个类:
PrimaryGameLayout
GameUIManagerSubsystem
GameUIPolicy
与UI相关的类在Lyra项目中的继承关系如下:
GamePlay
- B_LyraGameInstance(蓝图) -> LyraGameInstance -> CommonGameInstance ->GameInstance
- LyraLocalPlayer -> CommonLocalPlayer ->LocalPlayer
- LyraPlayerController -> CommonPlayerController -> ModularPlayerController -> PlayerController
UIMangaer
LyraUIManagerSubsystem -> GameUIManagerSubsystem ->GameInstanceSubsystem
B_LyraUIPolicy(蓝图) -> GameUIPolicy -> Object
W_OverallUILayout(蓝图)-> PrimaryGameLayout -> CommonUserWidget(CommonUI插件中的类)
- 类作用
- GameUIManagerSubsystem负责GameUIPolicy的配置管理、生命周期等;
- GameUIPolicy负责PrimaryGameLayout的配置管理、生命周期等;
- PrimaryGameLayout负责游戏中UI的分层布局等;
Subsystem
GameUIManagerSubsystem
1.判断创建哪一个游戏UI管理子系统
在ShouldCreateSubsystem函数中会判断子系统是否有子类,如果没子类就创建该子系统,有子类就不创建该子系统,由于Lyra项目创建了LyraUIManagerSubsystem并继承GameUIManagerSubsystem,所以项目会创建LyraUIManagerSubsystem而不会创建GameUIManagerSubsystem.
由于GameUIManagerSubsystem标记了Abstract宏,所以GameUIManagerSubsystem默认不会创建,所以需要自己继承一个或者修改掉插件里的宏定义。
UCLASS(MinimalAPI, Abstract, config = Game) class UGameUIManagerSubsystem : public UGameInstanceSubsystem
2.配置GameUIPolicy(游戏策略)类
在游戏UI管理子系统中有一个GameUIPolicy(游戏策略)类的配置项,配置位置在DefaultGame.ini文件中
3.创建GameUIPolicy(游戏策略)对象
- Initialize
方法 | 参数 | 返回值 | 说明 |
---|---|---|---|
Initialize void Initialize(FSubsystemCollectionBase& Collection) |
Collection :子系统依赖集合 |
void |
初始化子系统,加载默认 UI 策略类并创建策略实例。若未配置策略(CurrentPolicy 为空)且 DefaultUIPolicyClass 有效,则同步加载策略类并创建实例 。 |
Deinitialize void Deinitialize() |
- | void |
反初始化子系统,清空当前 UI 策略(触发策略清理逻辑)。 |
ShouldCreateSubsystem bool ShouldCreateSubsystem(UObject* Outer) |
Outer :外部对象(指向 UGameInstance ) |
bool |
判断是否创建子系统实例: • 专用服务器跳过创建 • 非专用服务器 且 无派生类覆盖时 才创建基类实例(避免多重实例)。 |
NotifyPlayerAdded void NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer) |
LocalPlayer :加入的本地玩家实例 |
void |
处理新玩家加入事件: • 验证玩家指针有效性(ensure ) • 转发事件至当前 UI 策略(CurrentPolicy )。 |
NotifyPlayerRemoved void NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer) |
LocalPlayer :被移除的本地玩家 |
void |
处理玩家移除事件: • 非空检查后转发事件至当前 UI 策略 。 |
NotifyPlayerDestroyed void NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer) |
LocalPlayer :销毁中的玩家实例 |
void |
处理玩家销毁事件: • 非空检查后转发事件至当前 UI 策略(通常用于释放玩家关联资源)。 |
SwitchToPolicy void SwitchToPolicy(UGameUIPolicy* InPolicy) |
InPolicy :新 UI 策略实例 |
void |
切换 UI 策略: • 新旧策略不同时更新 CurrentPolicy • 旧策略自动释放(由引擎垃圾回收管理) • 新策略接管后需手动初始化(如玩家事件绑定) |
LyraUIManagerSubsystem
在LyraUIManagerSubsystem添加了Tick函数,并根据AHUD的bShowHUD布尔值决定游戏布局UI的显示与隐藏。
方法 | 参数 | 返回值 | 说明 |
---|---|---|---|
Initialize void Initialize(FSubsystemCollectionBase& Collection) |
Collection :子系统依赖集合 |
void |
子系统初始化 • 注册 FTicker 委托实现每帧更新 • 通过 CoreTicker 确保帧同步 • 父类初始化后立即执行 |
Deinitialize void Deinitialize() |
- | void |
子系统反初始化 • 移除 FTicker 委托避免无效调用 • 执行父类资源释放 • 确保无内存泄漏 |
Tick bool Tick(float DeltaTime) |
DeltaTime :帧间隔时间 |
bool |
每帧更新逻辑 • 调用 SyncRootLayoutVisibilityToShowHUD 同步可见性 • 始终返回 true 保持持续触发 • 轻量化设计避免性能问题 |
SyncRootLayoutVisibilityToShowHUD void SyncRootLayoutVisibilityToShowHUD() |
- | void |
HUD 与布局可见性同步 • 当 HUD 隐藏时折叠 UI 布局(如过场动画) • 遍历所有本地玩家控制器 • 状态映射: - bShowHUD=true → SelfHitTestInvisible (可交互) - bShowHUD=false → Collapsed (完全隐藏) • 避免冗余更新 |
UIPolicy
GameUIPolicy
在GameUIPolicy(游戏策略)中,有一个游戏主UI布局类的配置项, Lyra创建B_LyraUIPolicy蓝图类并配置游戏主UI布局类。
基础接口
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
GetGameUIPolicy static UGameUIPolicy* GetGameUIPolicy(const UObject* WorldContextObject) |
WorldContextObject :世界上下文对象 |
UGameUIPolicy* |
获取当前场景的 UI 策略实例。通过游戏实例→UI 管理子系统→当前策略的三级查询实现 |
GetOwningUIManager UGameUIManagerSubsystem* GetOwningUIManager() const |
- | UGameUIManagerSubsystem* |
获取所属的 UI 管理子系统(通过 Outer 链追溯) |
GetWorld virtual UWorld* GetWorld() const override |
- | UWorld* |
获取关联的游戏世界(重写 AActor 方法) |
GetRootLayout UPrimaryGameLayout* GetRootLayout(const UCommonLocalPlayer* LocalPlayer) const |
LocalPlayer :目标本地玩家 |
UPrimaryGameLayout* |
获取指定玩家的根布局实例(不存在时返回 nullptr) |
玩家生命周期管理
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
NotifyPlayerAdded void NotifyPlayerAdded(UCommonLocalPlayer* LocalPlayer) |
LocalPlayer :加入的玩家 |
void |
处理玩家加入事件: • 监听控制器设置事件 • 创建/添加玩家专属布局 |
NotifyPlayerRemoved void NotifyPlayerRemoved(UCommonLocalPlayer* LocalPlayer) |
LocalPlayer :移除的玩家 |
void |
处理玩家移除事件: • 从视口移除布局 • SingleToggle 模式自动转移控制权到主玩家 |
NotifyPlayerDestroyed void NotifyPlayerDestroyed(UCommonLocalPlayer* LocalPlayer) |
LocalPlayer :销毁的玩家 |
void |
处理玩家销毁事件: • 解绑事件委托 • 释放布局资源 |
布局视口管理
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
AddLayoutToViewport void AddLayoutToViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout) |
|
void |
添加布局到视口: • 设置玩家上下文(FLocalPlayerContext) • ZOrder=1000 确保顶层显示 • 触发添加回调 |
RemoveLayoutFromViewport void RemoveLayoutFromViewport(UCommonLocalPlayer* LocalPlayer, UPrimaryGameLayout* Layout) |
|
void |
从视口移除布局: • 检查 Slate 控件有效性 • 记录移除日志(含泄漏警告) |
控制权管理
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
RequestPrimaryControl void RequestPrimaryControl(UPrimaryGameLayout* Layout) |
Layout :请求控制权的布局 |
void |
分屏模式下切换控制权: • 仅 SingleToggle 模式生效 • 停用当前活跃布局 • 激活目标布局 |
布局创建接口
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
GetLayoutWidgetClass virtual TSubclassOf<UPrimaryGameLayout> GetLayoutWidgetClass(UCommonLocalPlayer* LocalPlayer) |
LocalPlayer :目标玩家 |
TSubclassOf<UPrimaryGameLayout> |
获取布局控件类(可被子类覆盖): • 默认返回 LayoutClass 配置的软引用 • 支持按玩家差异化布局 |
Widget
CommonUserWidget
构造函数与基础设置
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
UCommonUserWidget UCommonUserWidget(const FObjectInitializer&) |
ObjectInitializer :对象初始化器 |
- | 初始化控件属性: • 编辑器模式下设置面板分类为 Common UI • 默认开启指针输入消耗(bConsumePointerInput=true ) |
输入子系统访问
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
GetInputSubsystem UCommonInputSubsystem* GetInputSubsystem() |
- | UCommonInputSubsystem* |
获取当前本地玩家的输入子系统实例(用于处理平台差异化输入) |
GetUISubsystem UCommonUISubsystemBase* GetUISubsystem() |
- | UCommonUISubsystemBase* |
获取游戏实例中的通用UI子系统(管理全局UI状态) |
GetOwnerSlateUser TSharedPtr<FSlateUser> GetOwnerSlateUser() |
- | TSharedPtr<FSlateUser> |
获取关联的Slate用户对象(分屏场景中隔离玩家输入) |
输入事件处理
(所有函数均根据 bConsumePointerInput
决定是否拦截事件)
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
SetConsumePointerInput void SetConsumePointerInput(bool) |
bInConsumePointerInput :是否消耗事件 |
void |
启用时拦截所有鼠标/触摸事件(防止穿透点击底层UI) |
NativeOnMouseButtonDown FReply NativeOnMouseButtonDown(...) |
InGeometry :控件几何信息 InMouseEvent :鼠标事件 |
FReply |
鼠标按下事件处理(返回 Handled 时阻止事件传递) |
NativeOnMouseWheel FReply NativeOnMouseWheel(...) |
同上 | FReply |
鼠标滚轮事件处理(与滚动接收器协同工作) |
NativeOnTouchStarted FReply NativeOnTouchStarted(...) |
InGeometry :控件几何信息 InGestureEvent :触摸事件 |
FReply |
触摸开始事件处理(移动设备专用) |
动作绑定管理
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
RegisterUIActionBinding FUIActionBindingHandle RegisterUIActionBinding(FBindUIActionArgs) |
BindActionArgs :绑定参数(动作类型/输入事件) |
FUIActionBindingHandle |
注册UI动作: • 通过 CommonUIActionRouter 统一管理 • 若 bDisplayInActionBar=true 强制显示在动作栏 |
RemoveActionBinding void RemoveActionBinding(FUIActionBindingHandle) |
ActionBinding :绑定句柄 |
void |
移除单个动作绑定(自动从路由系统解绑) |
AddActionBinding void AddActionBinding(FUIActionBindingHandle) |
ActionBinding :绑定句柄 |
void |
动态添加已存在的绑定(用于UI重组) |
滚动接收器管理
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
RegisterScrollRecipient void RegisterScrollRecipient(const UWidget&) |
AnalogScrollRecipient :目标滚动控件 |
void |
注册滚动接收器: • 允许将滚动手势委托给子控件(如嵌套的 ScrollBox ) • 支持多级嵌套容器 |
UnregisterScrollRecipient void UnregisterScrollRecipient(const UWidget&) |
同上 | void |
取消滚动接收器注册 |
RegisterScrollRecipientExternal void RegisterScrollRecipientExternal(const UWidget*) |
AnalogScrollRecipient :目标滚动控件指针 |
void |
外部注册(空指针安全检查) |
生命周期管理
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
OnWidgetRebuilt void OnWidgetRebuilt() |
- | void |
控件重建时回调: • 清理无效动作绑定 • 向 ActionRouter 重新注册控件和滚动接收器 |
NativeDestruct void NativeDestruct() |
- | void |
控件销毁时回调: • 通知 ActionRouter 清理绑定 • 确保无内存泄漏 |
编辑器集成
函数签名 | 参数 | 返回值 | 说明 |
---|---|---|---|
GetPaletteCategory const FText GetPaletteCategory() |
- | FText |
获取控件面板分类(固定返回 Common UI 分类) |
PrimaryGameLayout
- 这就是游戏的布局UI,在这个UI中会添加不同的栈UI控件,来达到UI分层的目的。Lyra项目中将UI分为游戏层,游戏菜单层,菜单层,modol层。
方法 | 参数 | 返回值 | 说明 |
---|---|---|---|
GetPrimaryGameLayoutForPrimaryPlayer static UPrimaryGameLayout* GetPrimaryGameLayoutForPrimaryPlayer(const UObject* WorldContextObject) |
WorldContextObject :世界上下文对象 |
UPrimaryGameLayout* |
获取主玩家根布局: • 通过 UGameplayStatics::GetGameInstance 获取游戏实例 • 调用 GetPrimaryGameLayout(PlayerController) 实现 |
GetPrimaryGameLayout static UPrimaryGameLayout* GetPrimaryGameLayout(APlayerController* PlayerController) |
PlayerController :玩家控制器实例 |
UPrimaryGameLayout* |
通过控制器获取布局: • 转换控制器为 UCommonLocalPlayer • 调用本地玩家版 GetPrimaryGameLayout |
GetPrimaryGameLayout static UPrimaryGameLayout* GetPrimaryGameLayout(ULocalPlayer* LocalPlayer) |
LocalPlayer :本地玩家实例 |
UPrimaryGameLayout* |
通过本地玩家获取布局: 1. 验证本地玩家有效性 2. 通过游戏实例获取 UGameUIManagerSubsystem 3. 从当前策略获取关联布局 |
SetIsDormant void SetIsDormant(bool InDormant) |
InDormant :是否休眠 |
void |
设置布局休眠状态: • 分屏场景暂停非活动玩家 UI 渲染 • 状态变更时记录日志并触发 OnIsDormantChanged 回调 • 设计意图:节省资源(如折叠 UI/停用输入) |
RegisterLayer void RegisterLayer(FGameplayTag LayerTag, UCommonActivatableWidgetContainerBase* LayerWidget) |
|
void |
注册 UI 层级: • 绑定过渡事件 OnWidgetStackTransitioning • 禁用过渡动画(确保手柄焦点正确) • 支持四层结构: - Game:实时 HUD(血条/弹药) - GameMenu:局内菜单(暂停) - Menu:主菜单系统 - Modal:模态弹窗(确认框) |
OnWidgetStackTransitioning void OnWidgetStackTransitioning(UCommonActivatableWidgetContainerBase* Widget, bool bIsTransitioning) |
|
void |
处理控件栈过渡事件: • 过渡开始时暂停输入(SuspendInputForPlayer ) • 过渡结束时恢复输入(ResumeInputForPlayer ) • 令牌系统管理并发过渡 |
FindAndRemoveWidgetFromLayer void FindAndRemoveWidgetFromLayer(UCommonActivatableWidget* ActivatableWidget) |
ActivatableWidget :待移除控件 |
void |
跨层级移除控件: • 遍历所有层级容器移除目标控件 • 典型场景:动态关闭弹窗/场景切换清理 |
GetLayerWidget UCommonActivatableWidgetContainerBase* GetLayerWidget(FGameplayTag LayerName) |
LayerName :层级标识 |
UCommonActivatableWidgetContainerBase* |
获取层级容器: • 通过 GameplayTag 检索注册的容器控件 |
Loading UI
CommonUI插件以及UIExtension等插件
主要是 CommonLoadingScreen 插件 :
主Manager : ULoadingScreenManager
方法 | 参数 | 返回值 | 说明 |
---|---|---|---|
Initialize | Collection: FSubsystemCollectionBase& |
void |
初始化子系统,注册地图加载/卸载委托(PreLoadMapWithContext 、PostLoadMapWithWorld )。 |
Deinitialize | 无 | void |
清理资源:停止输入屏蔽、移除加载控件、注销委托并禁用后续 Tick 。 |
ShouldCreateSubsystem | Outer: UObject* |
bool |
仅在客户端创建子系统(服务器无需加载界面)。 |
Tick | DeltaTime: float |
void |
每帧更新加载屏幕状态,并递减心跳日志计时器。 |
ShowLoadingScreen | 无 | void |
显示加载屏幕:创建控件、屏蔽输入、禁用世界渲染并调整性能设置(如延长心跳超时阈值)。 |
HideLoadingScreen | 无 | void |
隐藏加载屏幕:移除控件、恢复性能设置、触发垃圾回收并恢复卡顿检测。 |
ChangePerformanceSettings | bEnabingLoadingScreen: bool |
void |
性能优化控制: • 启用时:设置着色器缓存为Fast 模式、挂起卡顿检测、禁用世界渲染 • 禁用时:恢复默认设置。 |
StartBlockingInput | 无 | void |
注册输入预处理器(FLoadingScreenInputPreProcessor ),屏蔽非编辑器下的用户输入。 |
StopBlockingInput | 无 | void |
注销输入预处理器,恢复用户输入响应。 |
UpdateLoadingScreen | 无 | void |
核心状态机:调用 ShouldShowLoadingScreen 决策显示/隐藏,管理心跳检测与日志。 |
CheckForAnyNeedToShowLoadingScreen | 无 | bool |
综合检测条件: 1. 控制台强制显示 2. 地图加载中(bCurrentlyInLoadMap ) 3. 无缝旅行(IsInSeamlessTravel ) 4. 玩家控制器未就绪 5. 外部处理器要求显示。 |
UIExtension
UI 扩展系统 API 文档
方法 | 参数 | 返回值 | 说明 |
---|---|---|---|
Initialize |
Collection: FSubsystemCollectionBase& |
void |
初始化子系统,注册地图加载/卸载委托(PreLoadMapWithContext 、PostLoadMapWithWorld )。 |
Deinitialize |
无 | void |
清理资源:停止输入屏蔽、移除加载控件、注销委托并禁用后续 Tick 。 |
RegisterExtensionPoint |
|
FUIExtensionPointHandle |
注册扩展点: • 校验标签有效性、回调绑定及数据类非空 • 支持上下文对象匹配(ContextObject ) • 触发 NotifyExtensionPointOfExtensions 通知现有扩展。 |
RegisterExtensionAsData |
|
FUIExtensionHandle |
注册数据扩展: • 校验标签与数据有效性 • 支持优先级排序(Priority ) • 自动通知关联扩展点(NotifyExtensionPointsOfExtension )。 |
UnregisterExtension |
ExtensionHandle: FUIExtensionHandle |
void |
注销扩展: • 安全检查句柄所属子系统 • 触发 EUIExtensionAction::Removed 回调 • 清理空扩展列表。 |
NotifyExtensionPointsOfExtension |
|
void |
核心通知逻辑: • 遍历扩展标签的父级层级 • 匹配时调用扩展点回调(ExactMatch /PartialMatch ) • 使用副本避免遍历时修改。 |
CreateExtensionRequest |
Extension: TSharedPtr<FUIExtension> |
FUIExtensionRequest |
构建请求结构: • 封装句柄、标签、优先级、数据及上下文对象。 |
K2_RegisterExtensionAsWidget |
|
FUIExtensionHandle |
蓝图兼容控件注册: • 内部调用 RegisterExtensionAsData • 自动处理空上下文。 |
关键类型说明
类型 | 作用 |
---|---|
FUIExtensionPointHandle |
扩展点句柄: • Unregister() 注销扩展点 • IsValid() 检查句柄有效性。 |
FUIExtensionHandle |
扩展句柄: • Unregister() 注销扩展 • IsValid() 检查句柄有效性。 |
FUIExtensionRequest |
扩展请求: • 包含数据、优先级、上下文对象等运行时信息。 |