UMG 深入
自动关联目录:UMG 深入
1. 定位
UMG 是构建在 Slate 之上的 UObject 封装层:UWidget 包装 SWidget,让 UI 可以用蓝图编辑、能被 GC 管理、能绑定属性。
一句话定位:UMG 换来的是"可视化编辑 + 蓝图可用",代价是多一层对象开销与 GC 参与。
2. 层次
UUserWidget(UObject 层,蓝图可编辑)
└── 内部持有 SWidget 树(Slate 层,真正绘制)| 概念 | 说明 |
|---|---|
UUserWidget | UI 的基本单位,可被加入视口 |
UWidgetBlueprint | 可视化编辑的 UI |
UWidgetTree | Widget 的层级结构 |
NativeConstruct / NativeDestruct | C++ 侧的构造/销毁回调 |
NativeTick | C++ 侧每帧(默认关闭) |
3. 生命周期
CreateWidget
→ NativeConstruct / Construct(蓝图)
→ (加入视口)AddToViewport
→ Tick(若开启)
→ RemoveFromParent
→ NativeDestruct
→ 无引用后被 GC 回收| 回调 | 时机 |
|---|---|
NativeConstruct | 构造时(可能多次——重建时会再调) |
NativePreConstruct | 编辑器的设计视图里也会跑 |
NativeTick | 每帧(默认 bCanEverTick 相关) |
NativeDestruct | 销毁时 |
NativeConstruct 可能被多次调用——不要在里面做只能做一次的初始化(如注册全局委托),否则重复注册。
NativePreConstruct 在编辑器预览里也会跑,里面不要放依赖运行时状态的代码,否则编辑器报错。
4. 绑定与刷新
| 方式 | 说明 |
|---|---|
| 属性绑定(Property Binding) | 蓝图里绑定,每帧求值——很贵 |
| 委托驱动 | 数据变化时更新(推荐) |
| GAS 属性监听 | GetGameplayAttributeValueChangeDelegate(见 UAttributeSet) |
属性绑定是 UI 性能的头号杀手:每个绑定每帧都要执行一次(哪怕值没变)。
正确做法:
ASC->GetGameplayAttributeValueChangeDelegate(HealthAttr)
.AddLambda([this](const FOnAttributeChangeData& D) { SetHealthText(D.NewValue); });5. 常用控件与代价
| 控件 | 说明 | 代价 |
|---|---|---|
Canvas Panel | 自由定位 | 低 |
Horizontal/Vertical Box | 线性布局 | 低 |
Overlay | 层叠 | 低 |
Size Box / Scale Box | 尺寸控制 | 低 |
Scroll Box | 滚动(不复用条目) | 长列表很贵 |
List View | 支持条目复用 | 长列表用它 |
Rich Text Block | 富文本(图文混排) | 较高 |
Progress Bar / Image / Text Block | 基础展示 | 低 |
6. 代价与权衡
| 设计 | 收益 | 代价 | 什么时候不该用 |
|---|---|---|---|
| UMG | 可视化、蓝图可用 | 多一层 + GC 参与 | 极高性能列表可考虑 Slate |
| 属性绑定 | 最省事 | 每帧求值 | 任何高频/复杂绑定都应改委托 |
| Tick 刷新 | 简单 | 每帧重建布局 | 一律改用事件驱动 |
| Scroll Box 长列表 | 简单 | 不复用,条目多时崩 | 用 ListView |
| Rich Text | 表现丰富 | 解析与布局成本 | 简单文本用 TextBlock |
7. 踩坑与排查
| 坑 | 现象 | 怎么验证 |
|---|---|---|
| 用属性绑定做所有刷新 | UI 卡 | 改为委托驱动 |
| Tick 里刷新文本 | 每帧重建布局 | 改事件驱动 |
NativeConstruct 里注册全局委托 | 重复注册 | 加保护或用 Initialize 语义 |
NativePreConstruct 依赖运行时 | 编辑器报错 | 只放纯表现 |
| 长列表用 ScrollBox | 卡顿与内存 | 改 ListView |
| UI 里做游戏逻辑判定 | 可被绕过(见 Security) | 判定在服务端 |
| Widget 不销毁 | 泄漏 | 检查引用与 RemoveFromParent |
8. 排查顺序
1. UI 卡?→ 先看有没有属性绑定与 Tick 刷新
2. 改委托驱动 / ListView 复用
3. 见 [UI 性能](/cc065871.html)
4. 布局错?→ 检查 Box/SizeBox 与层级参考
- UI 性能 · Slate 实践 · 输入与焦点
- UAttributeSet(属性变化委托) · Security
许可协议:CC BY
作者:Davids
本文链接:https://hustjjd.github.io/9afcfce6.html
更新于:2026年10月10日