前言
最近在尝试对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
操作的方式也有两种:
- 直接调用LoadConfig再入预先设置的ini文件,之后调用SaveConfig保存设置。
- 直接修改UObject的属性变量值,之后调用SaveConfig保存设置。
另外需要注意两点:
- Config的类型分为Config与GlobalConfig,调用函数时需要使用对应的类型枚举:CPF_Config与CPF_GlobalConfig。
- 是否在UCLASS(Config=XXX)有设置Config保存位置。
具体操作请见下文代码。
使用DataTable来管理GameplayTag
本人在尝试通过修改配置来添加GameplayTag时,发现这个方法是无效的。(可能是个bug)在404的过程中,我发现可以通过DataTable来管理Tag。大致的方式是:
- 构建CSV或者JSON文件
- 导入UE4中并将DataTable类型选择为GameplayTagTableRow。
- 在ProjectSettings-GameplayTagTableList中添加DataTable即可。
这样做的好处有:
- GameplayTags的重复利用,避免手动输入出错,方便下个项目移植。
- 可以对不同类型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));