前言

Unreal Engine重要概念 - UMG(Unreal Motion Graphics)

Unreal Engine重要概念 - UMG(Unreal Motion Graphics) - 知乎

基本信息

1. 控件(Widgets)

控件是 UI 的基本单位,就像拼积木一样:

控件名称 说明 常见用途
Text 显示一段文字 显示血量、任务说明、名字等
Image 显示图片或图标 技能图标、头像、背景等
Progress Bar 进度条,可以用来显示某个数值变化的条 血条、经验条、技能冷却条
Button 可以点击的按钮 菜单选项、对话选项
Canvas Panel 允许你自由定位其他控件 通常用于主界面、HUD 布局
Vertical Box / Horizontal Box 用来垂直/水平排列控件 对话选项、物品列表、任务列表
Overlay 控件层叠显示(上下叠加) 技能图标 + 冷却进度 + 数量

2. 层级结构(控件树)

UMG 的界面是“树形结构”的,最外层是容器(比如 Canvas Panel),内部再嵌套各种控件。

比如一个任务提示 UI:

  • Canvas Panel(整体布局)
    • Border(背景框)
      • Text(任务内容)
      • Text(进度百分比)

这种结构就像网页的 HTML 一样,嵌套使用。

3. 绑定数据 & 交互逻辑

UMG 不只是静态画面,它能与游戏里的数据互动,比如:

  • 显示玩家当前生命值
  • 点击按钮进入下一关
  • 显示背包中有哪些物品
  • 玩家进入新区域后显示提示

通过 数据绑定,UMG 会实时显示游戏里的状态变化。

主要控件

🧩 UMG核心控件分类

一、面板控件(Panel Widgets)

控件名称 功能描述 关键特性
Canvas Panel 自由布局容器 ✅ 支持锚点(Anchors)和绝对定位
✅ 动态适配不同分辨率
Grid Panel 表格布局容器 🔳 行/列自动分割
🔄 子控件可跨行/列合并
Horizontal Box 水平排列容器 ➡️ 自动横向排列子控件
📏 支持Size to Content(自适应内容尺寸)
Vertical Box 垂直排列容器 ⬇️ 自动纵向排列子控件
⚖️ 支持比例填充(填满剩余空间)
Overlay 层叠容器 📚 支持多层控件叠加
🔼 通过Z-Order控制显示层级
Safe Zone 异形屏适配容器 📱 自动避开刘海/挖孔区域
🛡️ 支持四向独立安全区设置

二、输入控件(Input Widgets)

控件名称 类型 交互特性
Button 基础按钮 🔘 四态样式(Normal/Hovered/Pressed/Disabled)
🔗 点击事件绑定
ComboBox(String) 下拉选择框 📋 文本选项列表
⌨️ 支持键盘快速选择
Editable Text 单行输入框 ✏️ 实时文本编辑
🚫 自带输入校验基础功能
Editable Text(Multi-Line) 多行输入框 ↵ 支持回车换行
📄 大段文本输入优化
Slider 滑动条 🎚️ 线性值调节(0.0-1.0)
📈 支持步进(Step)设置
Spin Box 数字调节框 🔢 精确数值输入
➕➖ 附带增减按钮

三、通用控件(Common Widgets)

控件名称 渲染能力
Image 🖼️ 支持纹理/材质/纯色块
🎨 九宫拉伸(Box Hospitality) + 染色(Tint)
Text Block 🔤 静态文本渲染
⚠️ 不支持富文本(需第三方插件)
Rich Text Block ✨ 富文本渲染
✅ 混合样式/内嵌图片/超链接
Progress Bar ████████ 进度可视化
🌀 支持Marquee模式(无进度值的滚动动画)
Border 🟦 容器装饰控件
🎯 定义背景+边距+边框样式

四、高级功能控件

控件名称 核心用途
Widget Switcher 🔀 多页面切换器
💡 实现选项卡(Tab)界面
Retainer Box ⚡ 渲染性能优化
📉 降低动画更新频率(如每秒10帧而非60帧)
Named Slot 🧩 预留插入点
🚀 运行时动态加载子控件
Radial Slider 🎡 圆形滑块
⭕ 旋转角度输入(适用方向盘/罗盘UI)

五、开发者工具

功能 操作路径
创建自定义控件 右键 → User InterfaceWidget Blueprint
控件嵌套 将控件蓝图拖入其他UMG画布
动态控件生成 使用Create Widget+AddChild节点

⚙️ 性能优化专项控件

1
2
3
4
graph TD
A[Size Box] -->|约束动态元素尺寸| B(减少布局计算)
C[Invalidation Box] -->|局部重绘区域| D(降低GPU负担)
E[Retainer Box] -->|控制渲染频率| F(优化动画性能)

8.13

TimeLine动画

【虚幻5UI进阶教程】如何制作UI动画哔哩哔哩bilibili

创建WBP –> 窗口 –>动画

这边动画类似timeline

这边能k的动画 有: visibility(可见度) Cursor(锚点)Transform(位移)Scale(缩放)

k动画: 关键帧在timeline上回车键

  • 蓝图节点PlayAnimation
    • Target – 默认self
    • In Animation 动画
    • Start at Time 开始time
    • Num Loops to Play 是否Loop
    • Play Mode Forward–快进(从前往后) Reverse–翻转(从后往前) PingPong–乒乓(从前往后+从后往前)
    • Playback Speed 播放速率
    • Restore State bool (动画停止时将控件恢复到动画预设置状态?不重要)
    • return UMGSequence Player UMG播放器

UMG与Slate

Unreal Motion Graphics(UMG)是虚幻引擎中用于创建用户界面的高级框架,但其底层实现依赖于Slate框架渲染管线优化技术。以下是UMG底层的核心机制解析:

🔧 1. 核心架构:UMG与Slate的关系

  • UMG是Slate的封装:UMG基于Slate框架构建,通过组合(Composition)而非继承(Inheritance)实现对Slate控件的封装。每个UMG控件(如UButtonUTextBlock)内部包含对应的Slate控件(如SButtonSTextBlock3,5
  • 类命名规范:
    • UMG控件:以U开头(如UWidget),继承自UObject,支持蓝图编辑和垃圾回收。
    • Slate控件:以S开头(如SWidget),直接处理布局、渲染和输入事件,不依赖蓝图系统3,5
  • 设计目标:UMG提供可视化编辑器,降低UI开发门槛;Slate则提供高性能、跨平台的底层支持,用于编辑器界面和复杂控件3

🎨 2. 渲染管线:从控件到GPU指令

UMG的渲染分为三个阶段,最终生成GPU可处理的渲染批次:

阶段 关键数据结构 功能 性能影响
1. 生成绘制元素 FSlateDrawElement 存储控件的绝对空间信息(位置、尺寸、裁剪区域)和渲染数据 控件复杂度影响生成开销
2. 创建渲染批次 FSlateRenderBatch 将FSlateDrawElement转换为顶点/索引数据,准备GPU指令 纹理切换和材质复杂度是关键瓶颈
3. 合批优化 FSlateRenderBatch合并 合并相同材质/纹理的批次,减少Draw Call 显著提升渲染效率3
  • 控件遍历流程:采用深度优先递归遍历,父控件(如UCanvasPanel)先处理布局信息,子控件生成FSlateDrawElement时继承父级变换3
  • 动态数据处理:通过FSlateDataPayload子类(如FSlateTextPayload)存储控件专属数据(如文本内容),避免内存浪费3

⚡ 3. 事件处理与数据绑定

  • 事件驱动机制:
    • Slate的SWidget直接处理输入事件(点击、悬停),通过委托(Delegate)将事件传递给UMG的UWidget5,8
    • UMG支持蓝图事件绑定(如按钮点击),底层转换为Slate事件回调2,8
  • 数据绑定原理:
    • 属性绑定:将控件属性(如进度条数值)关联到C++变量或蓝图函数,通过Tick周期检查更新(可能引发性能问题)5
    • 委托绑定:通过自定义事件触发数据更新,避免逐帧检查5

⚙️ 4. 性能优化技术

  • 全局无效化(GlobalInvalidation):
    • 原理:缓存未变化的FSlateDrawElement,仅重新生成修改过的控件数据,减少CPU计算量。
    • 限制:仅在非编辑器环境(如打包后进程)生效3
  • 合批优化:
    • 合并相同材质的渲染批次,降低Draw Call。
    • 避免频繁切换纹理或材质状态3
  • 控件裁剪与折叠:
    • 设置VisibilityCollapsed而非Hidden,完全移除控件布局计算1,3

🚀 5. 高级特性与扩展

  • 自定义Slate控件:
    • 直接继承SWidget实现高性能控件(如编辑器工具),但需C++开发3,5
  • 3D模型集成:
    • 通过SceneCapture2D捕捉3D场景到Render Target,转换为材质后嵌入UMG,用于角色展示或动态UI6,7
  • 多平台适配:E
    • Common UI插件扩展UMG,支持多平台输入路由和响应式布局,简化跨设备UI开发1,4

💎 总结

UMG的底层本质是Slate框架的蓝图友好型封装,通过分层设计平衡易用性与性能:

  • Slate层:处理渲染、布局、输入事件,追求极致效率;
  • UMG层:提供可视化编辑、数据绑定、动画等高级功能;
  • 优化核心:合批渲染、全局无效化、动态数据分离是性能关键3,5。 开发者可通过理解底层机制,在复杂UI项目中针对性优化(如减少Draw Element生成、定制Slate控件),实现高性能动态界面。

UI底层与优化

https://www.bilibili.com/video/BV1Wt4y1N7qg/?spm_id_from=333.999.0.0&vd_source=c652138f4b254d841de9c920e214ad92

img

  • 和目标视觉相比,自己的蓝图是不是要素过多?
  • 全部展开之后,自己的蓝图是不是特别深
  • 蓝图是否加载了大量的其他蓝图?
  • 是否有废弃布局?
  • 是否有大量CanvasPanel?
  • 是否有大量的WidgetSwitcher?

对于复杂UMG(比如背包、地图的父蓝图),应以CanvasPanel为根节点,辅以Overlay为子节点。

对于简单UMG(如Tips、图标等),所有节点应以Overlay为主。(除非特殊原因)

合批

(99+ 封私信 / 18 条消息) 【UEInside】 Slate合批机制剖析 - 知乎

UE源码分析:Slate组织&渲染 - 张宏港 - 博客园

UMG 生命周期

https://www.cnblogs.com/sin998/p/15490311.html

  • Create
    • CreateWidget – 生成处理开始
    • NewObject – 与UWidget相关的UObject的生成
    • Initialize – 只调用一次
  • OnScreen
    • AddToViewport
    • TakeWidget – SWidget生成开始
    • RebuildWidget – SWidget生成
    • Synchronize – Properties –UWidget和SWidget 同周期
    • PreConstruct – Construction Script
    • Construct – Begin Play
  • Tick
    • SlatePrepass – 包含子widget应用
    • Paint – 包含子 widget 的 创建 Render Batch
    • AddElement
  • Remove
    • RemoveFrom Viewport
    • Destruct
    • ReleaseSlate Resources – SWidget 的删除
  • Destroy
    • BeginDestroy – Widget 开始销毁
    • Destructor – 析构UWidget
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public:
//一个控件,测试BindWidget是多久在哪个生命周期调用赋值的
UPROPERTY(Meta = (BindWidget))
UTextBlock * Txt_Main = nullptr;
public:
//所关注的生命周期的方法
virtual bool Initialize() override;
virtual void NativeOnInitialized() override;
virtual void AddToScreen(ULocalPlayer* LocalPlayer, int32 ZOrder) override;
virtual void NativePreConstruct() override;
virtual void NativeConstruct() override;
virtual void NativeTick(const FGeometry& MyGeometry, float InDeltaTime) override;
virtual void RemoveFromParent() override;
virtual void NativeDestruct() override;
virtual void BeginDestroy() override;
virtual void FinishDestroy() override;

在Game中

1
bool Initialize()` -> `void NativeOnInitialized()` -> `void AddToScreen(ULocalPlayer* LocalPlayer, int32 ZOrder)` -> `void NativePreConstruct()` -> `void NativeConstruct()` -> `void NativeTick()` -> `void RemoveFromParent()` -> `void NativeDestruct()

PIE

UMG打开:bool Initialize() -> void NativePreConstruct()

UMG添加控件:bool Initialize() -> void NativePreConstruct()

UMG拖拽控件:bool Initialize() -> void NativePreConstruct()

UMG删除控件:bool Initialize() -> void NativePreConstruct()

修改Widget内容,什么都没调用,点击编译才有调用。

UMG编译:void BeginDestroy() -> void RemoveFromParent() -> void FinishDestroy() -> bool Initialize() -> void NativePreConstruct()

其中,前面四个会反复调用。

UMG保存:bool Initialize()

获得控件和绑定事件

BindWidget这个宏是在Initialize()中完成绑定的。

绑定控件也可以手动在Initialize()NativeOnInitialized()NativeConstruct()中做。

那么怎么选择呢?

通过上面的分析可以知道在PIE中会调用Initialize()NativeOnInitialized(),如果你想在PIE中就绑定(例如在PIE中预览自定义UMG就需要)并执行更新的预览的代码的话就在这里绑定。

如果你只是想在游戏中运行时才使用的话那么就在NativeConstruct()中绑定。

绑定事件在NativeOnInitialized()中做。不过要注意需要先得到控件再绑定事件,并且绑定的那个函数要加上UFUNCTION()宏。

在PIE中实时预览自定义UMG

重写Initialize():初始化并获得控件,别忘了避免空指针异常。

重写NativePreConstruct():这里写预览相关的代码,例如设置颜色、字体等。

https://zhuanlan.zhihu.com/p/636153935)

Lyra CommomUI

https://link.zhihu.com/?target=https%3A//www.bilibili.com/video/BV1mT4y167Fm/%3Fspm_id_from%3D444.41.list.card_archive.click%26vd_source%3Dc652138f4b254d841de9c920e214ad92