ue-game-features

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

UE Game Features and Modular Gameplay

UE Game Features 与模块化玩法

You are an expert in Unreal Engine's Game Features plugin system and modular gameplay architecture.
你是Unreal Engine的Game Features插件系统和模块化玩法架构方面的专家。

Context Check

上下文检查

Read
.agents/ue-project-context.md
before proceeding. Determine:
  • Whether the
    GameFeatures
    and
    ModularGameplay
    plugins are enabled
  • Which actors register as component receivers (
    AddReceiver
    )
  • Whether the project uses an init state system or experience-based loading
  • Existing
    UGameFeatureAction
    subclasses or modular component base classes
在开始操作前,请阅读
.agents/ue-project-context.md
,确定以下内容:
  • GameFeatures
    ModularGameplay
    插件是否已启用
  • 哪些Actor注册为组件接收者(调用
    AddReceiver
  • 项目是否使用初始化状态系统或基于体验的加载机制
  • 已有的
    UGameFeatureAction
    子类或模块化组件基类

Information Gathering

信息收集

Ask the developer:
  1. Are you creating a new Game Feature plugin or extending an existing one?
  2. What components or abilities should the feature inject into gameplay actors?
  3. Does the feature need async loading or runtime activation/deactivation?
  4. Is there an experience/game mode composition system (Lyra-style)?
  5. Do components need ordered initialization across features?

请向开发者询问以下问题:
  1. 你是要创建新的Game Feature插件,还是扩展现有插件?
  2. 该功能需要向游戏玩法Actor中注入哪些组件或能力?
  3. 该功能是否需要异步加载或运行时激活/停用?
  4. 项目是否有体验/游戏模式组合系统(Lyra风格)?
  5. 组件是否需要跨功能按顺序初始化?

Game Feature Plugin Structure

Game Feature插件结构

A Game Feature plugin is a standard UE plugin with
Type
set to
"GameFeature"
in its
.uplugin
descriptor. This tells the engine to manage its lifecycle through the Game Features subsystem rather than loading it as a regular plugin.
Game Feature插件是标准的UE插件,其
.uplugin
描述文件中的
Type
需设置为
"GameFeature"
。这会告知引擎通过Game Features子系统管理其生命周期,而非作为常规插件加载。

.uplugin Descriptor

.uplugin描述文件

cpp
{
    "Type": "GameFeature",
    "BuiltInInitialFeatureState": "Active",  // or "Registered", "Installed"
    "Plugins": [
        { "Name": "GameFeatures", "Enabled": true },
        { "Name": "ModularGameplay", "Enabled": true }
    ]
}
BuiltInInitialFeatureState
controls how far the plugin advances on startup. Use
"Active"
for always-on features,
"Registered"
for features activated by gameplay code, or
"Installed"
for downloadable content loaded on demand.
cpp
{
    "Type": "GameFeature",
    "BuiltInInitialFeatureState": "Active",  // 可选值:"Registered"、"Installed"
    "Plugins": [
        { "Name": "GameFeatures", "Enabled": true },
        { "Name": "ModularGameplay", "Enabled": true }
    ]
}
BuiltInInitialFeatureState
控制插件在启动时的推进程度。始终启用的功能使用
"Active"
,通过游戏代码激活的功能使用
"Registered"
,按需加载的可下载内容使用
"Installed"

UGameFeatureData

UGameFeatureData

Each Game Feature plugin contains a
UGameFeatureData
primary data asset (extends
UPrimaryDataAsset
) that defines what the feature does:
cpp
// From GameFeatureData.h
UPROPERTY(EditDefaultsOnly, Instanced, Category = "Game Feature | Actions")
TArray<TObjectPtr<UGameFeatureAction>> Actions;

UPROPERTY(EditAnywhere, Category = "Game Feature | Asset Manager")
TArray<FPrimaryAssetTypeInfo> PrimaryAssetTypesToScan;
Actions
is the core — an instanced array of
UGameFeatureAction
subclasses that execute when the feature activates.
每个Game Feature插件都包含一个
UGameFeatureData
主数据资产(继承自
UPrimaryDataAsset
),用于定义该功能的作用:
cpp
// 来自GameFeatureData.h
UPROPERTY(EditDefaultsOnly, Instanced, Category = "Game Feature | Actions")
TArray<TObjectPtr<UGameFeatureAction>> Actions;

UPROPERTY(EditAnywhere, Category = "Game Feature | Asset Manager")
TArray<FPrimaryAssetTypeInfo> PrimaryAssetTypesToScan;
Actions
是核心部分——它是
UGameFeatureAction
子类的实例化数组,会在功能激活时执行。

Directory Convention

目录规范

Plugins/GameFeatures/
├── ShooterCore/
│   ├── ShooterCore.uplugin          (Type: GameFeature)
│   ├── Content/
│   │   └── ShooterCore.uasset       (UGameFeatureData)
│   └── Source/ShooterCoreRuntime/
└── DeathmatchRules/
    ├── DeathmatchRules.uplugin
    └── Content/DeathmatchRules.uasset

Plugins/GameFeatures/
├── ShooterCore/
│   ├── ShooterCore.uplugin          (Type: GameFeature)
│   ├── Content/
│   │   └── ShooterCore.uasset       (UGameFeatureData)
│   └── Source/ShooterCoreRuntime/
└── DeathmatchRules/
    ├── DeathmatchRules.uplugin
    └── Content/DeathmatchRules.uasset

Plugin State Machine

插件状态机

Game Feature plugins transition through a well-defined state machine. Actions fire at specific transitions and runtime activation must target valid destination states.
Game Feature插件会按照定义明确的状态机进行状态转换。动作会在特定转换节点触发,且运行时激活必须针对有效的目标状态。

EGameFeaturePluginState Lifecycle

EGameFeaturePluginState生命周期

Uninitialized → Terminal → UnknownStatus → StatusKnown
    → Installed → Registered → Loaded → Active
Each major state has transition states between them (e.g.,
Registering
,
Loading
,
Activating
). You target a destination state and the subsystem walks the chain.
Uninitialized → Terminal → UnknownStatus → StatusKnown
    → Installed → Registered → Loaded → Active
每个主要状态之间都有过渡状态(例如
Registering
Loading
Activating
)。你只需指定目标状态,子系统就会自动完成整个状态链的转换。

Destination States

目标状态

StateDescription
Terminal
Plugin removed from tracking entirely
StatusKnown
Availability confirmed (exists on disk or bundle)
Installed
Files on local storage, not yet registered
Registered
Assets registered with Asset Manager, actions notified
Loaded
Assets loaded into memory
Active
Actions fully activated, components injected
URL protocols:
file:
for built-in disk plugins,
installbundle:
for downloadable features. Convert descriptor path to URL with
UGameFeaturesSubsystem::GetPluginURL_FileProtocol(Path)
.

状态描述
Terminal
插件完全从跟踪列表中移除
StatusKnown
确认插件可用(存在于磁盘或包中)
Installed
文件已存储在本地,但尚未注册
Registered
资产已在Asset Manager中注册,动作已收到通知
Loaded
资产已加载到内存中
Active
动作已完全激活,组件已注入
URL协议:内置磁盘插件使用
file:
,可下载功能使用
installbundle:
。使用
UGameFeaturesSubsystem::GetPluginURL_FileProtocol(Path)
将描述文件路径转换为URL。

UGameFeatureAction

UGameFeatureAction

UGameFeatureAction
(
UCLASS(MinimalAPI, DefaultToInstanced, EditInlineNew, Abstract)
) is the base class for all actions.
DefaultToInstanced
+
EditInlineNew
allow instances to be created inline within
UGameFeatureData
's
Actions
array.
UGameFeatureAction
UCLASS(MinimalAPI, DefaultToInstanced, EditInlineNew, Abstract)
)是所有动作的基类。
DefaultToInstanced
+
EditInlineNew
允许在
UGameFeatureData
Actions
数组中直接创建实例。

Lifecycle Methods

生命周期方法

cpp
// Registration phase
virtual void OnGameFeatureRegistering();
virtual void OnGameFeatureUnregistering();

// Loading phase
virtual void OnGameFeatureLoading();
virtual void OnGameFeatureUnloading();

// Activation — primary override point
virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context);
virtual void OnGameFeatureActivating();  // legacy no-arg fallback

// Post-activation confirmation
virtual void OnGameFeatureActivated();

// Deactivation — supports async via context
virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context);
OnGameFeatureActivating(Context)
is the primary override. The base calls the legacy no-arg version for backward compatibility.
cpp
// 注册阶段
virtual void OnGameFeatureRegistering();
virtual void OnGameFeatureUnregistering();

// 加载阶段
virtual void OnGameFeatureLoading();
virtual void OnGameFeatureUnloading();

// 激活——主要重写点
virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context);
virtual void OnGameFeatureActivating();  // 遗留的无参回退方法

// 激活后确认
virtual void OnGameFeatureActivated();

// 停用——通过上下文支持异步操作
virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context);
OnGameFeatureActivating(Context)
是主要的重写方法。基类会调用遗留的无参版本以保持向后兼容性。

Async Deactivation

异步停用

When deactivation requires async work, pause it via the context:
cpp
void UMyAction::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context)
{
    FSimpleDelegate ResumeDelegate = Context.PauseDeactivationUntilComplete(
        TEXT("MyAction_AsyncCleanup"));
    // Start async work — MUST invoke ResumeDelegate when done or deactivation hangs
    AsyncTask(ENamedThreads::GameThread, [ResumeDelegate]()
    {
        // ... cleanup ...
        ResumeDelegate.ExecuteIfBound();
    });
}
See
references/game-feature-patterns.md
for complete custom action subclass templates.

当停用需要异步操作时,可通过上下文暂停停用流程:
cpp
void UMyAction::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context)
{
    FSimpleDelegate ResumeDelegate = Context.PauseDeactivationUntilComplete(
        TEXT("MyAction_AsyncCleanup"));
    // 启动异步操作——完成后必须调用ResumeDelegate,否则停用流程会挂起
    AsyncTask(ENamedThreads::GameThread, [ResumeDelegate]()
    {
        // ... 清理操作 ...
        ResumeDelegate.ExecuteIfBound();
    });
}
完整的自定义动作子类模板请查看
references/game-feature-patterns.md

Built-in Actions

内置动作

UGameFeatureAction_AddComponents

UGameFeatureAction_AddComponents

UCLASS(MinimalAPI, meta=(DisplayName="Add Components"), final)
. The most commonly used action — injects components into actors via
UGameFrameworkComponentManager
.
Configuration uses
FGameFeatureComponentEntry
:
cpp
UPROPERTY(EditAnywhere) TSoftClassPtr<AActor> ActorClass;
UPROPERTY(EditAnywhere) TSoftClassPtr<UActorComponent> ComponentClass;
UPROPERTY(EditAnywhere) uint8 bClientComponent : 1;
UPROPERTY(EditAnywhere) uint8 bServerComponent : 1;
Internally stores
TSharedPtr<FComponentRequestHandle>
— RAII removes components when the handle drops (feature deactivates). Set both
bClientComponent
and
bServerComponent
for components needed everywhere, server-only for gameplay logic, client-only for cosmetic.
UCLASS(MinimalAPI, meta=(DisplayName="Add Components"), final)
。这是最常用的动作——通过
UGameFrameworkComponentManager
向Actor中注入组件。
配置使用
FGameFeatureComponentEntry
cpp
UPROPERTY(EditAnywhere) TSoftClassPtr<AActor> ActorClass;
UPROPERTY(EditAnywhere) TSoftClassPtr<UActorComponent> ComponentClass;
UPROPERTY(EditAnywhere) uint8 bClientComponent : 1;
UPROPERTY(EditAnywhere) uint8 bServerComponent : 1;
内部存储
TSharedPtr<FComponentRequestHandle>
——当句柄销毁时(功能停用),RAII机制会自动移除组件。如果组件需要在客户端和服务端都存在,则同时设置
bClientComponent
bServerComponent
;仅游戏逻辑使用的组件设为服务端专用;仅 cosmetic(外观)组件设为客户端专用。

Other Built-in Actions

其他内置动作

ActionPurpose
UGameFeatureAction_AddCheats
Register cheat manager extensions
UGameFeatureAction_DataRegistry
Register data registry sources

动作用途
UGameFeatureAction_AddCheats
注册作弊管理器扩展
UGameFeatureAction_DataRegistry
注册数据注册表源

UGameFeaturesSubsystem

UGameFeaturesSubsystem

UGameFeaturesSubsystem
(
UEngineSubsystem
) manages all Game Feature plugin lifecycles:
cpp
UGameFeaturesSubsystem& GFS = UGameFeaturesSubsystem::Get();
UGameFeaturesSubsystem
UEngineSubsystem
)管理所有Game Feature插件的生命周期:
cpp
UGameFeaturesSubsystem& GFS = UGameFeaturesSubsystem::Get();

Runtime Activation and Deactivation

运行时激活与停用

cpp
FString PluginURL = UGameFeaturesSubsystem::GetPluginURL_FileProtocol(
    TEXT("/MyProject/Plugins/GameFeatures/MyFeature/MyFeature.uplugin"));

// Activate — callback receives const UE::GameFeatures::FResult&
GFS.LoadAndActivateGameFeaturePlugin(PluginURL,
    FGameFeaturePluginLoadComplete::CreateUObject(this, &UMyMgr::OnLoaded));

// Deactivate and unload
GFS.DeactivateGameFeaturePlugin(PluginURL);
GFS.UnloadGameFeaturePlugin(PluginURL, /*bKeepRegistered=*/ false);

// Or target a specific state:
GFS.ChangeGameFeatureTargetState(PluginURL, EGameFeatureTargetState::Registered,
    FGameFeaturePluginChangeStateComplete());
cpp
FString PluginURL = UGameFeaturesSubsystem::GetPluginURL_FileProtocol(
    TEXT("/MyProject/Plugins/GameFeatures/MyFeature/MyFeature.uplugin"));

// 激活——回调接收const UE::GameFeatures::FResult&
GFS.LoadAndActivateGameFeaturePlugin(PluginURL,
    FGameFeaturePluginLoadComplete::CreateUObject(this, &UMyMgr::OnLoaded));

// 停用并卸载
GFS.DeactivateGameFeaturePlugin(PluginURL);
GFS.UnloadGameFeaturePlugin(PluginURL, /*bKeepRegistered=*/ false);

// 或者指定特定目标状态:
GFS.ChangeGameFeatureTargetState(PluginURL, EGameFeatureTargetState::Registered,
    FGameFeaturePluginChangeStateComplete());

Query and Observe

查询与监听

cpp
bool bActive = GFS.IsGameFeaturePluginActive(PluginURL, /*bCheckForActivating=*/ false);
EGameFeaturePluginState State = GFS.GetPluginState(PluginURL);

// Global observer — implement IGameFeatureStateChangeObserver
GFS.AddObserver(MyObserver, UGameFeaturesSubsystem::EObserverPluginStateUpdateMode::CurrentAndFuture);
GFS.RemoveObserver(MyObserver);
IGameFeatureStateChangeObserver
provides:
OnGameFeatureRegistering(Data, PluginName, URL)
,
OnGameFeatureActivating(Data, URL)
,
OnGameFeatureDeactivating(Data, Context, URL)
.

cpp
bool bActive = GFS.IsGameFeaturePluginActive(PluginURL, /*bCheckForActivating=*/ false);
EGameFeaturePluginState State = GFS.GetPluginState(PluginURL);

// 全局监听——实现IGameFeatureStateChangeObserver
GFS.AddObserver(MyObserver, UGameFeaturesSubsystem::EObserverPluginStateUpdateMode::CurrentAndFuture);
GFS.RemoveObserver(MyObserver);
IGameFeatureStateChangeObserver
提供以下方法:
OnGameFeatureRegistering(Data, PluginName, URL)
OnGameFeatureActivating(Data, URL)
OnGameFeatureDeactivating(Data, Context, URL)

Component Injection System

组件注入系统

UGameFrameworkComponentManager
(
UGameInstanceSubsystem
) is the runtime engine that injects components into actors. It is a Game Instance subsystem — not an engine subsystem:
cpp
UGameFrameworkComponentManager* CompMgr =
    GetGameInstance()->GetSubsystem<UGameFrameworkComponentManager>();
UGameFrameworkComponentManager
UGameInstanceSubsystem
)是运行时向Actor中注入组件的引擎。它是Game Instance子系统,而非引擎子系统:
cpp
UGameFrameworkComponentManager* CompMgr =
    GetGameInstance()->GetSubsystem<UGameFrameworkComponentManager>();

Actor Registration (Receivers)

Actor注册(接收者)

Actors must register as receivers to accept injected components:
cpp
void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
}
void AMyCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);
    Super::EndPlay(EndPlayReason);
}
Actor必须注册为接收者才能接受注入的组件:
cpp
void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
}
void AMyCharacter::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
    UGameFrameworkComponentManager::RemoveGameFrameworkComponentReceiver(this);
    Super::EndPlay(EndPlayReason);
}

Component Requests (RAII)

组件请求(RAII机制)

cpp
TSharedPtr<FComponentRequestHandle> Handle = CompMgr->AddComponentRequest(
    TSoftClassPtr<AActor>(AMyCharacter::StaticClass()),
    UMyHealthComponent::StaticClass(),
    EGameFrameworkAddComponentFlags::AddUnique);
// Handle is RAII — destroying it removes the request and cleans up injected components
FlagValueBehavior
None
0Default, allows duplicates
AddUnique
1Skip if same class already exists
AddIfNotChild
2Skip if a child class already exists
UseAutoGeneratedName
4Auto-generated name instead of class name
cpp
TSharedPtr<FComponentRequestHandle> Handle = CompMgr->AddComponentRequest(
    TSoftClassPtr<AActor>(AMyCharacter::StaticClass()),
    UMyHealthComponent::StaticClass(),
    EGameFrameworkAddComponentFlags::AddUnique);
// Handle遵循RAII机制——销毁它会移除请求并清理注入的组件
标志行为
None
0默认值,允许重复添加
AddUnique
1如果已存在相同类的组件,则跳过
AddIfNotChild
2如果已存在子类组件,则跳过
UseAutoGeneratedName
4使用自动生成的名称而非类名

Extension Handlers and Events

扩展处理器与事件

cpp
TSharedPtr<FComponentRequestHandle> ExtHandle = CompMgr->AddExtensionHandler(
    TSoftClassPtr<AActor>(AMyCharacter::StaticClass()),
    FExtensionHandlerDelegate::CreateUObject(this, &UMyAction::OnExtension));

void UMyAction::OnExtension(AActor* Actor, FName EventName)
{
    if (EventName == UGameFrameworkComponentManager::NAME_GameActorReady)
    { /* Actor fully initialized */ }
}
Standard event names:
NAME_ReceiverAdded
,
NAME_ReceiverRemoved
,
NAME_ExtensionAdded
,
NAME_ExtensionRemoved
,
NAME_GameActorReady
. Send custom events with
CompMgr->SendExtensionEvent(Actor, FName("MyEvent"))
.

cpp
TSharedPtr<FComponentRequestHandle> ExtHandle = CompMgr->AddExtensionHandler(
    TSoftClassPtr<AActor>(AMyCharacter::StaticClass()),
    FExtensionHandlerDelegate::CreateUObject(this, &UMyAction::OnExtension));

void UMyAction::OnExtension(AActor* Actor, FName EventName)
{
    if (EventName == UGameFrameworkComponentManager::NAME_GameActorReady)
    { /* Actor已完全初始化 */ }
}
标准事件名称:
NAME_ReceiverAdded
NAME_ReceiverRemoved
NAME_ExtensionAdded
NAME_ExtensionRemoved
NAME_GameActorReady
。使用
CompMgr->SendExtensionEvent(Actor, FName("MyEvent"))
发送自定义事件。

Init State System

初始化状态系统

The init state system solves ordered initialization across independently-loaded features. Without it, Component A might read from Component B before B exists — a common problem in modular architectures.
初始化状态系统解决了独立加载的功能之间的有序初始化问题。如果没有该系统,组件A可能会在组件B存在之前读取其数据——这是模块化架构中的常见问题。

Registering Init States

注册初始化状态

Define project-wide init states as
FGameplayTag
values in a fixed order:
cpp
CompMgr->RegisterInitState(TAG_InitState_Spawning, false, FGameplayTag());
CompMgr->RegisterInitState(TAG_InitState_DataAvailable, false, TAG_InitState_Spawning);
CompMgr->RegisterInitState(TAG_InitState_DataInitialized, false, TAG_InitState_DataAvailable);
CompMgr->RegisterInitState(TAG_InitState_GameplayReady, false, TAG_InitState_DataInitialized);
将项目级的初始化状态定义为
FGameplayTag
值,并按固定顺序排列:
cpp
CompMgr->RegisterInitState(TAG_InitState_Spawning, false, FGameplayTag());
CompMgr->RegisterInitState(TAG_InitState_DataAvailable, false, TAG_InitState_Spawning);
CompMgr->RegisterInitState(TAG_InitState_DataInitialized, false, TAG_InitState_DataAvailable);
CompMgr->RegisterInitState(TAG_InitState_GameplayReady, false, TAG_InitState_DataInitialized);

Changing and Observing Init State

更改与监听初始化状态

cpp
// Advance a feature's state
bool bChanged = CompMgr->ChangeFeatureInitState(
    MyActor, FName("MyComponent"), this, TAG_InitState_DataAvailable);

// Wait for another feature to reach a state
FDelegateHandle DH = CompMgr->RegisterAndCallForActorInitState(
    MyActor, FName("OtherComp"), TAG_InitState_DataInitialized,
    FActorInitStateChangedDelegate::CreateUObject(this, &UMyComp::OnOtherReady),
    /*bCallImmediately=*/ true);

// Check if all features reached a state
bool bAllReady = CompMgr->HaveAllFeaturesReachedInitState(
    MyActor, TAG_InitState_GameplayReady, /*ExcludingFeature=*/ NAME_None);
cpp
// 推进某个功能的状态
bool bChanged = CompMgr->ChangeFeatureInitState(
    MyActor, FName("MyComponent"), this, TAG_InitState_DataAvailable);

// 等待另一个功能达到指定状态
FDelegateHandle DH = CompMgr->RegisterAndCallForActorInitState(
    MyActor, FName("OtherComp"), TAG_InitState_DataInitialized,
    FActorInitStateChangedDelegate::CreateUObject(this, &UMyComp::OnOtherReady),
    /*bCallImmediately=*/ true);

// 检查所有功能是否都达到指定状态
bool bAllReady = CompMgr->HaveAllFeaturesReachedInitState(
    MyActor, TAG_InitState_GameplayReady, /*ExcludingFeature=*/ NAME_None);

IGameFrameworkInitStateInterface

IGameFrameworkInitStateInterface

Implement on components for structured init state progression:
cpp
UCLASS()
class UMyModularComponent : public UPawnComponent,
    public IGameFrameworkInitStateInterface
{
    GENERATED_BODY()
public:
    virtual FName GetFeatureName() const override { return TEXT("MyFeature"); }
    virtual bool CanChangeInitState(UGameFrameworkComponentManager* Manager,
        FGameplayTag CurrentState, FGameplayTag DesiredState) const override;
    virtual void HandleChangeInitState(UGameFrameworkComponentManager* Manager,
        FGameplayTag CurrentState, FGameplayTag DesiredState) override;
    virtual void CheckDefaultInitialization() override;

    virtual void BeginPlay() override
    {
        Super::BeginPlay();
        RegisterInitStateFeature();
    }
    virtual void EndPlay(const EEndPlayReason::Type Reason) override
    {
        UnregisterInitStateFeature();
        Super::EndPlay(Reason);
    }
};
ContinueInitStateChain(TArray<FGameplayTag>{State1, State2, State3})
attempts to advance through a sequence of states. Use this in
CheckDefaultInitialization
to auto-advance as far as possible.

在组件上实现该接口以实现结构化的初始化状态推进:
cpp
UCLASS()
class UMyModularComponent : public UPawnComponent,
    public IGameFrameworkInitStateInterface
{
    GENERATED_BODY()
public:
    virtual FName GetFeatureName() const override { return TEXT("MyFeature"); }
    virtual bool CanChangeInitState(UGameFrameworkComponentManager* Manager,
        FGameplayTag CurrentState, FGameplayTag DesiredState) const override;
    virtual void HandleChangeInitState(UGameFrameworkComponentManager* Manager,
        FGameplayTag CurrentState, FGameplayTag DesiredState) override;
    virtual void CheckDefaultInitialization() override;

    virtual void BeginPlay() override
    {
        Super::BeginPlay();
        RegisterInitStateFeature();
    }
    virtual void EndPlay(const EEndPlayReason::Type Reason) override
    {
        UnregisterInitStateFeature();
        Super::EndPlay(Reason);
    }
};
ContinueInitStateChain(TArray<FGameplayTag>{State1, State2, State3})
会尝试按顺序推进状态。在
CheckDefaultInitialization
中使用此方法可自动推进到尽可能远的状态。

Modular Component Hierarchy

模块化组件层级

The
ModularGameplay
plugin provides typed base components for gameplay framework actors:
Base ClassParentTyped Accessor
UGameFrameworkComponent
UActorComponent
None (generic base)
UPawnComponent
UGameFrameworkComponent
GetPawn<T>()
,
GetPawnChecked<T>()
UControllerComponent
UGameFrameworkComponent
GetController<T>()
,
GetControllerChecked<T>()
UGameStateComponent
UGameFrameworkComponent
GetGameState<T>()
,
GetGameStateChecked<T>()
UPlayerStateComponent
UGameFrameworkComponent
GetPlayerState<T>()
,
GetPlayerStateChecked<T>()
Use these instead of raw
UActorComponent
for type-safe owner access and init state integration.

ModularGameplay
插件为游戏玩法框架Actor提供了类型化的基础组件:
基类父类类型化访问器
UGameFrameworkComponent
UActorComponent
无(通用基类)
UPawnComponent
UGameFrameworkComponent
GetPawn<T>()
GetPawnChecked<T>()
UControllerComponent
UGameFrameworkComponent
GetController<T>()
GetControllerChecked<T>()
UGameStateComponent
UGameFrameworkComponent
GetGameState<T>()
GetGameStateChecked<T>()
UPlayerStateComponent
UGameFrameworkComponent
GetPlayerState<T>()
GetPlayerStateChecked<T>()
使用这些基类而非原生
UActorComponent
,可获得类型安全的所有者访问权限和初始化状态集成。

Experience System Pattern

体验系统模式

The experience system (pioneered by Lyra) composes game modes from Game Feature plugins at runtime. Instead of a monolithic GameMode, lightweight experience data assets list which features to activate.
体验系统(由Lyra首创)在运行时通过Game Feature插件组合游戏模式。它不再使用单一的GameMode,而是通过轻量级的体验数据资产列出需要激活的功能。

Core Flow

核心流程

GameMode::InitGame()
  → Load UExperienceDefinition (from map or URL options)
    → For each feature: LoadAndActivateGameFeaturePlugin()
    → All loaded → OnExperienceLoaded broadcast
      → Components initialize, gameplay begins
A
UExperienceManagerComponent
on
AGameStateBase
orchestrates loading. Systems bind to its
OnExperienceLoaded
delegate rather than assuming features are available at
BeginPlay
.
See
references/experience-system.md
for the full pattern with code templates.

GameMode::InitGame()
  → 加载UExperienceDefinition(来自地图或URL选项)
    → 针对每个功能:调用LoadAndActivateGameFeaturePlugin()
    → 所有功能加载完成后:广播OnExperienceLoaded
      → 组件初始化,游戏玩法开始
AGameStateBase
上的
UExperienceManagerComponent
负责协调加载。系统会绑定到其
OnExperienceLoaded
委托,而非假设功能在
BeginPlay
时已可用。
完整的模式及代码模板请查看
references/experience-system.md

Project Policies

项目策略

UGameFeaturesProjectPolicies
controls feature loading behavior. Override
IsPluginAllowed(PluginURL, OutReason)
to filter plugins,
GetGameFeatureLoadingMode(bLoadClientData, bLoadServerData)
for network filtering, and
InitGameFeatureManager()
/
ShutdownGameFeatureManager()
for custom lifecycle. Register via
DefaultGame.ini
under
GameFeaturesSubsystemSettings
.
See
references/game-feature-patterns.md
for the full policies subclass template.

UGameFeaturesProjectPolicies
控制功能加载行为。重写
IsPluginAllowed(PluginURL, OutReason)
可过滤插件,重写
GetGameFeatureLoadingMode(bLoadClientData, bLoadServerData)
可进行网络过滤,重写
InitGameFeatureManager()
/
ShutdownGameFeatureManager()
可自定义生命周期。通过
DefaultGame.ini
中的
GameFeaturesSubsystemSettings
进行注册。
完整的策略子类模板请查看
references/game-feature-patterns.md

Common Mistakes

常见错误

Missing receiver registration:
cpp
// WRONG — components never injected, no error logged
void AMyCharacter::BeginPlay() { Super::BeginPlay(); }
// RIGHT
void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
}
Leaking FComponentRequestHandle:
cpp
// WRONG — handle destroyed immediately, component removed next frame
CompMgr->AddComponentRequest(ActorClass, CompClass, Flags);
// RIGHT — store for lifetime of injection
RequestHandle = CompMgr->AddComponentRequest(ActorClass, CompClass, Flags);
Using BeginPlay for cross-component init:
cpp
// WRONG — modular components may not exist yet
void UMyComp::BeginPlay() { GetOwner()->FindComponentByClass<UOther>()->Configure(); }
// RIGHT — use init state system to wait for dependencies
void UMyComp::HandleChangeInitState(/*...*/, FGameplayTag DesiredState)
{
    if (DesiredState == TAG_InitState_DataInitialized)
        GetOwner()->FindComponentByClass<UOther>()->Configure();
}
Forgetting PauseDeactivationUntilComplete delegate: If you call
PauseDeactivationUntilComplete
but never invoke the returned delegate, plugin deactivation hangs indefinitely. Always invoke it, even on error paths.
Wrong subsystem type for ComponentManager:
cpp
// WRONG — NOT an engine subsystem
GEngine->GetEngineSubsystem<UGameFrameworkComponentManager>();
// RIGHT — UGameInstanceSubsystem
GetGameInstance()->GetSubsystem<UGameFrameworkComponentManager>();

缺少接收者注册:
cpp
// 错误——组件永远不会被注入,且无错误日志
void AMyCharacter::BeginPlay() { Super::BeginPlay(); }
// 正确
void AMyCharacter::BeginPlay()
{
    Super::BeginPlay();
    UGameFrameworkComponentManager::AddGameFrameworkComponentReceiver(this);
}
FComponentRequestHandle泄漏:
cpp
// 错误——句柄立即销毁,组件会在下一帧被移除
CompMgr->AddComponentRequest(ActorClass, CompClass, Flags);
// 正确——存储句柄以保持注入的生命周期
RequestHandle = CompMgr->AddComponentRequest(ActorClass, CompClass, Flags);
使用BeginPlay进行跨组件初始化:
cpp
// 错误——模块化组件可能尚未存在
void UMyComp::BeginPlay() { GetOwner()->FindComponentByClass<UOther>()->Configure(); }
// 正确——使用初始化状态系统等待依赖项
void UMyComp::HandleChangeInitState(/*...*/, FGameplayTag DesiredState)
{
    if (DesiredState == TAG_InitState_DataInitialized)
        GetOwner()->FindComponentByClass<UOther>()->Configure();
}
**忘记调用PauseDeactivationUntilComplete委托:**如果你调用了
PauseDeactivationUntilComplete
但从未调用返回的委托,插件停用流程会无限挂起。即使在错误路径中,也必须调用该委托。
ComponentManager的子系统类型错误:
cpp
// 错误——它不是引擎子系统
GEngine->GetEngineSubsystem<UGameFrameworkComponentManager>();
// 正确——它是UGameInstanceSubsystem
GetGameInstance()->GetSubsystem<UGameFrameworkComponentManager>();

Related Skills

相关技能

  • ue-gameplay-framework
    — GameMode, GameState, PlayerController, PlayerState lifecycle
  • ue-actor-component-architecture
    — Component creation, attachment, tick management
  • ue-gameplay-abilities
    — GAS integration with modular components
  • ue-data-assets-tables
    — Primary data assets, Asset Manager scanning
  • ue-module-build-system
    — Plugin structure, Build.cs dependencies
  • ue-world-level-streaming
    — Level streaming, seamless travel interactions
  • ue-gameplay-framework
    — GameMode、GameState、PlayerController、PlayerState生命周期
  • ue-actor-component-architecture
    — 组件创建、附着、Tick管理
  • ue-gameplay-abilities
    — GAS与模块化组件的集成
  • ue-data-assets-tables
    — 主数据资产、Asset Manager扫描
  • ue-module-build-system
    — 插件结构、Build.cs依赖
  • ue-world-level-streaming
    — 关卡流送、无缝旅行交互