4.2 模块划分:Runtime 与 EditorOnly 的边界
为什么要拆
一个常见错误:工具功能全塞在 Runtime 模块里(因为「写着方便」)。后果:
- 打包时编辑器代码进包体(包体变大,且 Editor 模块在打包时会编译报错/被排除导致功能消失)
- 服务器/客户端构建因为 Editor 依赖而炸
铁律:编辑器工具必须放 EditorOnly 模块(Type=Editor 或 Type=EditorNoCommandlet),打包构建自动剔除。「模块属性」(https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/module-properties-in-unreal-en-f860)里有全部合法 Type。
三种典型插件结构
① 纯编辑器工具:
Modules: [ { Name: "XEditor", Type: "Editor" } ]
② 运行时功能 + 编辑器工具(如「技能系统插件」运行时有逻辑,编辑器里能可视化编辑):
Modules: [
{ Name: "XRuntime", Type: "Runtime" },
{ Name: "XEditor", Type: "Editor", "LoadingPhase": "PostEngineInit" }
]
XEditor 的 Build.cs 依赖 XRuntime。
③ 完全独立工具集(连运行时数据都没有):
Modules: [ { Name: "XTools", Type: "Editor" } ]
拆分的决策依据
| 代码 | 放哪 |
|---|---|
| 游戏运行时执行的逻辑/组件/Actor | Runtime 模块 |
| 只在编辑器运行的 UI/工具/校验 | Editor 模块 |
| 运行时类 + 编辑器可视化 + 公共结构 | Runtime + Editor 各一份(Editor 依赖 Runtime) |
| DataAsset 类既要游戏读又要编辑器编辑 | Runtime(UObject 类必须运行时可构造) |
| 编辑器技术版/Uncooked 运行的辅助 | Developer 模块(Cook 后保留、打包包含但开发版才用) |
DataAsset 的坑:继承 UPrimaryDataAsset 的数据资产类,如果定义在 Editor 模块里,打包后游戏想读这份数据会找不到类 → 必须把数据资产类放 Runtime 模块。
构建目标的过滤
打包时(Server/Client/Default Target),Editor 模块被 BuildConfiguration 自动跳过。调试 Editor 模块是否「污染」运行时的方法:构建一个 Shipping/Server 版本。如果能编过且功能正常,说明拆分干净。
"运行时也能用"的灰区
有些代码「编辑器和运行时都要」(如插件的一个数学库、工具类)。三种选项:
- 放 Runtime 模块,Editor 模块依赖它(最简单、推荐)
- 复制两份(禁止——会分叉)
- 放 Engine Developer 模块(自建插件做不到)
验证清单
- [ ] 打包后游戏能跑(Editor 工具不影响运行时)
- [ ] Shipping/Server 编译通过
- [ ] Editor 模块里没有需要 Runtime 反射的 UObject 类
心得:拆 Runtime/Editor 的时机是在写第一行代码之前。先确定了「这段逻辑哪个端要跑」,模块就定了;后期「把 Runtime 的东西挪到 Editor」是真体力活(头文件移动 + 依赖链修一遍)。
