Game Features 与模块化玩法
Game Features & Modular Gameplay
Game Features 与 Modular Gameplay 两个插件让开发者把一个玩法功能做成独立的插件,可以单独启用或停用。功能本身由一个 GameFeatureData 资产里的 Action 列表描述,最常用的是 Add Components:把插件里的组件按需挂到已有的 Actor 类上。这样主工程的代码保持干净,无关功能之间不会意外产生依赖,对需要长期增减功能的线上产品尤其重要。
为什么要这样做
官方列出的好处:保持项目代码干净可读;避免无关功能之间意外的交互或依赖,这对随时间改变功能集的线上产品 (live products) 尤其重要。(Game Features and Modular Gameplay in Unreal Engine)它把 模块、插件与构建系统 中的插件机制用到玩法层:主工程只提供基础 Actor,具体功能由插件「注入」。
搭建
- 启用 Game Features 和 Modular Gameplay 插件(都在 Gameplay 分类下),然后重启。
- 新建插件时选择 Game Feature (Content Only),插件必须保存在项目的
/Plugins/GameFeatures/目录下。 - 在插件内容目录中新建一个数据资产,类选 GameFeatureData,名字与插件相同。
- 在它的 Actions 数组里添加 Action。首次创建 GameFeatureData 后要重启一次编辑器,因为该类资产在编辑器启动时加载;之后修改无需重启。(Game Features and Modular Gameplay in Unreal Engine)
Action 类型
| Action | 作用 |
|---|---|
| Add Cheats | 注册 Cheat Manager 扩展,添加调试作弊命令;Shipping 构建中不会实例化 |
| Add Components | 把组件按需添加到指定的 Actor 子类上;最常用 |
| Add Data Registry / Data Registry Source | 添加数据注册表,或往已有注册表里添加数据表 |
| Add World Partition Content | 为功能添加 世界分区 内容 |
(Game Features and Modular Gameplay in Unreal Engine)
Add Components 的工作方式
sequenceDiagram participant A as 主工程 Actor(如 MyPawn) participant M as UGameFrameworkComponentManager participant F as Game Feature 插件 A->>M: BeginPlay 中调用 AddReceiver(this) F->>M: 功能激活:Add Components Action M->>A: 给匹配的 Actor 添加插件里的组件
上图中,Actor 先在 BeginPlay 里把自己注册为「接收者」,功能激活时,组件管理器再把插件里的组件加给匹配的 Actor。
- Actor 必须向 GameInstance 子系统
UGameFrameworkComponentManager注册:GetGameInstance()->GetSubsystem<UGameFrameworkComponentManager>()->AddReceiver(this),一般写在 BeginPlay 里。(Game Features and Modular Gameplay in Unreal Engine)组件管理器本身就是一个 子系统。 - 可以指定组件只在客户端、只在服务器或两端都添加(默认两端都添加)。
- 组件应来自插件本身,并承担这个功能的全部逻辑和数据;与主工程 Actor 的交互越少,功能越容易移植到别的项目。
- 坑:Add Components 不支持用基类
AActor作目标,系统会忽略这部分配置;应该选最窄的那组需要该组件的 Actor 子类。没有共同父类时,就配多个 Add Components Action。(Game Features and Modular Gameplay in Unreal Engine)
插件状态机
UGameFeaturesSubsystem 是一个引擎子系统 (UEngineSubsystem),为每个 Game Feature 插件维护一个状态机对象 (UGameFeaturePluginStateMachine),按插件 URL(file: 或 installbundle: 协议)寻址。(UGameFeaturesSubsystem (API) + Lyra Sample Game)
下图是根据 API 中的查询函数和转换函数整理的主干状态:
stateDiagram-v2 [*] --> Installed: 安装 / 内置 Installed --> Mounted: Mounting Mounted --> Registered: RegisterGameFeaturePlugin Registered --> Loaded: LoadGameFeaturePlugin Loaded --> Active: Activate Active --> Loaded: DeactivateGameFeaturePlugin Loaded --> Registered: UnloadGameFeaturePlugin(bKeepRegistered) Registered --> [*]: Terminate / Uninstall
图中只画了 API 命名直接对应的状态。官方注释里,IsGameFeaturePluginInstalled/Registered/Loaded 都是「处于该状态或更后」,IsGameFeaturePluginActive 判断是否为 Active,IsGameFeaturePluginMounted 判断是否已越过 Mounting。另外还有中间过渡态和错误态(IsGameFeaturePluginInErrorState)。可以作为目标设置的状态只有四个:UE 5.6 API 中 EGameFeatureTargetState 的取值为 Installed、Registered、Loaded、Active(EGameFeatureTargetState),也就是 ChangeGameFeatureTargetState 能指定的终点。完整的内部状态枚举 EGameFeaturePluginState,在 UE 5.4/5.5 API 中只是 GameFeaturePluginStatePrivate::EGameFeaturePluginState 的 typedef,定义在 GameFeatureTypes.h,文档页没有列出取值(EGameFeaturePluginState),官方 Python API(5.0–5.8)没有这个枚举的页面。下表来自非官方来源:第三方博客(2021-11-17)引用的 UE5 早期访问版源码注释。之后的版本可能有增删,例如 5.8 API 的 DeactivateGameFeaturePlugin 提到了 Terminal 状态,而下表中没有。(UE5: Game Feature Tech Preview,非官方)
| 状态 | 类型 | 源码注释(意译) |
|---|---|---|
| Uninitialized | 稳定 | 尚未设置 |
| UnknownStatus | 稳定 | 只知道用于查询状态的 URL |
| CheckingStatus | 过渡 UnknownStatus→StatusKnown | 正在查询状态 |
| StatusKnown | 稳定 | 已知插件信息,尚未执行任何操作 |
| Downloading / Uninstalling | 过渡 StatusKnown⇄Installed | 写入或移除本地存储 |
| Installed | 稳定 | 插件已在本地存储(硬盘上) |
| WaitingForDependencies | 过渡 Installed→Registered | 加载所有依赖的代码和内容 |
| Mounting / Unmounting | 过渡 Installed⇄Registered | 挂载或卸载 pak 等内容文件 |
| Registering / Unregistering | 过渡 Installed⇄Registered | 发现资产但不加载(少数用于发现的除外),或清理这些数据 |
| Registered | 稳定 | 资产已知但未加载 |
| Loading / Unloading | 过渡 Registered⇄Loaded | 把代码和内容载入或移出内存 |
| Loaded | 稳定 | 已在内存中,但还没有注册到游戏系统 |
| Activating / Deactivating | 过渡 Loaded⇄Active | 向游戏系统注册或注销 |
| Active | 稳定 | 完全加载并激活,正在影响游戏 |
这与官方 EGameFeatureTargetState 一致:只有 Installed、Registered、Loaded、Active 四个稳定状态可以作为目标,其余都是途经的过渡态(这是对照两份来源的归纳)。(UGameFeaturesSubsystem (API) + Lyra Sample Game)
| 操作 | 官方说明 |
|---|---|
ChangeGameFeatureTargetState | 设定目标状态,由状态机自动推进 |
LoadAndActivateGameFeaturePlugin | 加载并激活 |
DeactivateGameFeaturePlugin | 停用,转到 [Terminal, Loaded] |
ReleaseGameFeaturePlugin / TerminateGameFeaturePlugin | 释放数据 / 移除全部跟踪数据 |
DetermineBuiltInInitialFeatureState | 从 .uplugin 描述读取内置插件的初始状态 |
Lyra 中的用法
官方 Lyra 文档:Content 目录只放通用资产和大厅,核心玩法放在 Game Feature 插件里(例如 ShooterCore、TopDownArena),大多数地图也在插件中。在大厅选择一个体验 (Experience) 后,游戏加载所需的插件。Experience 由 LyraExperienceDefinition 类定义,官方说可以把它看作「高级得多的 GameMode」。Team Death Match 与 Control Points 两个体验都使用 ShooterCore 插件。(UGameFeaturesSubsystem (API) + Lyra Sample Game)Experience 内部如何调用上述子系统 API,没有官方全文来源,不展开。
出处
- Game Features and Modular Gameplay in Unreal Engine UE 5.8 文档
- UGameFeaturesSubsystem (API) + Lyra Sample Game UE 5.8 API 参考 + UE 5.8 Lyra 文档
- EGameFeatureTargetState UE 5.6 API
- EGameFeaturePluginState UE 5.5 API
- UE5: Game Feature Tech Preview 【非官方】第三方博客引用 UE5 EA 源码