BlueRose
文章97
标签28
分类7
Ue4Config方法总结与另外一种GameplayTag管理方法

Ue4Config方法总结与另外一种GameplayTag管理方法

前言

最近在尝试对GameplayAbility进行插件化处理。这么做好处在于这样可以方便后续项目的迭代。UE4官方频道有一个“盗贼之海”团队的经验分享视频,他们说制作的GAS Asset复用率能都达到90%,这可是相当惊人的程度。而且在不断地项目迭代过程中,使用GAS制作的技能、Buff/Debuff会越来越充分。从而加快后续项目的开发速度。所以将GAS插件化是很有必要的。

但使用GAS需要给项目添加一些项目设置比如:AssetManager中的DataAsset、指定AssetManager类、添加GameplayTag。在发现网上的添加配置方法无效后,我又花了一些时间研究,终于找到方法,隧有了此文。

Ue4配置设置方法

Ue4的配置设置方法大致为二:通过GConfig设置与直接设置对应UObject变量。
具体的可以参考Ue4的Wiki与一篇Csdn的文章:

https://www.ue4community.wiki/legacy/config-files-read-and-write-to-config-files-zuoaht01
https://blog.csdn.net/u012999985/article/details/52801264

通过GConfig进行修改

通过全局对象GConfig,调用GetInt/Float/Rotator/String/Array等函数获取对应的数值;调用SetInt/Float/Rotator/String/Array等函数设置数值。最终需要调用Flush函数将结果写入对应的ini文件中,不然重启后数据会丢失。

GConfig通过一个Map来存储配置,这个你可以通过断点来查看内部数据。

这里我简单补充一些:

  • Section:属性所处于的区块。
  • Key:属性的变量名。
  • Value:对应类型的属性值。
  • Filename:存储ini文件的位置。Ue4提供了默认位置的字符串变量,具体的请下文。

据我所知Section可以通过将对应属性通过编辑器修改,找到对应的ini文件,即可从所在段落的第一行找到所在的Section。如果是自己写的类,Section即为类的资源路径。可通过在ContentBrowser中对指定类执行复制命令,之后在外部文本编辑器中粘贴即可得到。Key值就需要用VS或者VSCode之类的编辑器查询了。

请注意,使用以下代码,相关的配置的是保存在Saved——对应平台文件夹中的。

下面是Wiki上Rama大神的代码:

//in your player controller class
void AVictoryController::VictoryConfigGetTests()
{
    //Basic Syntax
    /*
    bool GetString( 
        const TCHAR* Section, 
        const TCHAR* Key, 
        FString& Value, 
        const FString& Filename 
    );
    */

    if(!GConfig) return;
    //~~

    //Retrieve Default Game Type
    FString ValueReceived;
    GConfig->GetString(
        TEXT("/Script/Engine.WorldInfo"),
        TEXT("GlobalDefaultGameType"),
        ValueReceived,
        GGameIni
    );

    ClientMessage("GlobalDefaultGameType");
    ClientMessage(ValueReceived);

        //Retrieve Max Objects not considered by GC
    int32 IntValueReceived = 0;
    GConfig->GetInt(
        TEXT("Core.System"),
        TEXT("MaxObjectsNotConsideredByGC"),
        IntValueReceived,
        GEngineIni
    );

    ClientMessage("MaxObjectsNotConsideredByGC");
    ClientMessage(FString::FromInt(IntValueReceived));

         //Retrieve Near Clip Plane (how close things can get to camera)
    float floatValueReceived = 0;
    GConfig->GetFloat(
        TEXT("/Script/Engine.Engine"),
        TEXT("NearClipPlane"),
        floatValueReceived,
        GEngineIni
    );

    ClientMessage("NearClipPlane");
    ClientMessage(FString::SanitizeFloat(floatValueReceived));
}
//write to existing Game.ini
//the results get stored in YourGameDir\Saved\Config\Windows
void AVictoryController::VictoryConfigSetTests()
{
    if(!GConfig) return;
    //~~

    //New Section to Add
    FString VictorySection = "Victory.Core";

    //String
    GConfig->SetString (
        *VictorySection,
        TEXT("RootDir"),
        TEXT("E:\UE4\IsAwesome"),
        GGameIni
    );

    //FColor
    GConfig->SetColor (
        *VictorySection,
        TEXT("Red"),
        FColor(255,0,0,255),
        GGameIni
    );

    //FVector
    GConfig->SetVector (
        *VictorySection,
        TEXT("PlayerStartLocation"),
        FVector(0,0,512),
        GGameIni
    );

    //FRotator
    GConfig->SetRotator (
        *VictorySection,
        TEXT("SunRotation"),
        FRotator(-90,0,0),
        GGameIni
    );

    //ConfigCacheIni.h
    //void Flush( bool Read, const FString& Filename=TEXT("") );
    GConfig->Flush(false,GGameIni);
}

ini配置文件的位置字符串变量,代码位于CoreGlobals.cpp中。

FString                GEngineIni;                                                    /* Engine ini filename */

/** Editor ini file locations - stored per engine version (shared across all projects). Migrated between versions on first run. */
FString                GEditorIni;                                                    /* Editor ini filename */
FString                GEditorKeyBindingsIni;                                        /* Editor Key Bindings ini file */
FString                GEditorLayoutIni;                                            /* Editor UI Layout ini filename */
FString                GEditorSettingsIni;                                            /* Editor Settings ini filename */

/** Editor per-project ini files - stored per project. */
FString                GEditorPerProjectIni;                                        /* Editor User Settings ini filename */

FString                GCompatIni;
FString                GLightmassIni;                                                /* Lightmass settings ini filename */
FString                GScalabilityIni;                                            /* Scalability settings ini filename */
FString                GHardwareIni;                                                /* Hardware ini filename */
FString                GInputIni;                                                    /* Input ini filename */
FString                GGameIni;                                                    /* Game ini filename */
FString                GGameUserSettingsIni;                                        /* User Game Settings ini filename */
FString                GRuntimeOptionsIni;                                            /* Runtime Options ini filename */
FString                GInstallBundleIni;                                            /* Install Bundle ini filename*/
FString                GDeviceProfilesIni;

通过UObject进行修改

很遗憾,本人没有从GConfig找到对应的设置变量,所以添加配置的操作都是通过UObject进行的。主要是通过GEngine这个全局对象以及GetMutableDefault()获取对应的UObject,这两种方式进行操作。

操作的方式也有两种:

  1. 直接调用LoadConfig再入预先设置的ini文件,之后调用SaveConfig保存设置。
  2. 直接修改UObject的属性变量值,之后调用SaveConfig保存设置。

另外需要注意两点:

  1. Config的类型分为Config与GlobalConfig,调用函数时需要使用对应的类型枚举:CPF_Config与CPF_GlobalConfig。
  2. 是否在UCLASS(Config=XXX)有设置Config保存位置。

具体操作请见下文代码。

使用DataTable来管理GameplayTag

本人在尝试通过修改配置来添加GameplayTag时,发现这个方法是无效的。(可能是个bug)在404的过程中,我发现可以通过DataTable来管理Tag。大致的方式是:

  1. 构建CSV或者JSON文件
  2. 导入UE4中并将DataTable类型选择为GameplayTagTableRow。
  3. 在ProjectSettings-GameplayTagTableList中添加DataTable即可。

这样做的好处有:

  1. GameplayTags的重复利用,避免手动输入出错,方便下个项目移植。
  2. 可以对不同类型Tag进行分表处理,在做项目时只需要加载需要的DataTable即可。

DataTable大致格式如下:

PS.有关GameplayTag的操作可以查看UGameplayTagsSettings、UGameplayTagsManager与GameplayTagEditorModule中的代码。

创建Console命令

将这些代码放在GameMode、GameInstance里会降低性能(纯属强迫症),放在模块cpp文件中又会报错(估计需要将插件设置成后启动模式才行)。为了方便使用,我创建了一个Console命令,并将其放在BlueprintFunctionLibrary中。在编辑器中下入~,输入AddGASSettings并按下回车,即可添加所需设置。

具体代码实现

static void AddRPGGameplayAbilityProjectSettings(UWorld *InWorld)
{
    UE_LOG(LogRPGGameplayAbility, Warning, TEXT("Add RPGGameplayAbility Project Settings!"));

    //Special AssetManager
    FString ConfigPath = FPaths::ProjectConfigDir();
    GEngine->AssetManagerClassName = FString("/Script/RPGGameplayAbility.RPGAssetManager");
    GEngine->SaveConfig(CPF_GlobalConfig, *(ConfigPath + FString("DefaultEngine.ini")));

    //Add DataAsset Config
    UAssetManagerSettings *AssetmanagerSettings = GetMutableDefault<UAssetManagerSettings>();
    FString PluginConfigPath = FPaths::ProjectPluginsDir() + FString("RPGGameplayAbility/Config/");
    AssetmanagerSettings->LoadConfig(nullptr, *FString(PluginConfigPath + FString("DefaultGame.ini")));
    AssetmanagerSettings->SaveConfig(CPF_Config, *(ConfigPath + FString("DefaultGame.ini")));

    //Add GameplayTag
    UGameplayTagsSettings *GameplayTagsSettings = GetMutableDefault<UGameplayTagsSettings>();
    //GameplayTagsSettings->LoadConfig(nullptr, *FString(PluginConfigPath + FString("DefaultGameplayTags.ini")));
    FString TagDataTable{FString("/RPGGameplayAbility/Abilities/DataTables/GameplayTags.GameplayTags")};

    if (GameplayTagsSettings->GameplayTagTableList.Find(TagDataTable) == -1)
    {
        GameplayTagsSettings->GameplayTagTableList.Add(TagDataTable);
        UGameplayTagsManager &tagManager = UGameplayTagsManager::Get();
        tagManager.DestroyGameplayTagTree();
        tagManager.LoadGameplayTagTables(false);
        tagManager.ConstructGameplayTagTree();
        tagManager.OnEditorRefreshGameplayTagTree.Broadcast();

        GameplayTagsSettings->SaveConfig(CPF_Config, *(ConfigPath + FString("DefaultGameplayTags.ini")));
    }
}

FAutoConsoleCommandWithWorld AbilitySystemDebugNextCategoryCmd(
    TEXT("AddGASSettings"),
    TEXT("增加RPGGameplayAbility所需要的项目设置!"),
    FConsoleCommandWithWorldDelegate::CreateStatic(AddRPGGameplayAbilityProjectSettings));