UMG 深入

自动关联目录:UMG 深入

1. 定位

UMG 是构建在 Slate 之上的 UObject 封装层:UWidget 包装 SWidget,让 UI 可以用蓝图编辑、能被 GC 管理、能绑定属性。

一句话定位:UMG 换来的是"可视化编辑 + 蓝图可用",代价是多一层对象开销与 GC 参与。

2. 层次

UUserWidget(UObject 层,蓝图可编辑)
   └── 内部持有 SWidget 树(Slate 层,真正绘制)
概念说明
UUserWidgetUI 的基本单位,可被加入视口
UWidgetBlueprint可视化编辑的 UI
UWidgetTreeWidget 的层级结构
NativeConstruct / NativeDestructC++ 侧的构造/销毁回调
NativeTickC++ 侧每帧(默认关闭)

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 与层级

参考