UMG、Slate 与 Common UI
UMG, Slate & Common UI
Slate 是 UE 平台无关的 C++ 声明式 UI 框架(编辑器即用它构建),UMG 是在其之上的可视化控件蓝图层(UE 5.8 API:UWidget 是所有暴露给 UObject 的被包装 Slate 控件的基类),Common UI 插件再在 UMG 之上补充多层界面、输入路由、手柄导航、平台按键图标和样式资产。官方 UMG 最佳实践强调事件驱动更新而非绑定/Tick、固定分辨率与 DPI 制作、用 Scale Box 而非 Render Transform 做永久缩放。
三层关系
| 层 | 是什么 | 谁用 |
|---|---|---|
| Slate | 完全自定义、平台无关的 C++ UI 框架;编辑器本身就是用它写的,也可做游戏内 UI | 引擎/工具程序员 |
| UMG (Unreal Motion Graphics) | 可视化 UI 编辑器与控件蓝图 (Widget Blueprint) | 设计师、UI 美术、玩法程序员 |
| Common UI | 插件:在 UMG 之上提供多层 UI、输入路由、手柄导航与样式资产 | 跨平台商业项目 |
Slate 的定位来自官方(Slate Overview);官方 UI 文档首页把 UMG 编辑器参考和「用 Slate 框架编程 UI」并列(UMG Best Practices / Creating User Interfaces)。UMG 控件是对 Slate 控件的 UObject 封装(UWidget 在运行时创建底层 SWidget)——UE 5.8 API 原文称 UWidget 是「所有暴露给 UObject 的被包装 Slate 控件的基类」,TakeWidget() 取得或构造底层 Slate 控件,各子类实现 RebuildWidget() 构造 SWidget,SynchronizeProperties() 把属性同步到原生控件(UWidget / CommonActivatableWidget / CommonActivatableWidgetStack API)。这一层关系解释了为什么 Widget Reflector(Slate 工具)能调试 UMG 界面。
Slate
- 声明式语法:
SLATE_BEGIN_ARGS、SLATE_ATTRIBUTE、SLATE_EVENT、SNew(...)与+ SVerticalBox::Slot()[...]嵌套组合。 - 样式 (Styles):集中定义画刷、字体并复用。
- 输入:鼠标键盘及可动态修改的按键绑定。输出:使用目标无关的渲染图元,通过 RHI 绘制,因此可跑在引擎支持的所有平台。
- 布局图元、可停靠标签页框架、Widget Reflector 调试分析工具、直接访问引擎与编辑器。(以上 Slate Overview,官方页部分文字沿袭 UE4 表述)
UMG 实践(官方最佳实践)
- 少量频繁变化的值可用属性绑定 (Property Binding);复杂界面应改用事件驱动更新,而不是每帧检查。
- 以目标分辨率和 DPI Scale 1.0 制作所有控件蓝图,否则多分辨率缩放会不一致。
- 纹理不要自带大量留白;把边框、角、背景拆开用 Border 模式平铺。
- 槽位 Auto(按需)vs Fill(尽量占满)。
- Render Transform 不参与布局,适合临时动画;永久缩放用 Scale Box。
- 控件蓝图即 User Widget,可复用。
- 优化:能用纹理就不用材质;用事件而非绑定或 Tick;缓存不常变的控件;用 Widget Reflector(Ctrl+Shift+W)查看统计。(以上 UMG Best Practices / Creating User Interfaces)
属性绑定每帧求值,本质是隐式 Tick,这与 委托与 Tick 机制 中「用委托/事件代替轮询」的原则一致(关联为通识);UI 的 CPU 开销可在 UE5 性能分析 中观察。
Common UI
官方定位:构建类似 Fortnite 的多层、跨平台 UI 的工具箱,包含:
- 一组提供常用游戏功能的 Common UI 控件;
- 把样式从 UI 元素中分离的样式数据资产;
- 输入路由 (Input Routing) 系统,让控件选择性可交互,便于管理多层 UI 的焦点;
- 平台专属元素(如对应手柄的按键图标);
- 手柄的方向导航 (Cardinal Navigation)。 另有与 增强输入 结合使用的专门文档。(Common UI)
可激活控件 (Activatable Widget):UCommonActivatableWidget 是可以在生命周期内被「激活/停用」而不必销毁或重建底层 SWidget 的控件基类。适用场景:界面需要开关但不想重建,需要内置「返回」行为(返回上一级、关闭弹窗),或者它是输入路由树上的一个节点。默认情况下它不会在构造时自动激活,也不接收返回操作;如果设为返回处理者 (Back Handler),收到返回时会停用但不销毁。关键属性:auto_activate、is_back_handler、is_modal(不论实际父级,都作为输入路由的根,用于模态弹窗屏蔽父级操作)、input_mapping(激活/停用时自动添加/移除增强输入映射上下文,见 增强输入)、auto_restore_focus;可重写 GetDesiredFocusTarget / GetDesiredInputConfig。注意:所有控件停用后,Common UI 不会自动恢复之前的输入配置。(UWidget / CommonActivatableWidget / CommonActivatableWidgetStack API,Python API 5.7)
控件栈:UCommonActivatableWidgetStack 只显示并激活栈顶控件,其余都停用;栈顶控件停用后自动移除,前一个控件重新显示并激活;RootContent 永不移除(Python API 5.4)。所以菜单层级通常用「推入栈 + 返回即停用」实现。Common UI Overview 概念页仍未能抓取;下面的输入路由和「层」来自 Epic 程序员 Nick Darnell 在 State of Unreal 2022 上的 Lyra 技术讲座(官方 YouTube 频道,UE 5.0 时期;Lyra Cross-platform UI Development (Nick Darnell, Epic Games) — Tech Talk, State of Unreal 2022)。
输入路由:Slate 输入从获得焦点的控件沿父级向上传递,最终到达视口 (Viewport),再经视口客户端交给玩家控制器。Common UI 要求项目把视口客户端换成 UCommonGameViewportClient 的子类,让输入先进入 UI 动作路由器 (CommonUIActionRouterBase,一个 LocalPlayer 子系统,见 子系统)。路由器决定是否触发已注册的 UI 动作,不处理的输入才继续传给玩家控制器。UI 动作(返回、应用、取消)定义在数据表里,分别映射手柄键和键盘键。
谁拿到输入:只有「最靠根的已激活控件」及其已激活的子控件会收到路由来的输入,Common UI 按绘制层级最高的可激活控件来判定。调试用 CommonUI.DumpActivatableTree。
层 (Layer):Common UI 只提供可激活控件和容器(栈与队列),不规定界面层级怎么组织;Lyra 自己在 CommonGame 插件中实现了一套(不属于引擎本体):
flowchart TD M["GameUIManagerSubsystem<br/>监听本地玩家加入/离开"] --> P["UI Policy(LyraUIPolicy)"] P --> L["每个玩家一个 PrimaryGameLayout"] L --> G["UI.Layer.Game 栈:HUD"] L --> GM["UI.Layer.GameMenu 栈:背包等游戏内菜单"] L --> MN["UI.Layer.Menu 栈:设置、主菜单"] L --> MD["UI.Layer.Modal 栈:确认弹窗"]
图中管理子系统通过 UI 策略为每个本地玩家创建一个主布局,布局里有四个用 Gameplay 标签注册的 CommonActivatableWidgetStack,从游戏 HUD 到模态弹窗,越往下层级越高。(讲座中层名为 Game / Game Menu / Menu / Modal;完整标签名 UI.Layer.* 来自 Lyra 工程,讲座原文只说「用 gameplay tag 注册」,标签写法参见 Gameplay 标签深入。)
要点与坑(均出自讲座):
- 同一层里推入新控件会顶替当前控件,新控件停用后回到上一个,所以主菜单、设置、制作人员名单都放在 Menu 层,彼此替换。
- 用 Add To Viewport 不维护层级,弹窗可能被 HUD 盖住,这正是需要层的原因。
- 推入层时用软类引用并异步加载,否则硬引用会让一个界面把所有界面都拉进内存(见 资产管理与异步加载);加载期间暂停输入,防止玩家乱按误操作到下层界面。
- 手柄断开提示这类必须常驻的界面不要放进栈里(可能被别的弹窗顶掉),应作为隐藏控件放在总布局中。
CommonHardwareVisibilityBorder按平台特性和当前输入类型标签控制控件显隐(如 PC 才显示「退出游戏」)。
选型
| 需求 | 建议 |
|---|---|
| 编辑器工具、细节面板定制 | Slate |
| 一般游戏 HUD/菜单 | UMG |
| 多层菜单 + 手柄/主机 + 按键提示 | UMG + Common UI |
| 极致性能的自绘控件 | 自定义 Slate 控件再包成 UMG(通识) |
UI 管理器常写成 LocalPlayer 子系统,以便分屏时每个玩家一份(见 子系统,此为通识用法)。
出处
- Slate Overview UE 5.8 文档
- UMG Best Practices / Creating User Interfaces UE 5.8 文档
- Common UI UE 5.8 文档
- UWidget / CommonActivatableWidget / CommonActivatableWidgetStack API UWidget UE 5.8 API;CommonActivatableWidget Python API 5.7;Stack Python API 5.4
- Lyra Cross-platform UI Development (Nick Darnell, Epic Games) — Tech Talk, State of Unreal 2022 Epic 官方 State of Unreal 2022 技术讲座(Lyra,UE 5.0 时期)