1.3 插件与模块:uplugin 结构和模块划分
模块是 UE 的代码组织单元
UE 的工程由模块组成——Project 本身是一个或多个模块,插件是额外的模块包。理解「模块」是理解插件的前提(官方:「虚幻引擎模块」https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/unreal-engine-modules-8352、「创建 Gameplay 模块」https://www.wanxiangyundang.top/read/unreal-engine-5-8-docs/how-to-make-a-gameplay-module-74f0)。
一个最小模块:
MyModule/
├── MyModule.Build.cs // 构建规则:依赖、包含路径
├── MyModule.h // 模块导出宏
├── MyModule.cpp // IModuleInterface 实现
└── Public/ Private/ // 头文件与实现的可见性
// Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" });
核心规则:模块间依赖单向声明(Build.cs 里写),不能循环。模块边界 = 编译依赖边界 = 重构时的「砖块」。分模块的收益是编译速度与代码归属清晰,代价是多一些样板文件。
uplugin 文件
插件的清单:
{
"FileVersion": 3,
"Version": 10,
"VersionName": "1.0",
"FriendlyName": "我的工具",
"Description": "批量处理工具",
"Category": "Editor",
"CreatedBy": "Your Name",
"EnabledByDefault": false,
"CanContainContent": true,
"Modules": [
{
"Name": "MyToolEditor",
"Type": "Editor", // EditorOnly:不进打包
"LoadingPhase": "PostEngineInit"
}
]
}
两个关键字段的经验值:
| 字段 | 建议 | 理由 |
|---|---|---|
| Modules[].Type | 工具类用 Editor,运行时功能用 Runtime |
Editor 模块不进包体,打包安全 |
| LoadingPhase | 多数 PostEngineInit;需要改引擎启动流程才用更早 |
越早加载,依赖越多越容易出加载顺序问题 |
| EnabledByDefault | 团队工具 true;给外部用户的看情况 | true 时项目编译自动带上 |
模块划分的建议
我自己的划分经验:
- Editor 工具:一个 EditorOnly 模块全装。小工具别拆模块——拆成 5 个模块不提升任何东西,只增加维护量。
- 工具 + 运行时功能(如插件既有编辑器工具又有运行时组件):拆
XRuntime+XEditor两个模块,Editor 依赖 Runtime。 - 大型插件(功能域差异大):按功能域拆,每个域 Runtime+Editor。
插件与项目的关系
插件可以:
- 项目专属(放
<Project>/Plugins/):跟项目版本走 - 引擎插件(放
<Engine>/Plugins/):所有项目可用(升级引擎时要重编) - 市场/分发插件:打包成 .upack 给别人用
经验:团队内部工具放项目 Plugins 目录最省心——代码版本随项目走,不涉及引擎升级时的兼容。除非真的多个项目共用,才提到引擎级。
新手常踩的两个坑
- Build.cs 依赖漏写:编译报「无法解析的外部符号」基本都是缺模块依赖。报错类里 带 Module 名(如
FAssetRegistryModule),查它属于哪个模块加上即可。 - 头文件可见性:Public/Private 分目录不是装饰——Private 里的类对外不可见,跨模块引用必须放 Public。放错会导致别的模块引用失败。
心得:插件工程的起步模板可以做一个「空的带 EditorUtilityWidget + 一个按钮 + 日志输出」的最小插件存档,再开新工具直接复制改名——比从向导点一遍快,也统一了规范(命名、版本号、描述格式)。
