Lyra学习004:GameFeatureData分析

一、GameFeatureData.h 文件的中文注释版本

cpp 复制代码
// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "Engine/DataAsset.h"
#include "GameFeatureAction.h"

#include "GameFeatureData.generated.h"

#define UE_API GAMEFEATURES_API

class FConfigFile;
struct FPrimaryAssetTypeInfo;
struct FExternalDataLayerUID;

struct FAssetData;

/** 与游戏功能相关的数据,游戏功能是一组代码和内容的集合,用于向游戏添加可分离的独立功能 */
UCLASS(MinimalAPI)
class UGameFeatureData : public UPrimaryDataAsset
{
	GENERATED_BODY()

public:
	/** 获取应在插件层次结构中扫描主资源类型的位置的方法 */
	virtual const TArray<FPrimaryAssetTypeInfo>& GetPrimaryAssetTypesToScan() const { return PrimaryAssetTypesToScan; }

#if WITH_EDITOR
	virtual TArray<FPrimaryAssetTypeInfo>& GetPrimaryAssetTypesToScan() { return PrimaryAssetTypesToScan; }
	UE_API virtual void GetAssetRegistryTags(FAssetRegistryTagsContext Context) const override; // 获取资源注册表标签
	static UE_API void GetDependencyDirectoriesFromAssetData(const FAssetData& AssetData, TArray<FString>& OutDependencyDirectories); // 从资源数据获取依赖目录

	//~Begin deprecation 开始弃用部分
	UE_DEPRECATED(5.4, "Implement the version that takes FAssetRegistryTagsContext instead.") // 已弃用:请改用接受 FAssetRegistryTagsContext 的版本
	UE_API virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const override;
	UE_DEPRECATED(5.4, "GetContentBundleGuidsAssetRegistryTag is deprecated") // 已弃用:GetContentBundleGuidsAssetRegistryTag 已弃用
	static FName GetContentBundleGuidsAssetRegistryTag() { return NAME_None; }
	UE_DEPRECATED(5.4, "GetContentBundleGuidsFromAsset is deprecated, use GetDependencyDirectoriesFromAssetData") // 已弃用:GetContentBundleGuidsFromAsset 已弃用,请使用 GetDependencyDirectoriesFromAssetData
	static void GetContentBundleGuidsFromAsset(const FAssetData& Asset, TArray<FGuid>& OutContentBundleGuids) {}
	//~End deprecation 结束弃用部分
#endif //if WITH_EDITOR

	/** 在加载期间处理插件的基础 Ini 文件的方法 */
	static UE_API void InitializeBasePluginIniFile(const FString& PluginInstalledFilename); // 初始化基础插件 Ini 文件

	/** 在激活期间处理插件的层次结构 Ini 文件的方法 */
	static UE_API void InitializeHierarchicalPluginIniFiles(const FString& PluginInstalledFilename); // 初始化层次结构插件 Ini 文件
	static UE_API void InitializeHierarchicalPluginIniFiles(const TArrayView<FString>& PluginInstalledFilenames); // 初始化层次结构插件 Ini 文件(数组版本)

	UFUNCTION(BlueprintCallable, Category = "GameFeature")
	static UE_API void GetPluginName(const UGameFeatureData* GFD, FString& PluginName); // 获取插件名称(蓝图可调用)

	UE_API void GetPluginName(FString& PluginName) const; // 获取插件名称

	/** 返回游戏功能插件是否已注册。 */
	UE_API bool IsGameFeaturePluginRegistered(bool bCheckForRegistering = false) const; // 检查游戏功能插件是否已注册

	/** 返回游戏功能插件是否处于活动状态。 */
	UE_API bool IsGameFeaturePluginActive(bool bCheckForActivating = false) const; // 检查游戏功能插件是否处于活动状态

	/**
	 * 如果此插件存在安装包名称,则返回它。
	 * @param - PluginName - 我们想要获取其安装包的 GameFeaturePlugin 的名称。应与 .uplugin 文件同名。
	 * @param - bEvenIfDoesntExist - 为 true 时,将返回我们要查找的包的名称,而不检查它是否存在。
	 */
	static UE_API FString GetInstallBundleName(FStringView PluginName, bool bEvenIfDoesntExist = false); // 获取安装包名称

	/**
	 * 如果此插件存在可选安装包名称,则返回它。
	 * @param - PluginName - 我们想要获取其可选安装包的 GameFeaturePlugin 的名称。应与 .uplugin 文件同名。
	 * @param - bEvenIfDoesntExist - 为 true 时,将返回我们要查找的包的名称,而不检查它是否存在。
	 */
	static UE_API FString GetOptionalInstallBundleName(FStringView PluginName, bool bEvenIfDoesntExist = false); // 获取可选安装包名称

public:
	//~UPrimaryDataAsset interface UPrimaryDataAsset 接口部分
#if WITH_EDITORONLY_DATA
	UE_API virtual void UpdateAssetBundleData() override; // 更新资源包数据
#endif
	//~End of UPrimaryDataAsset interface UPrimaryDataAsset 接口结束部分

	//~UObject interface UObject 接口部分
#if WITH_EDITOR
	UE_API virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override; // 检查数据有效性
#endif
	//~End of UObject interface UObject 接口结束部分

	const TArray<UGameFeatureAction*>& GetActions() const { return Actions; } // 获取动作列表

#if WITH_EDITOR
	TArray<TObjectPtr<UGameFeatureAction>>& GetMutableActionsInEditor() { return Actions; } // 获取在编辑器中可变的动作列表
#endif

private:
#if WITH_EDITOR
	static UE_API void GetContentBundleGuids(const FAssetData& Asset, TArray<FGuid>& OutContentBundleGuids); // 获取内容包 GUID(内部)
	static UE_API FName GetContentBundleGuidsAssetRegistryTagPrivate(); // 获取内容包 GUID 资源注册表标签(内部私有)

	UFUNCTION()
	UE_API TArray<UClass*> GetDisallowedActions() const; // 获取不允许的动作类列表
#endif

	/** 内部辅助函数,用于在插件 INI 加载后重新加载对象上的配置数据 */
	static UE_API void ReloadConfigs(FConfigFile& PluginConfig); // 重新加载配置

protected:

	/** 在此游戏功能加载/激活/停用/卸载时要执行的操作列表 */
	UPROPERTY(EditDefaultsOnly, Instanced, Category="Game Feature | Actions", meta = (GetDisallowedClasses = "GetDisallowedActions"))
	TArray<TObjectPtr<UGameFeatureAction>> Actions; // 游戏功能动作实例数组

	/** 在启动时要扫描的资源类型列表 */
	UPROPERTY(EditAnywhere, Category="Game Feature | Asset Manager", meta=(TitleProperty="PrimaryAssetType"))
	TArray<FPrimaryAssetTypeInfo> PrimaryAssetTypesToScan; // 主资源类型信息数组
};

#undef UE_API

二、UGameFeatureData 类成员分析

成员变量分析

1. Actions - 游戏功能动作数组
cpp 复制代码
UPROPERTY(EditDefaultsOnly, Instanced, Category="Game Feature | Actions")
TArray<TObjectPtr<UGameFeatureAction>> Actions;

在 LyraStarterGame 中的应用:

  • 这是核心的动作执行器,用于定义游戏功能激活时的具体行为
  • 在 Lyra 中,每个 GameFeature 插件(如武器系统、角色能力、UI组件等)都会在这里配置具体的动作
  • 例如:添加输入映射、注册游戏模式、加载资产包、添加角色能力等
  • 通过 UGameFeatureAction 的子类实现模块化的功能扩展
2. PrimaryAssetTypesToScan - 主资源类型扫描数组
cpp 复制代码
UPROPERTY(EditAnywhere, Category="Game Feature | Asset Manager")
TArray<FPrimaryAssetTypeInfo> PrimaryAssetTypesToScan;

在 LyraStarterGame 中的应用:

  • 定义该游戏功能插件需要扫描和管理的资源类型
  • 在 Lyra 中用于自动发现和加载特定类型的资源,如:
    • 武器数据资产 (WeaponData)
    • 角色能力定义 (GameplayAbility)
    • 输入配置 (InputConfig)
    • 经验值表格 (ExperienceTable)
  • 确保插件相关的资源能够被 Asset Manager 正确识别和管理

成员函数分析

1. GetPrimaryAssetTypesToScan() - 获取主资源类型
cpp 复制代码
virtual const TArray<FPrimaryAssetTypeInfo>& GetPrimaryAssetTypesToScan() const

在 LyraStarterGame 中的应用:

  • 被 Asset Manager 调用,用于构建资源依赖图
  • 确保游戏功能插件中的资源能够被正确预加载
  • 支持 Lyra 的模块化资源管理架构
2. InitializeBasePluginIniFile() - 初始化基础插件INI文件
cpp 复制代码
static UE_API void InitializeBasePluginIniFile(const FString& PluginInstalledFilename)

在 LyraStarterGame 中的应用:

  • 在插件加载时处理基础配置文件
  • 用于设置插件的默认配置值
  • 在 Lyra 中管理不同游戏功能插件的默认设置
3. InitializeHierarchicalPluginIniFiles() - 初始化层次化插件INI文件
cpp 复制代码
static UE_API void InitializeHierarchicalPluginIniFiles(const FString& PluginInstalledFilename)

在 LyraStarterGame 中的应用:

  • 处理插件的层次化配置覆盖
  • 支持 Lyra 的多平台、多配置环境
  • 允许不同环境(开发、测试、发布)有不同的配置
4. GetPluginName() - 获取插件名称
cpp 复制代码
static UE_API void GetPluginName(const UGameFeatureData* GFD, FString& PluginName)
UE_API void GetPluginName(FString& PluginName) const

在 LyraStarterGame 中的应用:

  • 用于识别和管理不同的游戏功能插件
  • 在日志、调试信息和插件管理界面中使用
  • 支持插件间的依赖关系解析
5. IsGameFeaturePluginRegistered() - 检查插件注册状态
cpp 复制代码
UE_API bool IsGameFeaturePluginRegistered(bool bCheckForRegistering = false) const

在 LyraStarterGame 中的应用:

  • 检查插件是否已成功注册到游戏功能子系统
  • 用于条件逻辑,确保依赖的插件已就绪
  • 在 Lyra 的启动流程中验证插件状态
6. IsGameFeaturePluginActive() - 检查插件激活状态
cpp 复制代码
UE_API bool IsGameFeaturePluginActive(bool bCheckForActivating = false) const

在 LyraStarterGame 中的应用:

  • 确定插件功能是否已激活并可用
  • 用于功能开关和条件执行
  • 在 Lyra 中控制特定游戏模式的可用功能
7. GetInstallBundleName() - 获取安装包名称
cpp 复制代码
static UE_API FString GetInstallBundleName(FStringView PluginName, bool bEvenIfDoesntExist = false)

在 LyraStarterGame 中的应用:

  • 支持按需加载和分包发布
  • 管理大型插件的下载和安装
  • 在 Lyra 中用于 DLC 和扩展内容管理
8. GetOptionalInstallBundleName() - 获取可选安装包名称
cpp 复制代码
static UE_API FString GetOptionalInstallBundleName(FStringView PluginName, bool bEvenIfDoesntExist = false)

在 LyraStarterGame 中的应用:

  • 管理可选内容包
  • 支持游戏内容的模块化分发
  • 在 Lyra 中用于可选的游戏模式、皮肤包等
9. UpdateAssetBundleData() - 更新资源包数据
cpp 复制代码
UE_API virtual void UpdateAssetBundleData() override

在 LyraStarterGame 中的应用:

  • 在编辑器中使用,自动构建资源包
  • 确保插件资源能够正确打包和分发
  • 支持 Lyra 的资源热更新机制
10. IsDataValid() - 数据有效性检查
cpp 复制代码
UE_API virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override

在 LyraStarterGame 中的应用:

  • 在编辑器中进行数据验证
  • 确保游戏功能配置的正确性
  • 防止配置错误导致的运行时问题
11. GetActions() - 获取动作列表
cpp 复制代码
const TArray<UGameFeatureAction*>& GetActions() const

在 LyraStarterGame 中的应用:

  • 被游戏功能管理器调用执行具体动作
  • 支持插件的生命周期管理(加载、激活、停用、卸载)
  • 是 Lyra 插件系统的核心执行机制
12. GetMutableActionsInEditor() - 获取可编辑动作列表
cpp 复制代码
TArray<TObjectPtr<UGameFeatureAction>>& GetMutableActionsInEditor()

在 LyraStarterGame 中的应用:

  • 仅在编辑器中使用
  • 允许设计人员配置和修改插件行为
  • 支持可视化配置游戏功能

私有和保护成员

13. GetContentBundleGuids() - 获取内容包GUID
cpp 复制代码
static UE_API void GetContentBundleGuids(const FAssetData& Asset, TArray<FGuid>& OutContentBundleGuids)

在 LyraStarterGame 中的应用:

  • 内部使用,管理内容包标识
  • 支持内容包版本管理和依赖解析
14. ReloadConfigs() - 重新加载配置
cpp 复制代码
static UE_API void ReloadConfigs(FConfigFile& PluginConfig)

在 LyraStarterGame 中的应用:

  • 在插件配置更新时重新加载相关对象的配置
  • 支持运行时配置热更新
  • 确保配置变更能够及时生效

总结

在 LyraStarterGame 项目中,UGameFeatureData 是整个游戏功能插件系统的核心数据资产。它通过:

  1. 模块化设计:允许独立开发和管理不同的游戏功能
  2. 生命周期管理:控制插件的加载、激活、停用和卸载过程
  3. 资源管理:自动处理插件相关的资源扫描和加载
  4. 配置管理:支持灵活的配置覆盖机制
  5. 扩展性:通过 UGameFeatureAction 系统支持无限的功能扩展

这使得 Lyra 能够实现高度模块化的架构,支持快速迭代和功能组合。

三、实现独立开发和管理不同的游戏功能:

1. 插件化的架构设计

每个功能作为独立插件

cpp 复制代码
// Lyra 中的具体 GameFeature 插件示例:
// - LyraGameFeature_WeaponSystem.uplugin
// - LyraGameFeature_CharacterAbilities.uplugin  
// - LyraGameFeature_UIComponents.uplugin
// - LyraGameFeature_GameModes.uplugin

// 每个插件都有自己的 UGameFeatureData 资产
// 例如:/Game/WeaponSystem/GF_WeaponSystem.GF_WeaponSystem

独立的开发和部署

  • 源代码隔离:每个游戏功能在独立的插件目录中开发
  • 资源隔离:每个插件的资源文件独立管理
  • 配置独立:每个插件有自己的 DefaultGame.ini 配置

2. 动作系统的模块化执行

通过 Actions 数组实现功能组合

cpp 复制代码
// 在 Lyra 的武器系统 GameFeature 中可能包含的动作:
UPROPERTY(EditDefaultsOnly, Instanced, Category="Actions")
TArray<TObjectPtr<UGameFeatureAction>> Actions = {
    // 1. 注册武器数据资产
    NewObject<UGameFeatureAction_RegisterAssetData>(),
    
    // 2. 添加输入映射上下文
    NewObject<UGameFeatureAction_AddInputContextMapping>(),
    
    // 3. 注册游戏玩法技能
    NewObject<UGameFeatureAction_RegisterGameplayAbilities>(),
    
    // 4. 添加角色组件
    NewObject<UGameFeatureAction_AddComponents>()
};

具体的 Action 实现示例

资源注册 Action
cpp 复制代码
// LyraGameFeatureAction_RegisterAssetData.h
UCLASS()
class ULyraGameFeatureAction_RegisterAssetData : public UGameFeatureAction
{
    UPROPERTY(EditAnywhere)
    TArray<TSoftObjectPtr<UDataAsset>> AssetsToRegister;
    
    virtual void OnGameFeatureRegistering() override {
        for(auto& Asset : AssetsToRegister) {
            UAssetManager::Get().RegisterAsset(Asset);
        }
    }
};
输入配置 Action
cpp 复制代码
// LyraGameFeatureAction_AddInputContextMapping.h
UCLASS()
class ULyraGameFeatureAction_AddInputContextMapping : public UGameFeatureAction
{
    UPROPERTY(EditAnywhere)
    TSoftObjectPtr<UInputMappingContext> InputMappingContext;
    
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override {
        // 为本地玩家添加输入映射
        if(auto* LocalPlayer = GetLocalPlayer()) {
            LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>()
                      ->AddMappingContext(InputMappingContext.LoadSynchronous(), Priority);
        }
    }
};

3. 资源管理的独立性

主资源类型扫描配置

cpp 复制代码
// 在武器系统的 GameFeatureData 中:
PrimaryAssetTypesToScan = {
    FPrimaryAssetTypeInfo("WeaponData", "/Game/WeaponSystem/Weapons"),
    FPrimaryAssetTypeInfo("AmmoData", "/Game/WeaponSystem/Ammo"),
    FPrimaryAssetTypeInfo("WeaponSkin", "/Game/WeaponSystem/Skins")
};

// 在能力系统的 GameFeatureData 中:  
PrimaryAssetTypesToScan = {
    FPrimaryAssetTypeInfo("GameplayAbility", "/Game/AbilitySystem/Abilities"),
    FPrimaryAssetTypeInfo("GameplayEffect", "/Game/AbilitySystem/Effects"),
    FPrimaryAssetTypeInfo("AttributeSet", "/Game/AbilitySystem/Attributes")
};

4. 配置管理的层次化

插件特定的配置覆盖

复制代码
// LyraGameFeature_WeaponSystem/Config/DefaultGame.ini
[/Script/Lyra.LyraWeaponSettings]
BaseDamage=50.0
FireRate=600.0
MaxAmmo=30

// LyraGameFeature_CharacterAbilities/Config/DefaultGame.ini  
[/Script/Lyra.LyraAbilitySystemSettings]
MaxAbilityLevel=10
AbilityCooldownMultiplier=1.0

配置合并机制

cpp 复制代码
// 当插件激活时,UGameFeatureData 会自动处理:
// 1. 基础配置 (DefaultGame.ini)
// 2. 平台特定配置 (DefaultGame_PS5.ini)  
// 3. 环境特定配置 (DefaultGame_Development.ini)
// 所有这些配置会按正确顺序合并

5. 生命周期管理的独立性

状态驱动的功能管理

cpp 复制代码
// Lyra 中管理 GameFeature 状态的代码示例:
void ULyraGameFeaturePolicy::UpdateGameFeaturePluginStatus(const FString& PluginURL)
{
    // 根据游戏状态决定是否激活插件
    if(ShouldActivatePluginForCurrentState(PluginURL)) {
        UGameFeaturesSubsystem::Get().LoadAndActivateGameFeaturePlugin(PluginURL);
    } else {
        UGameFeaturesSubsystem::Get().DeactivateGameFeaturePlugin(PluginURL);
    }
}

基于游戏模式的功能激活

cpp 复制代码
// 在 Lyra 的游戏模式中控制功能激活:
void ALyraGameMode::OnMatchStarted()
{
    Super::OnMatchStarted();
    
    // 激活当前模式需要的 GameFeatures
    for(const FString& RequiredFeature : GetRequiredGameFeatures()) {
        UGameFeaturesSubsystem::Get().LoadAndActivateGameFeaturePlugin(RequiredFeature);
    }
}

6. 依赖管理的解耦

声明式依赖管理

cpp 复制代码
// 在 .uplugin 文件中声明依赖:
{
    "Dependencies": [
        {
            "Name": "LyraGameFeature_Core",
            "Type": "Runtime",
            "Version": "1.0.0"
        },
        {
            "Name": "LyraGameFeature_UI", 
            "Type": "Runtime",
            "Version": "1.0.0"
        }
    ]
}

运行时依赖解析

cpp 复制代码
// UGameFeatureData 确保依赖按正确顺序加载:
void UGameFeaturesSubsystem::LoadGameFeatureDependencies(const FString& PluginURL)
{
    // 1. 解析插件的依赖关系
    // 2. 递归加载所有依赖项  
    // 3. 确保依赖树正确构建
    // 4. 按正确顺序激活插件
}

7. 实际应用案例

武器系统的独立开发

复制代码
LyraGameFeature_WeaponSystem/
├── Content/
│   ├── Weapons/           # 所有武器资源
│   ├── Animations/        # 武器动画
│   └── UI/               # 武器UI组件
├── Config/
│   └── DefaultGame.ini   # 武器系统配置
├── Source/
│   └── WeaponSystem/     # 武器系统代码
└── LyraGameFeature_WeaponSystem.uplugin

能力系统的独立开发

复制代码
LyraGameFeature_CharacterAbilities/
├── Content/
│   ├── Abilities/        # 游戏玩法能力
│   ├── Effects/          # 游戏效果
│   └── Attributes/       # 属性集
├── Config/
│   └── DefaultGame.ini  # 能力系统配置  
├── Source/
│   └── AbilitySystem/   # 能力系统代码
└── LyraGameFeature_CharacterAbilities.uplugin

优势总结

通过 UGameFeatureData 的这种设计,LyraStarterGame 实现了:

  1. 真正的模块化:每个功能可以独立开发、测试和部署
  2. 热插拔支持:运行时动态加载和卸载功能
  3. 资源隔离:避免资源冲突和依赖混乱
  4. 配置独立:每个功能有自己的配置,不会相互影响
  5. 团队协作:不同团队可以并行开发不同功能
  6. 可扩展性:轻松添加新功能而不影响现有系统

这种架构使得 Lyra 能够支持从简单原型到完整游戏的各种规模开发,同时保持代码的清晰和可维护性。

四、生命周期管理机制控制插件的加载、激活、停用和卸载过程:

1. 生命周期状态机

完整的生命周期状态

cpp 复制代码
// 定义在 GameFeaturePluginStateMachine.h 中
enum class EGameFeaturePluginState
{
    // 初始状态
    Unknown,
    
    // 加载阶段
    Uninstalled,        // 未安装
    Registered,         // 已注册
    Loading,            // 加载中
    Loaded,             // 已加载
    
    // 激活阶段  
    Activating,         // 激活中
    Active,             // 已激活
    
    // 停用阶段
    Deactivating,       // 停用中
    
    // 卸载阶段
    Unloading,          // 卸载中
    Unloaded            // 已卸载
};

2. 状态转换的核心实现

状态机管理类

cpp 复制代码
// Lyra 中扩展的状态机管理
class ULyraGameFeatureStateMachine : public UGameFeaturePluginStateMachine
{
protected:
    // 重写状态转换逻辑
    virtual void UpdateStateMachine() override;
    
    // Lyra 特定的状态检查
    virtual bool CanLoadPlugin() const;
    virtual bool CanActivatePlugin() const;
    virtual bool CanDeactivatePlugin() const;
    virtual bool CanUnloadPlugin() const;
};

状态转换流程

cpp 复制代码
void ULyraGameFeatureStateMachine::UpdateStateMachine()
{
    switch (CurrentState)
    {
    case EGameFeaturePluginState::Uninstalled:
        if (ShouldLoadPlugin())
        {
            BeginLoading();
        }
        break;
        
    case EGameFeaturePluginState::Loaded:
        if (ShouldActivatePlugin())
        {
            BeginActivating();
        }
        break;
        
    case EGameFeaturePluginState::Active:
        if (ShouldDeactivatePlugin())
        {
            BeginDeactivating();
        }
        break;
        
    case EGameFeaturePluginState::Deactivating:
        if (CanUnloadPlugin())
        {
            BeginUnloading();
        }
        break;
    }
}

3. UGameFeatureData 的动作执行机制

生命周期钩子函数

cpp 复制代码
// UGameFeatureAction 的生命周期方法
class UGameFeatureAction : public UObject
{
public:
    // 注册阶段
    virtual void OnGameFeatureRegistering() {}
    
    // 加载阶段
    virtual void OnGameFeatureLoading() {}
    
    // 激活阶段
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) {}
    
    // 停用阶段  
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) {}
    
    // 卸载阶段
    virtual void OnGameFeatureUnloading() {}
};

在 Lyra 中的具体 Action 实现

资源加载 Action
cpp 复制代码
// Lyra 中的资源管理 Action
UCLASS()
class ULyraGameFeatureAction_LoadAssetBundles : public UGameFeatureAction
{
    UPROPERTY(EditAnywhere)
    TArray<FName> AssetBundlesToLoad;
    
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        // 激活时加载资源包
        for (FName BundleName : AssetBundlesToLoad)
        {
            UAssetManager::Get().LoadBundle(BundleName);
        }
    }
    
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
    {
        // 停用时卸载资源包
        for (FName BundleName : AssetBundlesToLoad)
        {
            UAssetManager::Get().UnloadBundle(BundleName);
        }
    }
};
组件注册 Action
cpp 复制代码
// Lyra 中的组件管理 Action
UCLASS()
class ULyraGameFeatureAction_AddComponents : public UGameFeatureAction
{
    UPROPERTY(EditAnywhere)
    TArray<TSubclassOf<UActorComponent>> ComponentsToAdd;
    
    TMap<AActor*, TArray<UActorComponent*>> AddedComponents;
    
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        // 为现有和未来的 Actor 添加组件
        UGameInstance* GameInstance = Context.GetGameInstance();
        if (GameInstance)
        {
            GameInstance->GetOnActorSpawning().AddUObject(this, &ThisClass::OnActorSpawning);
            
            // 为现有 Actor 添加组件
            for (TActorIterator<AActor> It(GameInstance->GetWorld()); It; ++It)
            {
                AddComponentsToActor(*It);
            }
        }
    }
    
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
    {
        // 移除所有添加的组件
        for (auto& Pair : AddedComponents)
        {
            for (UActorComponent* Component : Pair.Value)
            {
                Component->DestroyComponent();
            }
        }
        AddedComponents.Empty();
    }
};

4. Lyra 特定的生命周期策略

基于游戏状态的管理

cpp 复制代码
// LyraGameFeaturePolicy.cpp
void ULyraGameFeaturePolicy::UpdateGameFeaturePluginStatus(const FString& PluginURL)
{
    EGameFeaturePluginState CurrentState = GetPluginState(PluginURL);
    ELyraGameState CurrentGameState = GetCurrentGameState();
    
    switch (CurrentGameState)
    {
    case ELyraGameState::MainMenu:
        // 主菜单时只激活 UI 相关功能
        if (IsUIFeature(PluginURL) && CurrentState < EGameFeaturePluginState::Active)
        {
            UGameFeaturesSubsystem::Get().LoadAndActivateGameFeaturePlugin(PluginURL);
        }
        break;
        
    case ELyraGameState::InGame:
        // 游戏中激活游戏玩法功能
        if (IsGameplayFeature(PluginURL) && CurrentState < EGameFeaturePluginState::Active)
        {
            UGameFeaturesSubsystem::Get().LoadAndActivateGameFeaturePlugin(PluginURL);
        }
        break;
        
    case ELyraGameState::Exiting:
        // 退出时停用所有功能
        if (CurrentState == EGameFeaturePluginState::Active)
        {
            UGameFeaturesSubsystem::Get().DeactivateGameFeaturePlugin(PluginURL);
        }
        break;
    }
}

依赖关系管理

cpp 复制代码
// 处理插件依赖的生命周期
void ULyraGameFeatureStateMachine::HandleDependencyLifecycle()
{
    // 确保依赖按正确顺序加载和激活
    for (const FString& DependencyURL : GetDependencies())
    {
        EGameFeaturePluginState DependencyState = GetPluginState(DependencyURL);
        
        if (DependencyState < EGameFeaturePluginState::Loaded)
        {
            // 依赖未加载,先加载依赖
            UGameFeaturesSubsystem::Get().LoadGameFeaturePlugin(DependencyURL);
        }
        
        if (CurrentState == EGameFeaturePluginState::Activating && 
            DependencyState < EGameFeaturePluginState::Active)
        {
            // 等待依赖激活
            UGameFeaturesSubsystem::Get().ActivateGameFeaturePlugin(DependencyURL);
        }
    }
}

5. 配置驱动的生命周期控制

GameFeatureData 中的配置

cpp 复制代码
// 在具体的 GameFeatureData 资产中配置:
UCLASS()
class ULyraWeaponSystemFeatureData : public UGameFeatureData
{
    UPROPERTY(EditAnywhere, Category = "Lifecycle")
    bool bAutoActivate = true;  // 是否自动激活
    
    UPROPERTY(EditAnywhere, Category = "Lifecycle")
    ELyraGamePhase ActivationPhase = ELyraGamePhase::GameplayReady; // 激活阶段
    
    UPROPERTY(EditAnywhere, Category = "Lifecycle") 
    TArray<FName> RequiredBundles; // 需要加载的资源包
};

阶段管理系统

cpp 复制代码
// Lyra 的游戏阶段管理
void ALyraGamePhaseSubsystem::OnGamePhaseChanged(ELyraGamePhase Phase)
{
    // 通知所有 GameFeature 游戏阶段变化
    for (UGameFeatureData* FeatureData : GetAllRegisteredFeatures())
    {
        if (auto* LyraFeature = Cast<ULyraWeaponSystemFeatureData>(FeatureData))
        {
            if (LyraFeature->ActivationPhase == Phase)
            {
                // 激活达到触发阶段的插件
                UGameFeaturesSubsystem::Get().ActivateGameFeaturePlugin(GetPluginURL(FeatureData));
            }
        }
    }
}

6. 实际生命周期执行示例

武器系统的完整生命周期

cpp 复制代码
// 1. 注册阶段
void ULyraWeaponSystemFeature::OnGameFeatureRegistering()
{
    // 注册武器类型到全局管理器
    ULyraWeaponSystemSubsystem::RegisterWeaponTypes(this);
}

// 2. 加载阶段  
void ULyraWeaponSystemFeature::OnGameFeatureLoading()
{
    // 异步加载武器资源
    LoadWeaponAssetsAsync();
}

// 3. 激活阶段
void ULyraWeaponSystemFeature::OnGameFeatureActivating(FGameFeatureActivatingContext& Context)
{
    // 注册输入映射
    RegisterInputMappings();
    
    // 添加到游戏玩法系统
    RegisterWithAbilitySystem();
    
    // 初始化武器管理器
    InitializeWeaponManager();
}

// 4. 停用阶段
void ULyraWeaponSystemFeature::OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context)
{
    // 移除输入映射
    UnregisterInputMappings();
    
    // 从游戏玩法系统移除
    UnregisterFromAbilitySystem();
    
    // 清理武器实例
    CleanupWeaponInstances();
}

// 5. 卸载阶段
void ULyraWeaponSystemFeature::OnGameFeatureUnloading()
{
    // 释放资源引用
    ReleaseWeaponAssets();
    
    // 从全局管理器注销
    ULyraWeaponSystemSubsystem::UnregisterWeaponTypes(this);
}

7. 错误处理和回滚机制

安全的生命周期管理

cpp 复制代码
void ULyraGameFeatureStateMachine::HandleActivationFailure()
{
    // 激活失败时的回滚处理
    switch (CurrentState)
    {
    case EGameFeaturePluginState::Activating:
        // 回滚到加载状态
        SetState(EGameFeaturePluginState::Loaded);
        
        // 执行部分停用清理
        for (UGameFeatureAction* Action : GetFeatureData()->GetActions())
        {
            Action->OnPartialDeactivation();
        }
        break;
    }
}

这种精细的生命周期管理使得 LyraStarterGame 能够:

  1. 精确控制资源使用:按需加载和卸载资源
  2. 避免状态冲突:确保状态转换的原子性
  3. 支持热重载:在运行时动态更新功能
  4. 优雅的错误处理:在失败时安全回滚
  5. 性能优化:只在需要时激活功能模块

通过这种机制,Lyra 实现了真正意义上的模块化架构,每个游戏功能都可以独立管理自己的生命周期,同时与整个系统协调工作。

五、实现自动化的资源管理:

1. 主资源类型扫描系统

资源扫描配置

cpp 复制代码
// 在 GameFeatureData 中定义要扫描的资源类型
UPROPERTY(EditAnywhere, Category="Game Feature | Asset Manager")
TArray<FPrimaryAssetTypeInfo> PrimaryAssetTypesToScan;

// Lyra 中的具体配置示例:
PrimaryAssetTypesToScan = {
    FPrimaryAssetTypeInfo(
        "WeaponDefinition",                    // 资源类型
        "/Game/WeaponSystem/Weapons",          // 资源目录
        "LyraWeaponDataAsset",                 // 资产基类
        FPrimaryAssetTypeInfo::ForceSynchronousScan, // 扫描选项
        true,                                  // 是否递归扫描
        false                                  // 是否仅编辑器扫描
    ),
    FPrimaryAssetTypeInfo(
        "AmmoType",
        "/Game/WeaponSystem/Ammo", 
        "LyraAmmoDataAsset",
        FPrimaryAssetTypeInfo::ForceSynchronousScan,
        true,
        false
    )
};

扫描过程实现

cpp 复制代码
// Asset Manager 中的扫描逻辑
void ULyraAssetManager::ScanPrimaryAssetTypesForGameFeature(UGameFeatureData* GameFeatureData)
{
    if (!GameFeatureData)
        return;
        
    const TArray<FPrimaryAssetTypeInfo>& AssetTypes = GameFeatureData->GetPrimaryAssetTypesToScan();
    
    for (const FPrimaryAssetTypeInfo& AssetTypeInfo : AssetTypes)
    {
        // 扫描指定目录下的资源
        TArray<FAssetData> AssetDataList;
        AssetRegistry->GetAssetsByPath(FName(*AssetTypeInfo.Directory), AssetDataList, true);
        
        for (const FAssetData& AssetData : AssetDataList)
        {
            // 验证资产类型
            if (AssetData.GetClass()->IsChildOf(AssetTypeInfo.AssetBaseClassLoaded))
            {
                // 注册到主资产系统
                RegisterPrimaryAsset(AssetData);
            }
        }
    }
}

2. 资源包管理机制

资源包定义和配置

cpp 复制代码
// 在 GameFeatureData 中定义资源包依赖
UCLASS()
class ULyraWeaponFeatureData : public UGameFeatureData
{
    UPROPERTY(EditAnywhere, Category = "Resource Management")
    TArray<FName> PreloadBundles; // 预加载的资源包
    
    UPROPERTY(EditAnywhere, Category = "Resource Management")
    TArray<FLyraResourceBundleDependency> BundleDependencies; // 资源包依赖
};

// Lyra 中的资源包依赖结构
USTRUCT()
struct FLyraResourceBundleDependency
{
    GENERATED_BODY()
    
    UPROPERTY(EditAnywhere)
    FName BundleName;
    
    UPROPERTY(EditAnywhere)
    TArray<FName> DependentBundles; // 依赖的其他资源包
};

资源包加载 Action

cpp 复制代码
// 专门的资源包管理 Action
UCLASS()
class ULyraGameFeatureAction_ManageResourceBundles : public UGameFeatureAction
{
    UPROPERTY(EditAnywhere)
    TArray<FName> BundlesToLoadOnActivate; // 激活时加载的包
    
    UPROPERTY(EditAnywhere)
    TArray<FName> BundlesToUnloadOnDeactivate; // 停用时卸载的包
    
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        Super::OnGameFeatureActivating(Context);
        
        // 加载指定的资源包
        for (FName BundleName : BundlesToLoadOnActivate)
        {
            LoadResourceBundle(BundleName);
        }
    }
    
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
    {
        // 卸载指定的资源包
        for (FName BundleName : BundlesToUnloadOnDeactivate)
        {
            UnloadResourceBundle(BundleName);
        }
        
        Super::OnGameFeatureDeactivating(Context);
    }
    
private:
    void LoadResourceBundle(FName BundleName)
    {
        TArray<FName> BundlesToLoad = { BundleName };
        
        // 异步加载资源包
        UAssetManager::Get().LoadBundles(BundlesToLoad, FStreamableDelegate::CreateUObject(
            this, &ULyraGameFeatureAction_ManageResourceBundles::OnBundleLoaded, BundleName));
    }
    
    void OnBundleLoaded(FName BundleName)
    {
        UE_LOG(LogLyra, Log, TEXT("Resource bundle %s loaded for game feature"), *BundleName.ToString());
    }
};

3. 异步资源加载系统

基于引用的资源预加载

cpp 复制代码
// 管理软引用资源的预加载
UCLASS()
class ULyraGameFeatureAction_PreloadAssets : public UGameFeatureAction
{
    UPROPERTY(EditAnywhere)
    TArray<TSoftObjectPtr<UObject>> AssetsToPreload; // 要预加载的资产
    
    UPROPERTY(EditAnywhere)
    TArray<TSoftClassPtr<UObject>> ClassesToPreload; // 要预加载的类
    
private:
    FStreamableHandle* PreloadHandle = nullptr;
    
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        TArray<FSoftObjectPath> PathsToLoad;
        
        // 收集所有要加载的资产路径
        for (const TSoftObjectPtr<UObject>& AssetPtr : AssetsToPreload)
        {
            if (!AssetPtr.IsNull())
            {
                PathsToLoad.Add(AssetPtr.ToSoftObjectPath());
            }
        }
        
        for (const TSoftClassPtr<UObject>& ClassPtr : ClassesToPreload)
        {
            if (!ClassPtr.IsNull())
            {
                PathsToLoad.Add(ClassPtr.ToSoftObjectPath());
            }
        }
        
        // 异步预加载所有资产
        if (PathsToLoad.Num() > 0)
        {
            PreloadHandle = UAssetManager::Get().LoadAssetList(PathsToLoad,
                FStreamableDelegate::CreateUObject(this, &ThisClass::OnAssetsPreloaded));
        }
    }
    
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
    {
        // 取消预加载并释放引用
        if (PreloadHandle)
        {
            PreloadHandle->CancelHandle();
            PreloadHandle = nullptr;
        }
    }
};

4. 资源依赖关系解析

依赖图构建

cpp 复制代码
// Lyra 中的资源依赖管理系统
class ULyraResourceDependencyManager : public UEngineSubsystem
{
public:
    // 构建游戏功能的资源依赖图
    void BuildDependencyGraphForGameFeature(UGameFeatureData* GameFeatureData)
    {
        TArray<FPrimaryAssetId> FeatureAssets = GetAllPrimaryAssetsForFeature(GameFeatureData);
        
        for (const FPrimaryAssetId& AssetId : FeatureAssets)
        {
            // 获取资产的直接依赖
            TArray<FPrimaryAssetId> DirectDependencies;
            UAssetManager::Get().GetPrimaryAssetDependencies(AssetId, DirectDependencies);
            
            // 构建依赖图
            ResourceDependencyGraph.Add(AssetId, DirectDependencies);
            
            // 递归处理传递依赖
            ProcessTransitiveDependencies(AssetId, DirectDependencies);
        }
    }
    
private:
    TMap<FPrimaryAssetId, TArray<FPrimaryAssetId>> ResourceDependencyGraph;
};

智能预加载策略

cpp 复制代码
// 基于使用预测的资源预加载
void ULyraAssetManager::PreloadAssetsForGameFeature(UGameFeatureData* GameFeatureData, 
                                                   ELyraGamePhase ExpectedUsagePhase)
{
    // 根据预期使用阶段决定预加载策略
    EAssetLoadingPriority Priority = GetPriorityForGamePhase(ExpectedUsagePhase);
    
    // 获取该功能的所有资源
    TArray<FPrimaryAssetId> FeatureAssets = GetAllPrimaryAssetsForFeature(GameFeatureData);
    
    // 根据优先级分批预加载
    PreloadAssetBatch(FeatureAssets, Priority);
}

5. 内存管理优化

资源生命周期绑定

cpp 复制代码
// 将资源生命周期与游戏功能绑定
UCLASS()
class ULyraGameFeatureAction_BindResourceLifetimes : public UGameFeatureAction
{
    UPROPERTY(EditAnywhere)
    TMap<FName, ELyraResourceLifetime> ResourceLifetimes; // 资源生命周期配置
    
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        // 为资源设置生命周期策略
        for (const auto& Pair : ResourceLifetimes)
        {
            FName BundleName = Pair.Key;
            ELyraResourceLifetime Lifetime = Pair.Value;
            
            UAssetManager::Get().SetBundleLifetime(BundleName, Lifetime);
        }
    }
};

// Lyra 中的资源生命周期枚举
UENUM()
enum class ELyraResourceLifetime : uint8
{
    Permanent,      // 永久保持加载
    LevelBound,     // 关卡生命周期
    SessionBound,   // 会话生命周期
    Temporary       // 临时使用
};

内存使用监控

cpp 复制代码
// 资源使用监控系统
class ULyraResourceMonitor : public UObject
{
public:
    void MonitorGameFeatureResources(UGameFeatureData* GameFeatureData)
    {
        FName FeatureName = GetFeatureName(GameFeatureData);
        
        // 跟踪资源内存使用
        MemoryUsageTracker.Add(FeatureName, 0);
        
        // 定期报告内存使用情况
        GetWorld()->GetTimerManager().SetTimer(ReportTimer, 
            [this, FeatureName]() { ReportMemoryUsage(FeatureName); }, 
            5.0f, true);
    }
    
private:
    TMap<FName, int64> MemoryUsageTracker;
    
    void ReportMemoryUsage(FName FeatureName)
    {
        int64 MemoryUsed = CalculateMemoryUsage(FeatureName);
        UE_LOG(LogLyra, Verbose, TEXT("GameFeature %s using %lld bytes"), 
               *FeatureName.ToString(), MemoryUsed);
    }
};

6. 实际应用示例

武器系统的资源管理配置

cpp 复制代码
// 武器功能 GameFeatureData 的资源配置
ULyraWeaponFeatureData::ULyraWeaponFeatureData()
{
    // 配置要扫描的资源类型
    PrimaryAssetTypesToScan = {
        FPrimaryAssetTypeInfo("Weapon", "/Game/Weapons", "LyraWeaponItemDefinition"),
        FPrimaryAssetTypeInfo("Ammo", "/Game/Ammo", "LyraAmmoItemDefinition"),
        FPrimaryAssetTypeInfo("WeaponSkin", "/Game/WeaponSkins", "LyraWeaponSkinDefinition")
    };
    
    // 配置资源包
    PreloadBundles = { "WeaponCore", "WeaponMeshes", "WeaponSounds" };
    
    // 配置资源生命周期
    ResourceLifetimes = {
        { "WeaponCore", ELyraResourceLifetime::Permanent },
        { "WeaponMeshes", ELyraResourceLifetime::SessionBound },
        { "WeaponSounds", ELyraResourceLifetime::LevelBound }
    };
}

资源加载的性能优化

cpp 复制代码
// 渐进式资源加载策略
void ULyraGameFeatureResourceLoader::LoadResourcesProgressively(UGameFeatureData* GameFeatureData)
{
    // 第一阶段:加载核心资源(同步)
    LoadCoreResources(GameFeatureData);
    
    // 第二阶段:加载常用资源(异步,高优先级)
    LoadHighPriorityResourcesAsync(GameFeatureData);
    
    // 第三阶段:加载可选资源(异步,低优先级)
    LoadLowPriorityResourcesAsync(GameFeatureData);
    
    // 基于使用预测的预加载
    PredictAndPreloadResources(GameFeatureData);
}

7. 错误处理和恢复

资源加载失败处理

cpp 复制代码
void ULyraGameFeatureAction_ManageResourceBundles::OnBundleLoadFailed(FName BundleName)
{
    UE_LOG(LogLyra, Warning, TEXT("Failed to load resource bundle: %s"), *BundleName.ToString());
    
    // 尝试回退到基本资源
    if (FallbackBundles.Contains(BundleName))
    {
        LoadResourceBundle(FallbackBundles[BundleName]);
    }
    
    // 通知系统资源加载失败
    OnResourceLoadFailure.Broadcast(BundleName);
}

通过这种精细的资源管理系统,LyraStarterGame 实现了:

  1. 自动化资源发现:自动扫描和注册插件相关资源
  2. 智能预加载:基于使用模式预测和预加载资源
  3. 内存优化:精确控制资源生命周期和内存使用
  4. 性能分层:根据优先级分层加载资源
  5. 错误恢复:健壮的错误处理和回退机制
  6. 依赖管理:自动解析和处理资源依赖关系

这使得每个游戏功能插件都能高效地管理自己的资源,同时与整个游戏的资源系统无缝集成。

六、实现灵活的配置覆盖管理:

1. 层次化配置系统架构

配置加载顺序定义

cpp 复制代码
// GameFeatureData 中的配置初始化方法
static UE_API void InitializeHierarchicalPluginIniFiles(const FString& PluginInstalledFilename)
{
    // 配置文件的加载层次结构:
    // 1. Base Defaults (最低优先级)
    // 2. Platform Overrides
    // 3. Project Overrides  
    // 4. User Overrides (最高优先级)
    
    FString PluginName = GetPluginNameFromFilename(PluginInstalledFilename);
    
    // 加载基础默认配置
    LoadBasePluginIniFiles(PluginName);
    
    // 加载平台特定配置
    LoadPlatformSpecificIniFiles(PluginName);
    
    // 加载项目特定配置
    LoadProjectSpecificIniFiles(PluginName);
    
    // 加载用户特定配置
    LoadUserSpecificIniFiles(PluginName);
}

配置文件路径解析

cpp 复制代码
// 配置文件的层次化路径生成
void ULyraConfigManager::BuildConfigHierarchy(const FString& PluginName)
{
    ConfigHierarchy.Add(TEXT("Base"), FPaths::ProjectPluginsDir() / PluginName / TEXT("Config/DefaultGame.ini"));
    ConfigHierarchy.Add(TEXT("Platform"), FPaths::ProjectPluginsDir() / PluginName / TEXT("Config/DefaultGame_") + UGameplayStatics::GetPlatformName() + TEXT(".ini"));
    ConfigHierarchy.Add(TEXT("Project"), FPaths::ProjectConfigDir() / TEXT("Plugins") / PluginName / TEXT("Game.ini"));
    ConfigHierarchy.Add(TEXT("User"), FPaths::GeneratedConfigDir() / TEXT("Plugins") / PluginName / TEXT("UserSettings.ini"));
}

2. 配置合并机制

INI 文件合并实现

cpp 复制代码
// 配置合并的核心逻辑
void UGameFeatureData::MergePluginConfigurations(const FString& PluginName)
{
    TArray<FString> ConfigFiles = FindAllConfigFilesForPlugin(PluginName);
    
    // 按优先级排序配置文件
    ConfigFiles.Sort([](const FString& A, const FString& B) {
        return GetConfigPriority(A) < GetConfigPriority(B);
    });
    
    // 创建合并后的配置对象
    FConfigFile MergedConfig;
    
    for (const FString& ConfigFile : ConfigFiles)
    {
        FConfigFile PartialConfig;
        PartialConfig.Read(ConfigFile);
        
        // 合并到主配置
        MergedConfig.Combine(PartialConfig);
    }
    
    // 应用合并后的配置
    ApplyMergedConfigToRuntime(PluginName, MergedConfig);
}

配置冲突解决策略

cpp 复制代码
// 处理配置键冲突
FString ULyraConfigManager::ResolveConfigConflict(const FString& Section, const FString& Key, 
                                                 const TArray<FConfigValue>& ConflictingValues)
{
    // 应用"最后写入获胜"策略
    // 更高优先级的配置覆盖低优先级配置
    
    for (int32 i = ConflictingValues.Num() - 1; i >= 0; i--)
    {
        const FConfigValue& Value = ConflictingValues[i];
        if (!Value.Value.IsEmpty())
        {
            return Value.Value;
        }
    }
    
    return FString();
}

3. 运行时配置重载

配置热重载系统

cpp 复制代码
// 配置变更时的重载机制
static UE_API void ReloadConfigs(FConfigFile& PluginConfig)
{
    // 重新加载所有受影响的 UObject 配置
    for (TObjectIterator<UObject> It; It; ++It)
    {
        UObject* Object = *It;
        if (Object && Object->HasAllFlags(RF_ClassDefaultObject))
        {
            // 检查该对象是否使用此插件的配置
            if (UsesPluginConfig(Object, PluginConfig))
            {
                Object->ReloadConfig();
            }
        }
    }
    
    // 通知配置变更
    FCoreDelegates::OnConfigChanged.Broadcast();
}

Lyra 特定的配置监听器

cpp 复制代码
// 监听配置变更并响应
class ULyraConfigWatcher : public UObject
{
public:
    void WatchGameFeatureConfig(UGameFeatureData* GameFeatureData)
    {
        FString PluginName = GameFeatureData->GetPluginName();
        
        // 注册配置变更委托
        FCoreDelegates::OnConfigChanged.AddUObject(this, 
            &ULyraConfigWatcher::OnConfigChanged, PluginName);
    }
    
private:
    void OnConfigChanged(const FString& ChangedPluginName)
    {
        if (ChangedPluginName == TargetPluginName)
        {
            // 执行配置相关的重新初始化
            ReinitializeFromConfig();
            
            // 通知相关系统配置已变更
            BroadcastConfigChanged();
        }
    }
};

4. 环境特定的配置覆盖

开发/发布环境配置

cpp 复制代码
// 环境特定的配置管理
void ULyraGameFeatureData::LoadEnvironmentSpecificConfigs(const FString& PluginName)
{
#if WITH_EDITOR
    // 编辑器环境配置
    LoadConfigFile(PluginName, TEXT("DefaultGame_Editor.ini"));
#elif UE_BUILD_DEBUG
    // Debug 构建配置  
    LoadConfigFile(PluginName, TEXT("DefaultGame_Debug.ini"));
#elif UE_BUILD_DEVELOPMENT
    // 开发构建配置
    LoadConfigFile(PluginName, TEXT("DefaultGame_Development.ini"));
#else
    // 发布构建配置
    LoadConfigFile(PluginName, TEXT("DefaultGame_Ship.ini"));
#endif

    // 命令行覆盖
    ApplyCommandLineOverrides(PluginName);
}

平台特定配置

cpp 复制代码
// 多平台配置支持
void ULyraPlatformConfigManager::ApplyPlatformOverrides(const FString& PluginName)
{
    FString PlatformName = UGameplayStatics::GetPlatformName();
    FString PlatformConfigFile = FString::Printf(TEXT("DefaultGame_%s.ini"), *PlatformName);
    
    if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*GetConfigPath(PluginName, PlatformConfigFile)))
    {
        LoadConfigFile(PluginName, PlatformConfigFile);
    }
    
    // 应用平台特定的硬件配置
    ApplyHardwareSpecificOverrides(PluginName);
}

5. 配置数据驱动系统

配置驱动的功能开关

cpp 复制代码
// 在 GameFeatureData 中定义配置驱动的功能
UCLASS()
class ULyraWeaponFeatureData : public UGameFeatureData
{
    UPROPERTY(Config, EditAnywhere, Category = "Feature Toggles")
    bool bEnableAdvancedBallistics = true;
    
    UPROPERTY(Config, EditAnywhere, Category = "Feature Toggles")  
    bool bEnableWeaponCustomization = false;
    
    UPROPERTY(Config, EditAnywhere, Category = "Balance")
    float BaseDamageMultiplier = 1.0f;
    
    UPROPERTY(Config, EditAnywhere, Category = "Balance")
    TArray<FName> DisabledWeaponTypes;
};

// 配置读取和验证
void ULyraWeaponFeatureData::ApplyConfigOverrides()
{
    // 从合并的配置中读取值
    if (GConfig)
    {
        bool bConfigValue;
        if (GConfig->GetBool(TEXT("/Script/Lyra.LyraWeaponFeatureData"), TEXT("bEnableAdvancedBallistics"), bConfigValue, GetConfigFilename()))
        {
            bEnableAdvancedBallistics = bConfigValue;
        }
        
        float FloatValue;
        if (GConfig->GetFloat(TEXT("/Script/Lyra.LyraWeaponFeatureData"), TEXT("BaseDamageMultiplier"), FloatValue, GetConfigFilename()))
        {
            BaseDamageMultiplier = FloatValue;
        }
    }
}

6. 动态配置覆盖机制

运行时配置修改

cpp 复制代码
// 允许运行时修改配置
class ULyraDynamicConfigManager : public UObject
{
public:
    // 动态覆盖配置值
    void SetConfigOverride(const FString& PluginName, const FString& Section, 
                          const FString& Key, const FString& Value)
    {
        FConfigFile* PluginConfig = FindPluginConfig(PluginName);
        if (PluginConfig)
        {
            // 设置覆盖值
            PluginConfig->SetString(*Section, *Key, *Value);
            
            // 立即应用变更
            ApplyConfigOverride(PluginName, Section, Key, Value);
        }
    }
    
    // 应用配置覆盖到运行时
    void ApplyConfigOverride(const FString& PluginName, const FString& Section, 
                            const FString& Key, const FString& Value)
    {
        // 更新所有相关的 UObject 实例
        for (TObjectIterator<UObject> It; It; ++It)
        {
            if (It->GetClass()->GetName() == Section)
            {
                It->ProcessEvent(It->FindFunction("ReloadConfig"), nullptr);
            }
        }
        
        // 保存用户配置
        SaveUserConfig(PluginName, Section, Key, Value);
    }
};

配置版本管理

cpp 复制代码
// 配置版本控制和迁移
class ULyraConfigVersioning : public UObject
{
public:
    void HandleConfigVersionMigration(const FString& PluginName, int32 FromVersion, int32 ToVersion)
    {
        FConfigFile* PluginConfig = FindPluginConfig(PluginName);
        if (!PluginConfig) return;
        
        // 执行版本迁移
        for (int32 Version = FromVersion + 1; Version <= ToVersion; Version++)
        {
            ExecuteVersionMigration(PluginName, Version, *PluginConfig);
        }
        
        // 更新版本号
        PluginConfig->SetInt(TEXT("VersionInfo"), TEXT("ConfigVersion"), ToVersion);
    }
    
private:
    void ExecuteVersionMigration(const FString& PluginName, int32 Version, FConfigFile& Config)
    {
        switch (Version)
        {
        case 2:
            // 从版本1迁移到版本2
            MigrateV1ToV2(PluginName, Config);
            break;
        case 3:
            // 从版本2迁移到版本3  
            MigrateV2ToV3(PluginName, Config);
            break;
        }
    }
};

7. 实际应用示例

武器系统的配置层次

cpp 复制代码
// 武器 GameFeature 的配置覆盖示例
void ULyraWeaponSystemFeature::InitializeConfigHierarchy()
{
    FString PluginName = GetPluginName();
    
    // 1. 基础配置 (插件目录)
    // /Plugins/LyraWeaponSystem/Config/DefaultGame.ini
    LoadConfigFile(PluginName, TEXT("DefaultGame.ini"));
    
    // 2. 平台配置  
    // /Plugins/LyraWeaponSystem/Config/DefaultGame_PS5.ini
    LoadPlatformSpecificConfigs(PluginName);
    
    // 3. 项目配置 (项目级覆盖)
    // /Config/Plugins/LyraWeaponSystem/Game.ini
    LoadConfigFile(PluginName, TEXT("../../../Config/Plugins/LyraWeaponSystem/Game.ini"));
    
    // 4. 环境配置
    // /Plugins/LyraWeaponSystem/Config/DefaultGame_Development.ini
    LoadEnvironmentSpecificConfigs(PluginName);
    
    // 5. 用户配置 (最高优先级)
    // /Saved/Config/Plugins/LyraWeaponSystem/UserSettings.ini
    LoadUserConfigs(PluginName);
}

配置验证和回退

cpp 复制代码
// 配置完整性检查
bool ULyraConfigValidator::ValidateGameFeatureConfig(UGameFeatureData* GameFeatureData)
{
    FString PluginName = GameFeatureData->GetPluginName();
    FConfigFile* PluginConfig = FindPluginConfig(PluginName);
    
    if (!PluginConfig || PluginConfig->Num() == 0)
    {
        // 配置缺失,使用默认值
        ApplyDefaultConfig(GameFeatureData);
        return false;
    }
    
    // 验证必需配置节
    TArray<FString> RequiredSections = GetRequiredConfigSections(GameFeatureData);
    for (const FString& Section : RequiredSections)
    {
        if (!PluginConfig->Contains(Section))
        {
            UE_LOG(LogLyra, Warning, TEXT("Missing required config section %s for plugin %s"), 
                   *Section, *PluginName);
            return false;
        }
    }
    
    return true;
}

8. 高级配置特性

配置继承和模板

cpp 复制代码
// 配置模板系统
class ULyraConfigTemplateSystem : public UObject
{
public:
    void ApplyConfigTemplate(const FString& PluginName, const FString& TemplateName)
    {
        FString TemplatePath = FString::Printf(TEXT("/Game/ConfigTemplates/%s"), *TemplateName);
        ULyraConfigTemplate* Template = LoadObject<ULyraConfigTemplate>(nullptr, *TemplatePath);
        
        if (Template)
        {
            // 应用模板配置
            Template->ApplyToPlugin(PluginName);
        }
    }
};

// 配置模板资产
UCLASS()
class ULyraConfigTemplate : public UDataAsset
{
    UPROPERTY(EditAnywhere)
    TMap<FString, FString> ConfigValues;
    
    void ApplyToPlugin(const FString& PluginName)
    {
        for (const auto& Pair : ConfigValues)
        {
            FString Section, Key;
            Pair.Key.Split(TEXT("."), &Section, &Key);
            
            ULyraDynamicConfigManager::Get().SetConfigOverride(PluginName, Section, Key, Pair.Value);
        }
    }
};

通过这种层次化的配置管理系统,LyraStarterGame 实现了:

  1. 灵活的配置覆盖:支持多层次的配置优先级
  2. 环境适应性:不同环境和平台使用不同配置
  3. 运行时配置:支持热重载和动态修改
  4. 配置验证:确保配置的完整性和正确性
  5. 版本管理:支持配置格式的版本迁移
  6. 模板系统:快速应用预定义的配置模板

这使得每个游戏功能插件都能拥有独立且灵活的配置管理,同时保持与整个项目配置系统的协调一致。

七、实现无限功能扩展的机制

1. UGameFeatureAction 的核心架构

基础动作类设计

cpp 复制代码
// 核心基类定义
UCLASS(Abstract, EditInlineNew, DefaultToInstanced)
class UGameFeatureAction : public UObject
{
    GENERATED_BODY()

public:
    // 生命周期钩子方法
    virtual void OnGameFeatureRegistering() {}
    virtual void OnGameFeatureLoading() {}
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) {}
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) {}
    virtual void OnGameFeatureUnloading() {}
    
    // 世界相关的钩子
    virtual void OnGameFeatureRegisteringWorld(UWorld* World) {}
    virtual void OnGameFeatureActivatingWorld(UWorld* World) {}
    virtual void OnGameFeatureDeactivatingWorld(UWorld* World) {}
    
#if WITH_EDITOR
    virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const { return EDataValidationResult::Valid; }
#endif
};

2. 动作系统的可扩展性设计

插件化的动作注册

cpp 复制代码
// 自动发现和注册所有 GameFeatureAction 子类
class ULyraGameFeatureActionRegistry : public UEngineSubsystem
{
public:
    virtual void Initialize(FSubsystemCollectionBase& Collection) override
    {
        // 扫描所有 UGameFeatureAction 派生类
        for (TObjectIterator<UClass> It; It; ++It)
        {
            if (It->IsChildOf(UGameFeatureAction::StaticClass()) && !It->HasAnyClassFlags(CLASS_Abstract))
            {
                RegisteredActionClasses.Add(*It);
            }
        }
        
        // 按类别组织动作
        OrganizeActionsByCategory();
    }
    
    TArray<UClass*> GetActionsByCategory(const FString& Category) const
    {
        return ActionCategories.FindRef(Category);
    }
    
private:
    TArray<UClass*> RegisteredActionClasses;
    TMap<FString, TArray<UClass*>> ActionCategories;
};

3. 丰富的内置动作类型

资源管理动作

cpp 复制代码
// 资源加载和卸载动作
UCLASS(DisplayName = "Load Asset Bundles")
class ULyraGameFeatureAction_LoadAssetBundles : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Category = "Resources")
    TArray<FName> BundlesToLoad;
    
    UPROPERTY(EditAnywhere, Category = "Resources")
    TArray<FName> BundlesToUnloadOnDeactivate;

private:
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        for (FName BundleName : BundlesToLoad)
        {
            UAssetManager::Get().LoadBundle(BundleName);
        }
    }
    
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
    {
        for (FName BundleName : BundlesToUnloadOnDeactivate)
        {
            UAssetManager::Get().UnloadBundle(BundleName);
        }
    }
};

组件管理动作

cpp 复制代码
// 动态添加组件到 Actor
UCLASS(DisplayName = "Add Components")
class ULyraGameFeatureAction_AddComponents : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Category = "Components")
    TArray<FLyraComponentEntry> ComponentList;
    
    UPROPERTY(EditAnywhere, Category = "Components")
    TArray<TSoftClassPtr<AActor>> TargetActorClasses;

private:
    struct FPerContextData
    {
        TArray<TSharedPtr<FComponentRequestHandle>> ComponentRequests;
    };
    
    TMap<FGameFeatureStateChangeContext, FPerContextData> ContextData;
    
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        FPerContextData& ActiveData = ContextData.FindOrAdd(Context);
        
        for (const FLyraComponentEntry& Entry : ComponentList)
        {
            // 为匹配的 Actor 添加组件
            ULyraComponentManager::Get().AddComponentRequest(
                Entry.ComponentClass, 
                Entry.bClientComponent, 
                Entry.bServerComponent
            );
        }
    }
};

输入系统动作

cpp 复制代码
// 注册输入映射上下文
UCLASS(DisplayName = "Add Input Mapping")
class ULyraGameFeatureAction_AddInputMapping : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Category = "Input")
    TSoftObjectPtr<UInputMappingContext> InputMapping;
    
    UPROPERTY(EditAnywhere, Category = "Input")
    int32 Priority = 0;

private:
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        if (UEnhancedInputLocalPlayerSubsystem* InputSubsystem = GetInputSubsystem(Context))
        {
            InputSubsystem->AddMappingContext(InputMapping.LoadSynchronous(), Priority);
        }
    }
    
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
    {
        if (UEnhancedInputLocalPlayerSubsystem* InputSubsystem = GetInputSubsystem(Context))
        {
            InputSubsystem->RemoveMappingContext(InputMapping.Get());
        }
    }
};

游戏能力系统动作

cpp 复制代码
// 注册游戏玩法能力
UCLASS(DisplayName = "Register Gameplay Abilities")
class ULyraGameFeatureAction_RegisterAbilities : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Category = "Abilities")
    TArray<TSubclassOf<UGameplayAbility>> AbilitiesToRegister;
    
    UPROPERTY(EditAnywhere, Category = "Abilities")
    TArray<TSubclassOf<UGameplayEffect>> EffectsToRegister;

private:
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        if (ULyraAbilitySystemComponent* AbilitySystem = GetAbilitySystem(Context))
        {
            for (TSubclassOf<UGameplayAbility> AbilityClass : AbilitiesToRegister)
            {
                AbilitySystem->GiveAbility(FGameplayAbilitySpec(AbilityClass));
            }
        }
    }
};

4. 自定义动作创建框架

动作工厂系统

cpp 复制代码
// 支持动态创建自定义动作
class ULyraGameFeatureActionFactory : public UObject
{
public:
    template<typename TActionType>
    static TActionType* CreateActionForFeature(UGameFeatureData* FeatureData, const FString& ActionName)
    {
        TActionType* NewAction = NewObject<TActionType>(FeatureData);
        NewAction->SetFlags(RF_Transactional | RF_Public);
        
        FeatureData->GetMutableActionsInEditor().Add(NewAction);
        
        return NewAction;
    }
    
    // 从数据资产创建动作
    static UGameFeatureAction* CreateActionFromAsset(UObject* ActionAsset);
};

动作配置数据资产

cpp 复制代码
// 可重用的动作配置模板
UCLASS()
class ULyraGameFeatureActionConfig : public UDataAsset
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Instanced, Category = "Actions")
    TArray<UGameFeatureAction*> ActionTemplates;
    
    // 应用到目标 GameFeatureData
    void ApplyToGameFeature(UGameFeatureData* TargetFeature) const
    {
        for (UGameFeatureAction* TemplateAction : ActionTemplates)
        {
            if (TemplateAction)
            {
                UGameFeatureAction* NewAction = DuplicateObject(TemplateAction, TargetFeature);
                TargetFeature->GetMutableActionsInEditor().Add(NewAction);
            }
        }
    }
};

5. 动作组合和依赖管理

动作执行顺序控制

cpp 复制代码
// 动作依赖关系解析
class ULyraGameFeatureActionScheduler : public UObject
{
public:
    void ScheduleActionsForActivation(UGameFeatureData* FeatureData)
    {
        TArray<UGameFeatureAction*> SortedActions = TopologicalSortActions(FeatureData->GetActions());
        
        for (UGameFeatureAction* Action : SortedActions)
        {
            FGameFeatureActivatingContext Context;
            Action->OnGameFeatureActivating(Context);
            
            // 等待异步操作完成
            WaitForActionCompletion(Action);
        }
    }
    
private:
    TArray<UGameFeatureAction*> TopologicalSortActions(const TArray<UGameFeatureAction*>& Actions)
    {
        // 基于依赖关系进行拓扑排序
        // 确保依赖的动作先执行
        TArray<UGameFeatureAction*> SortedActions;
        TSet<UGameFeatureAction*> VisitedActions;
        
        for (UGameFeatureAction* Action : Actions)
        {
            if (!VisitedActions.Contains(Action))
            {
                VisitAction(Action, Actions, VisitedActions, SortedActions);
            }
        }
        
        return SortedActions;
    }
};

动作条件执行

cpp 复制代码
// 条件化动作执行
UCLASS(DisplayName = "Conditional Action")
class ULyraGameFeatureAction_Conditional : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Instanced, Category = "Conditional")
    UGameFeatureAction* ConditionAction;
    
    UPROPERTY(EditAnywhere, Instanced, Category = "Conditional")
    UGameFeatureAction* TrueAction;
    
    UPROPERTY(EditAnywhere, Instanced, Category = "Conditional")  
    UGameFeatureAction* FalseAction;
    
    UPROPERTY(EditAnywhere, Category = "Conditional")
    FName ConditionFlag;

private:
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        bool bConditionMet = CheckCondition(Context);
        
        if (bConditionMet && TrueAction)
        {
            TrueAction->OnGameFeatureActivating(Context);
        }
        else if (!bConditionMet && FalseAction)
        {
            FalseAction->OnGameFeatureActivating(Context);
        }
    }
};

6. 运行时动作动态修改

动作热重载系统

cpp 复制代码
// 支持运行时修改动作配置
class ULyraGameFeatureActionHotReload : public UObject
{
public:
    void UpdateActionAtRuntime(UGameFeatureData* FeatureData, UGameFeatureAction* OldAction, UGameFeatureAction* NewAction)
    {
        // 先停用旧动作
        FGameFeatureDeactivatingContext DeactivateContext;
        OldAction->OnGameFeatureDeactivating(DeactivateContext);
        
        // 替换动作实例
        TArray<UGameFeatureAction*>& Actions = FeatureData->GetMutableActionsInEditor();
        int32 Index = Actions.Find(OldAction);
        if (Index != INDEX_NONE)
        {
            Actions[Index] = NewAction;
            
            // 激活新动作
            FGameFeatureActivatingContext ActivateContext;
            NewAction->OnGameFeatureActivating(ActivateContext);
        }
    }
};

动作参数动态绑定

cpp 复制代码
// 支持运行时参数注入
UCLASS(DisplayName = "Dynamic Parameter Action")
class ULyraGameFeatureAction_DynamicParams : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Category = "Parameters")
    TMap<FString, FString> ParameterMap;
    
    // 运行时更新参数
    void UpdateParameter(const FString& Key, const FString& Value)
    {
        ParameterMap.FindOrAdd(Key) = Value;
        
        // 立即应用参数变更
        ApplyParameterChanges();
    }

private:
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        ApplyParameterChanges();
    }
    
    void ApplyParameterChanges()
    {
        // 将参数应用到相关系统
        for (const auto& Param : ParameterMap)
        {
            ULyraConfigManager::Get().SetDynamicParameter(Param.Key, Param.Value);
        }
    }
};

7. 实际扩展应用示例

自定义UI扩展动作

cpp 复制代码
// 完全自定义的动作类型 - UI扩展
UCLASS(DisplayName = "Add UI Widgets")
class ULyraGameFeatureAction_AddUIWidgets : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Category = "UI")
    TSoftClassPtr<UUserWidget> WidgetClass;
    
    UPROPERTY(EditAnywhere, Category = "UI")
    FName WidgetSlotName;
    
    UPROPERTY(EditAnywhere, Category = "UI")
    int32 ZOrder = 0;

private:
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        if (ULyraUIManager* UIManager = GetUIManager(Context))
        {
            UIManager->RegisterGameFeatureWidget(WidgetClass, WidgetSlotName, ZOrder);
        }
    }
    
    virtual void OnGameFeatureDeactivating(FGameFeatureDeactivatingContext& Context) override
    {
        if (ULyraUIManager* UIManager = GetUIManager(Context))
        {
            UIManager->UnregisterGameFeatureWidget(WidgetClass);
        }
    }
};

自定义音效系统动作

cpp 复制代码
// 音效系统扩展动作
UCLASS(DisplayName = "Register Audio Banks")
class ULyraGameFeatureAction_RegisterAudio : public UGameFeatureAction
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, Category = "Audio")
    TArray<FName> AudioBanksToLoad;
    
    UPROPERTY(EditAnywhere, Category = "Audio")
    TMap<FName, USoundBase*> SoundEvents;

private:
    virtual void OnGameFeatureActivating(FGameFeatureActivatingContext& Context) override
    {
        if (ULyraAudioManager* AudioManager = GetAudioManager(Context))
        {
            for (FName BankName : AudioBanksToLoad)
            {
                AudioManager->LoadAudioBank(BankName);
            }
            
            for (const auto& SoundEvent : SoundEvents)
            {
                AudioManager->RegisterGameFeatureSound(SoundEvent.Key, SoundEvent.Value);
            }
        }
    }
};

8. 动作调试和可视化

动作执行监控

cpp 复制代码
// 动作执行状态监控
class ULyraGameFeatureActionDebugger : public UObject
{
public:
    void MonitorActionExecution(UGameFeatureData* FeatureData)
    {
        for (UGameFeatureAction* Action : FeatureData->GetActions())
        {
            FActionExecutionState& State = ActionStates.FindOrAdd(Action);
            State.StartTime = FPlatformTime::Seconds();
            State.bIsExecuting = true;
            
            // 记录执行日志
            UE_LOG(LogLyra, Log, TEXT("Executing action: %s"), *Action->GetName());
        }
    }
    
private:
    struct FActionExecutionState
    {
        double StartTime;
        bool bIsExecuting;
        FString StatusMessage;
    };
    
    TMap<UGameFeatureAction*, FActionExecutionState> ActionStates;
};

通过这种设计,UGameFeatureAction 系统实现了真正的无限扩展性:

  1. 架构开放性:任何 UObject 都可以通过继承创建新动作类型
  2. 组合灵活性:动作可以任意组合和嵌套使用
  3. 生命周期集成:完整的插件生命周期集成
  4. 运行时动态性:支持热重载和参数动态修改
  5. 依赖管理:智能的动作执行顺序控制
  6. 调试支持:完整的执行状态监控和可视化

这使得开发人员可以为任何系统功能创建专用的 GameFeatureAction,实现真正的"即插即用"功能扩展。

相关推荐
阿恩.77039 分钟前
2026年1月最新计算机、人工智能、经济管理国际会议:选对会议 = 论文成功率翻倍
人工智能·经验分享·笔记·计算机网络·金融·区块链
xlq223221 小时前
22.多态(下)
开发语言·c++·算法
('-')1 小时前
《从根上理解MySQL是怎样运行的》第二十章笔记
数据库·笔记·mysql
zkl_zkl_1 小时前
地理信息系统学习笔记——第六章 空间数据采集与处理
笔记·学习·数据处理·数据质量·空间数据
光头程序员2 小时前
学习笔记——主攻 vite
笔记·学习
零匠学堂20252 小时前
移动学习系统,如何提升企业培训效果?
java·开发语言·spring boot·学习·音视频
不会c嘎嘎2 小时前
【数据结构】AVL树详解:从原理到C++实现
数据结构·c++
客梦2 小时前
数据结构-栈与队列
数据结构·笔记
AKDreamer_HeXY2 小时前
ABC434E 题解
c++·算法·图论·atcoder