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。

插件与项目的关系

插件可以:

  1. 项目专属(放 <Project>/Plugins/):跟项目版本走
  2. 引擎插件(放 <Engine>/Plugins/):所有项目可用(升级引擎时要重编)
  3. 市场/分发插件:打包成 .upack 给别人用

经验:团队内部工具放项目 Plugins 目录最省心——代码版本随项目走,不涉及引擎升级时的兼容。除非真的多个项目共用,才提到引擎级。

新手常踩的两个坑

  1. Build.cs 依赖漏写:编译报「无法解析的外部符号」基本都是缺模块依赖。报错类里 带 Module 名(如 FAssetRegistryModule),查它属于哪个模块加上即可。
  2. 头文件可见性:Public/Private 分目录不是装饰——Private 里的类对外不可见,跨模块引用必须放 Public。放错会导致别的模块引用失败。

心得:插件工程的起步模板可以做一个「空的带 EditorUtilityWidget + 一个按钮 + 日志输出」的最小插件存档,再开新工具直接复制改名——比从向导点一遍快,也统一了规范(命名、版本号、描述格式)。