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,实现真正的"即插即用"功能扩展。

相关推荐
fqbqrr15 小时前
2601C++,cmake与导入
c++
fqbqrr16 小时前
2601C++,编写自己模块
c++
Drawing stars16 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
崇山峻岭之间16 小时前
Matlab学习记录33
开发语言·学习·matlab
玄〤17 小时前
黑马点评中 VoucherOrderServiceImpl 实现类中的一人一单实现解析(单机部署)
java·数据库·redis·笔记·后端·mybatis·springboot
科技林总17 小时前
【系统分析师】3.5 多处理机系统
学习
芯思路18 小时前
STM32开发学习笔记之三【按键】
笔记·stm32·学习
Lips61118 小时前
2026.1.11力扣刷题笔记
笔记·算法·leetcode
charlie11451419119 小时前
从 0 开始的机器学习——NumPy 线性代数部分
开发语言·人工智能·学习·线性代数·算法·机器学习·numpy
王老师青少年编程19 小时前
2025年12月GESP真题及题解(C++七级): 城市规划
c++·gesp·csp·信奥赛·七级·csp-s·提高组