| name | reactumg-architecture |
| description | ReactUMG 架构原理深度解析。仅供 PlanReactUMG 和 DebugReactUMG Agent 显式调用,不应在日常开发中直接激活。包含三层架构、Reconciler、hostConfig 等底层实现机制。 |
ReactUMG 架构原理深度解析
本文档详细说明 ReactUMG 插件如何将 TypeScript 编写的 React 组件转换为 UE5 的 UMG Widget。
1. 核心思想
ReactUMG = React Reconciler + Puerts + UMG
ReactUMG 是一个桥接层,让开发者可以使用 React 的方式编写 UE5 UI,同时保持原生性能和完整类型安全。
关键特性
- ✅ 完全拥抱 React 生态 - 标准 react-reconciler,支持 Hooks、Context、Ref
- ✅ 零性能开销 - Puerts 直接操作 C++ 内存,无序列化/反序列化
- ✅ 极简 C++ 层 - 只有 2 个类 + 5 个函数
- ✅ 完整类型安全 - 2406 行类型定义
- ✅ 自动事件管理 - 自动检测 Delegate 类型,自动绑定/清理
2. 三层架构
┌─────────────────────────────────────────────────────────┐
│ TypeScript Layer (开发者编写层) │
│ ───────────────────────────────────────────────────── │
│ - 开发者编写 React.Component │
│ - 使用 JSX 语法描述 UI 结构 │
│ - 调用 ReactUMG.render() 渲染 │
│ - 管理状态和业务逻辑 │
└───────────────────────┬─────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ React-Reconciler Layer (协调层,核心逻辑) │
│ ───────────────────────────────────────────────────── │
│ - hostConfig 定义如何操作 Widget │
│ - UEWidget 包装 UE::Widget │
│ - UEWidgetRoot 包装 UReactWidget │
│ - 处理 VDOM diff/update/mount/unmount │
│ - 管理 Widget 树的生命周期 │
└───────────────────────┬─────────────────────────────────┘
│ Puerts Bridge (JS ↔ C++)
▼
┌─────────────────────────────────────────────────────────┐
│ C++ Layer (UE5 原生层) │
│ ───────────────────────────────────────────────────── │
│ - UReactWidget (根容器 UserWidget) │
│ - UUMGManager (蓝图函数库,工具函数) │
│ - UWidget::SynchronizeProperties() (同步到 Slate) │
└─────────────────────────────────────────────────────────┘
各层职责
TypeScript Layer(开发者层)
- 职责:提供 React 开发体验
- 工具:React 组件、JSX、Hooks、State 管理
- 入口:
ReactUMG.render(<MyComponent />)
React-Reconciler Layer(协调层)
- 职责:将 React VDOM 映射到 UMG Widget 树
- 核心:
hostConfig定义 Widget 操作,UEWidget包装原生 Widget - 优势:使用标准 react-reconciler,兼容 React 生态
C++ Layer(原生层)
- 职责:提供最小化的容器和工具函数
- 设计:极简设计,只有 2 个类
- 集成:与 UE5 UMG 系统无缝对接
3. 核心组件详解
C++ 层组件
UReactWidget(根容器)
文件位置:Plugins/ReactUMG/Source/ReactUMG/ReactWidget.h/.cpp
职责:
- 继承
UUserWidget,作为整个 React UI 的根容器 - 单根限制:只允许一个 RootSlot,对应 React 的单根要求
- 持有
WidgetTree,管理整个 Widget 树 - 提供
AddChild/RemoveChild供 TypeScript 调用
UUMGManager(工具函数库)
文件位置:Plugins/ReactUMG/Source/ReactUMG/UMGManager.h/.cpp
职责:
- 蓝图函数库,暴露静态函数给 TypeScript 调用
CreateReactWidget():创建 UReactWidget 实例SynchronizeWidgetProperties():同步 Widget 属性到 Slate 层SynchronizeSlotProperties():同步 Slot 属性到 Slate 层
为什么需要 Synchronize?
UE5 的 UMG 是双层架构:
- UWidget(逻辑层)- C++ 对象,存储属性
- SWidget(Slate 渲染层)- 实际渲染的组件
当通过 TypeScript 修改 UWidget 的属性时:
- 使用
puerts.merge直接修改 C++ 对象的内存 - UWidget 的属性值已改变,但 SWidget 还未更新
- 必须调用
SynchronizeProperties()将改动同步到 Slate 层
TypeScript 层组件
UEWidget(Widget 包装类)
文件位置:Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts:35-159
职责:
- 包装 UE::Widget,提供统一的操作接口
- 管理属性设置、事件绑定、子节点操作
- 处理 Slot 延迟设置(Slot 由父容器创建)
- 自动清理事件监听,防止内存泄漏
UEWidgetRoot(根容器包装)
文件位置:Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts:161-192
职责:
- 包装
UReactWidget,提供 TypeScript 访问接口 - 管理根 Widget 的添加/移除
- 控制 Widget 在视口中的显示/隐藏
hostConfig(React Reconciler 配置)
文件位置:Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts:195-283
职责:
- 定义 React Reconciler 如何操作 Widget 树
- 处理 Widget 的创建、更新、删除、挂载
- 连接 React VDOM 和 UMG Widget 树
4. 完整更新流程
渲染流程(TypeScript → UMG)
1. 开发者调用:
ReactUMG.render(<TaleSelectPanel />)
↓
2. React Reconciler 递归处理 VDOM:
hostConfig.createInstance("CanvasPanel", props)
→ new UEWidget("CanvasPanel", props)
→ new UE.CanvasPanel()
↓
3. 构建 Widget 树结构:
hostConfig.appendInitialChild(parent, child)
→ parent.appendChild(child)
→ parent.nativePtr.AddChild(child.nativePtr)
↓
4. 应用 Widget 属性:
puerts.merge(widget.nativePtr, {Text: "Hello"})
→ 直接修改 C++ 对象属性
↓
5. 同步属性到 Slate 层:
UE.UMGManager.SynchronizeWidgetProperties(...)
→ widget->SynchronizeProperties()
→ Slate 层更新
↓
6. 显示到屏幕:
hostConfig.resetAfterCommit(root)
→ root.addToViewport(0)
更新流程(Props 变化)
1. 状态变化触发:
this.setState({count: 2})
↓
2. Reconciler Diff Props:
hostConfig.prepareUpdate(...)
→ !deepEquals(...) → return true
↓
3. 提交更新:
hostConfig.commitUpdate(instance, ...)
→ instance.update(oldProps, newProps)
→ puerts.merge(nativePtr, {Text: "2"})
→ UE.UMGManager.SynchronizeWidgetProperties(...)
↓
4. 屏幕更新:
Slate 层重新渲染
事件流(UMG → TypeScript)
1. 初始化时绑定事件:
<Button OnClicked={this.handleClick} />
→ UEWidget.bind("OnClicked", this.handleClick)
→ nativePtr.OnClicked.Add(this.handleClick)
↓
2. 用户交互触发事件:
用户点击按钮
→ UButton::OnClicked.Broadcast()
↓
3. Puerts 桥接转发:
C++ Delegate.Broadcast()
→ Puerts 拦截并转发到 JS
→ this.handleClick() 在 TypeScript 中执行
↓
4. TypeScript 处理事件:
handleClick() { this.setState({clicked: true}) }
→ 触发 Re-render
5. 完整更新流程(4 阶段)
阶段 0:触发源(用户代码)
├── setState() - 状态更新
├── forceUpdate() - 强制更新
├── Context 变化 - Provider value
└── ReactUMG.render() - 初次挂载
⚠️ 私有成员变量改变不触发任何更新
↓
阶段 1:父组件视角(发起者)
父组件 render()
↓
生成新的 Virtual DOM: <Child propA={newValue} />
↓
Props 流向子组件 (oldProps → newProps)
📌 "Props Change" 是父组件 render 的结果,不是原因
↓
阶段 2:子组件视角(响应者)
Props Changed
↓
shouldComponentUpdate?
├── false → 停止更新
└── true → 子组件 render() → Diff 算法
📌 这就是 "Props Change → SCU → Render"
↓
阶段 3:Commit 阶段(hostConfig)
key/type 变了?
├── 是 → 销毁重建 (removeChild + createInstance + appendChild)
└── 否 → ref 变了?
├── 是 → ref 重绑定 (getPublicInstance)
└── 否 → props 变了?(deepEquals)
├── 是 → 属性更新
│ prepareUpdate() → commitUpdate()
│ puerts.merge(nativePtr, props)
│ SynchronizeWidgetProperties()
└── 否 → 无操作
关键结论:
• React 组件的 Render(生成 VDOM)≠ UE Widget 的 Update(实际修改属性)
• Props Change 发生在两个 Render 之间:父组件 Render 后、子组件 Render 前
• 私有成员变量的改变永远不会触发任何更新
6. 关键技术点
puerts.merge - 高性能属性设置
优势:
- 减少 JS ↔ C++ 跨界调用次数
- 支持嵌套对象深度合并
- Puerts 内部优化,性能接近原生
SynchronizeProperties - 双层架构同步
为什么需要同步?
- TypeScript 通过
puerts.merge修改 UWidget 属性 - Slate 层的 SWidget 不会自动更新
- 必须调用
SynchronizeProperties()触发同步
Slot 延迟设置
关键点:
- Slot 属性在
init()时暂存,不立即应用 - 父容器
AddChild()后设置nativeSlot,触发 setter - Setter 中应用 Slot 配置并同步
事件自动绑定与清理
避免内存泄漏:
- 每次绑定事件时,存储对应的清理函数
- Widget 销毁时,自动调用所有清理函数
- 防止 C++ Delegate 持有已销毁的 JS 函数引用
lazyloadComponents - 自定义 Widget 支持
使用场景:
- 复杂的自定义 UI 组件(在 UE 编辑器中设计)
- 动画蓝图驱动的 Widget
- 复用 Blueprint 团队的成果
7. 设计亮点
极简 C++ 层
统计:
- 2 个类:UReactWidget, UUMGManager
- 5 个函数:CreateReactWidget, AddChild, RemoveChild, SynchronizeWidgetProperties, SynchronizeSlotProperties
- 总代码量:< 150 行
零性能开销
无序列化/反序列化:
- Puerts 直接修改 C++ 对象内存,接近原生性能
- 一次
puerts.merge设置多个属性,减少跨界调用
完整类型安全
类型定义文件:Typing/react-umg/index.d.ts(2406 行)
覆盖范围:
- ✅ 所有 UMG 组件的 Props 接口
- ✅ 所有 Slot 类型的属性接口
- ✅ UE 类型的 TypeScript 映射
- ✅ 事件回调的参数类型
完全拥抱 React 生态
支持所有 React 特性:
- ✅ Hooks:
useState,useEffect,useContext,useRef等 - ✅ Context API:跨组件共享状态
- ✅ Ref:访问 Widget 实例
- ✅ Fragment:
<></> - ✅ 条件渲染:
{condition && <Widget />} - ✅ 列表渲染:
{items.map(item => <Widget key={item.id} />)}
自动事件管理
bind()时存储清理函数unbindAll()在 Widget 销毁时自动调用- 防止 C++ Delegate 持有悬挂指针
8. 关键源码文件
ReactUMG 插件
C++ 层:
Plugins/ReactUMG/Source/ReactUMG/ReactWidget.h/.cppPlugins/ReactUMG/Source/ReactUMG/UMGManager.h/.cpp
TypeScript 层:
Plugins/ReactUMG/TypeScript/react-umg/react-umg.ts
类型定义
Typing/react-umg/index.d.ts(2406 行)- React-UMG 组件类型Typing/ue/index.d.ts- UE5 类型映射
项目案例
TypeScript/src/debug-panel/DebugPanel.tsx- 调试面板(复杂布局)TypeScript/src/tale-select-panel/index.tsx- 剧本选择(居中布局)TypeScript/src/formation-panel/index.tsx- 编队面板(底部布局)TypeScript/src/game-operation-window/- 运营界面(完整示例)
9. UE 开发者理解指南
render() 不是 CreateWidget()
// ⚠️ UE 开发者常见误解
render() {
return (
<CanvasPanel> // ← 不是每次都创建新的!
<DragPreview /> // ← React 会检查 key 决定复用还是重建
</CanvasPanel>
);
}
// React 的实际行为:
// 第一次 render → 创建 CanvasPanel、创建 DragPreview
// 第二次 render → CanvasPanel 无变化则复用,DragPreview 检查 key
// - key 相同 → 复用,只更新 props
// - key 不同 → 销毁旧的,创建新的
React vs UE 更新模式对比
| 概念 | UE (C++) | React (TypeScript) |
|---|---|---|
| 创建组件 | CreateWidget<T>() 真的创建 |
<Component /> 只是描述 |
| 更新位置 | Widget->SetPosition() |
<Component Slot={{...}} /> |
| 组件复用 | 手动管理,同一个指针 | React 自动,key 相同则复用 |
| 强制重建 | RemoveChild + CreateWidget |
改变 key |
| 每帧更新 | NativeTick() |
setState() → render() |
10. 总结
ReactUMG 通过极简的 C++ 层和完整的 TypeScript 协调层,成功实现了 React 到 UMG 的无缝桥接:
- 三层架构清晰:开发者层(React)→ 协调层(Reconciler)→ 原生层(UMG)
- 核心组件职责明确:UReactWidget(容器)、UUMGManager(工具)、UEWidget(包装)、hostConfig(协调)
- 数据流完整:渲染、更新、事件流程清晰可追溯
- 关键技术精妙:puerts.merge、SynchronizeProperties、Slot 延迟、事件自动管理
- 设计理念先进:极简 C++、零开销、类型安全、拥抱生态
这种架构让开发者可以用 React 的方式编写 UE5 UI,同时保持原生性能和完整类型安全!
Version: v1.0 Last Updated: 2025-12-23