UE5.8 Game Features 喂饭级专题

UE5.8 Game Features 专题(五):Add Components 与 Actor 扩展

深入 UGameFeatureAction_AddComponents、UGameFrameworkComponentManager、Receiver、Extension Event、FComponentRequestHandle、AddUnique、client/server 组件和运行时移除。

总览

Add Components 是 Game Features 最常用的 Action。它让一个玩法包能给已有 Actor 动态添加组件。比如武器包给 Pawn 加瞄准辅助组件,活动包给 PlayerState 加积分组件,比赛模式包给 GameState 加规则组件。

UE5.8 Game Features 专题(五):Add Components 与 Actor 扩展 配图
Add Components 不是直接扫全世界 Actor,它依赖 Receiver 和 Component Manager 的请求系统。

三方配合

角色负责
UGameFeatureAction_AddComponents在玩法包激活时提交“给某类 Actor 加某组件”的请求
UGameFrameworkComponentManager保存请求、匹配 Receiver、创建/销毁组件
目标 Actor调用 AddReceiver/RemoveReceiver,声明自己愿意被扩展

如果目标 Actor 不 AddReceiver,Action 配得再对也不会生效。这是最常见的坑。

目标 Actor 接入模板

void AMyPlayerState::BeginPlay()
{
    Super::BeginPlay();

    UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
    UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(
        this,
        UGameFrameworkComponentManager::NAME_GameActorReady);
}

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

项目里建议把这段放到统一基类:AModularPlayerControllerAModularCharacterAModularPlayerStateAModularGameState。Lyra 也有 ModularGameplayActors 插件作为参考。

AddComponents 配置表

字段说明
ActorClass要扩展的 Actor 基类
ComponentClass要添加的组件类
bClientComponent是否在客户端世界添加
bServerComponent是否在服务器世界添加
AdditionFlagsAddUnique、AddIfNotChild、UseAutoGeneratedName

AddUnique 通常应该打开,避免多个插件或重复激活加出重复组件。UseAutoGeneratedName 用于避免类名复用导致对象回收行为混乱,但要清楚自己为什么需要。

Extension Event 的作用

Component Manager 不只会加组件,还能发送扩展事件:

UGameFrameworkComponentManager::SendGameFrameworkComponentExtensionEvent(
    Actor,
    UGameFrameworkComponentManager::NAME_GameActorReady);

项目自定义 Action 可以监听某类 Actor 的 Extension Event,在 NAME_GameActorReady 后才给它注入输入、UI 或 Ability。这样比在 BeginPlay 里乱猜时机可靠。

使用案例:给 PlayerController 加 UI 入口

活动包激活后,给 AMyPlayerController 添加 UEventEntryComponent。组件 BeginPlay 时不直接创建 UI,而是等 LocalPlayer、HUD RootLayout、Common UI 初始化完成后再注册入口。活动包停用,组件被 Component Manager 销毁,入口也在组件 EndPlay 中移除。

这个设计的好处是:活动 UI 不是主工程硬编码出来的,活动结束时不会残留入口。

架构分析

UGameFeatureAction_AddComponents 激活时遍历当前 WorldContext,并监听后续 GameInstance 启动和世界变化。它根据 NetMode 判断是否应该添加 client/server 组件,然后调用 UGameFrameworkComponentManager::AddComponentRequest。组件请求是引用计数的,多个相同请求不会创建多份;handle 释放后请求减少,最后一个 handle 消失时组件会被移除。

Component Manager 内部有 Receiver、ComponentClassToComponentInstanceMap、ReceiverClassToComponentClassMap、ReceiverClassToEventMap。它不是简单的 GetAllActorsOfClass,而是一个面向运行时扩展的管理器。

常见坑

  • 目标 Actor 没有 AddReceiver。
  • AddReceiver 放太晚,Game Feature 已激活但 Actor 没收到已有请求。
  • RemoveReceiver 忘记调用,编辑器 PIE 多次后 Dump 里有脏 Receiver。
  • Client-only 组件在服务器也勾选,浪费资源或触发加载错误。
  • 组件 BeginPlay 假设其它组件已准备好,结果初始化顺序不稳定。

源码依据

UGameFrameworkComponentManagerUGameInstanceSubsystem。源码注释说明请求会自动作用于已在内存中的 Receiver,也会作用于后续注册的 Receiver;请求通过 FComponentRequestHandle 保活,handle 销毁时移除请求。UGameFeatureAction_AddComponents 在激活时处理已有 WorldContext 和后续 GameInstance,并根据 NetMode 决定 client/server 添加。

源码路径索引

  • GameFeatures/Public/GameFeatureAction_AddComponents.h
  • GameFeatures/Private/GameFeatureAction_AddComponents.cpp
  • ModularGameplay/Public/Components/GameFrameworkComponentManager.h
  • ModularGameplay/Private/Components/GameFrameworkComponentManager.cpp
  • ModularGameplay/Public/Components/GameFrameworkComponent.h