UE5.8 Game Features 喂饭级专题

UE5.8 Game Features 专题(一):先做一个可开关的武器包插件

从启用插件、创建 Game Feature Plugin、配置 BuiltInInitialFeatureState、创建 GameFeatureData、写一个 AddComponents 动作,到用控制台加载、激活、停用武器包。

总览

第一篇不讲宏大架构,先做一个最小闭环:WeaponPack_Rifle 插件激活时给角色挂一个 URifleFeatureComponent,停用时自动移除。你只要跑通这个闭环,就理解了 Game Features 最核心的价值:玩法不是写死在角色蓝图里,而是作为包被激活。

UE5.8 Game Features 专题(一):先做一个可开关的武器包插件 配图
先把最小闭环跑起来:插件存在、能被发现、能注册、能激活、能停用,才谈复杂架构。

第一步:启用基础插件

编辑器里启用:

  • Game Features
  • Modular Gameplay
  • Enhanced Input 和 Common UI 如果后续要跟输入/UI 集成
  • Gameplay Abilities 如果武器包要给技能

C++ 项目主模块常见依赖:

PrivateDependencyModuleNames.AddRange(new[]
{
    "GameFeatures",
    "ModularGameplay",
    "GameplayTags"
});

第二步:创建 Game Feature 插件

在插件浏览器里创建 Game Feature 插件,名字示例:WeaponPack_Rifle。如果是 C++ 武器包,用带代码模板;如果只是内容和数据,内容插件也可以。模板目录在 UE 源码里是 GameFeatures/Templates/GameFeaturePluginWithCodeGameFeaturePluginContentOnly

.uplugin 建议先写成这样:

{
  "FileVersion": 3,
  "FriendlyName": "WeaponPack_Rifle",
  "Category": "Game Features",
  "CanContainContent": true,
  "ExplicitlyLoaded": true,
  "EnabledByDefault": false,
  "BuiltInInitialFeatureState": "Registered",
  "Modules": [
    {
      "Name": "WeaponPack_RifleRuntime",
      "Type": "Runtime",
      "LoadingPhase": "Default"
    }
  ],
  "Plugins": [
    { "Name": "GameFeatures", "Enabled": true },
    { "Name": "ModularGameplay", "Enabled": true }
  ]
}

BuiltInInitialFeatureState=Registered 的意思是项目启动时发现并注册它,但不自动 Load/Active。这样你能用控制台或代码决定什么时候打开这个武器包。Lyra 的 ShooterCore.uplugin 就是类似思路:ExplicitlyLoaded=trueBuiltInInitialFeatureState=Registered

第三步:准备一个组件

在插件里写组件:

UCLASS(Blueprintable, ClassGroup=(GameFeatures))
class URifleFeatureComponent : public UGameFrameworkComponent
{
    GENERATED_BODY()

public:
    virtual void BeginPlay() override
    {
        Super::BeginPlay();
        UE_LOG(LogTemp, Log, TEXT("Rifle feature enabled on %s"), *GetNameSafe(GetOwner()));
    }
};

这个组件里可以先只做日志。后面再接输入、Ability、UI、装备数据。

第四步:让角色愿意被扩展

Game Feature 不会自动侵入所有 Actor。目标 Actor 必须 opt-in。最简单是在你的 Character 或 Pawn:

void AMyHeroCharacter::BeginPlay()
{
    Super::BeginPlay();
    UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
    UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(
        this,
        UGameFrameworkComponentManager::NAME_GameActorReady);
}

void AMyHeroCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);
    Super::EndPlay(EndPlayReason);
}

AddReceiver 是关键。源码注释说 Actor 必须通过 AddReceiver/RemoveReceiver 主动接入;否则 Add Components Action 不会魔法般找到它。

第五步:创建 GameFeatureData

在插件内容目录创建 DA_GameFeatureData_Rifle,类型是 GameFeatureData。添加 Action:

字段示例
ActionAdd Components
Actor ClassAMyHeroCharacter
Component ClassURifleFeatureComponent
Client Componenttrue
Server Componenttrue
Addition Flags先用 Add Unique

如果组件只做 UI 或本地输入,可以只勾 Client。服务器不需要加载纯 UI 组件。

第六步:运行时测试

PIE 后执行:

ListGameFeaturePlugins
LoadGameFeaturePlugin WeaponPack_Rifle
ModularGameplay.DumpGameFrameworkComponentManagers
DeactivateGameFeaturePlugin WeaponPack_Rifle

预期结果:

  1. Load 后角色身上出现 URifleFeatureComponent
  2. Dump 能看到 Receiver、Request、ComponentInstance。
  3. Deactivate 后组件被移除。

使用案例:Rifle 包最小验收

把这个 Rifle 包当成团队模板:程序只负责 URifleFeatureComponent 和接入 Receiver;策划在 DA_GameFeatureData_Rifle 里配置 ActorClass、ComponentClass、client/server 开关;测试只需要按 Load、Dump、Deactivate 三步验收。等这个闭环稳定后,再把输入、UI、AbilitySet 加进来。

架构分析

UGameFeatureAction_AddComponents::OnGameFeatureActivating 会遍历已有 WorldContext,也会监听后续 GameInstance 启动。它根据 NetMode 判断 client/server,然后调用 UGameFrameworkComponentManager::AddComponentRequest。返回的 FComponentRequestHandle 被 Action 保存;停用时 handle 清空,请求释放,组件也会被移除。

所以你不应该自己在 Action 里 NewObject 组件,也不应该手动维护所有角色列表。让 Component Manager 做引用计数和生命周期。

常见坑

  • 只创建 GameFeatureData,却没把它放在插件里可被扫描的位置。
  • 角色没有 AddReceiver,Add Components 动作看似配置正确但没有任何效果。
  • 组件没有继承 UGameFrameworkComponent 也可以被加,但少了常用辅助函数和统一风格。
  • Dedicated Server 上勾了 Client-only UI 组件,Cook 和运行时资源都变重。
  • Deactivate 后还持有组件指针,下一次激活可能已经是新实例。

源码依据

UGameFeatureData 持有 Actions 数组。UGameFeatureAction_AddComponentsFGameFeatureComponentEntry 包含 ActorClass、ComponentClass、Client/Server 开关和 AdditionFlags。UGameFrameworkComponentManager 注释明确:Actor 必须 opt-in 调用 AddReceiver/RemoveReceiver;请求通过 handle 保活,handle 销毁时请求移除。

源码路径索引

  • GameFeatures/Templates/GameFeaturePluginWithCode
  • GameFeatures/Public/GameFeatureData.h
  • GameFeatures/Public/GameFeatureAction_AddComponents.h
  • GameFeatures/Private/GameFeatureAction_AddComponents.cpp
  • ModularGameplay/Public/Components/GameFrameworkComponentManager.h