Published on

UE4添加自定义项目设置

Authors
  • avatar
    Name
    东哥
    Twitter

前言

我们在项目开发的过程中,经常需要在项目设置里面添加一些参数,本文介绍如何添加这些参数

另外介绍一些特殊的资源类参数的添加和使用

先上图

image-20200831114210083

添加自定义项目设置

一般情况下,我们需要创建一个插件来添加这类属性,我们创建一个蓝图函数库模板的插件

然后创建一个UObject类来添加属性

UCLASS(config = ExtensionSetting,defaultconfig)
class GAMESETTINGEXTENSION_API USettingObj : public UObject
{
	GENERATED_BODY()
public:

	UPROPERTY(config, BlueprintReadOnly, EditAnywhere)
		FSoftClassPath SoftClassPath;
	UPROPERTY(config, BlueprintReadOnly, EditAnywhere)
		FSoftObjectPath SoftObjectPath;
	UPROPERTY(config, BlueprintReadOnly, EditAnywhere)
		`TSoftObjectPtr<AActor>` SoftObjectPtr;
	UPROPERTY(config, BlueprintReadOnly, EditAnywhere)
		`TSoftClassPtr<AActor>` SoftClassPtr;
};

configdonotcheckdefaultsdefaultconfig

  • defaultconfig:配置会写入到引擎默认配置里, 路径一般是./Config内而不是./Save/*/Config内.另外打包出来后运行以后会自动生成配置文件信息,但是无法再手动修改ini文件; 可以运行时修改但是会保存到./Save/*/Windows/Config内对应名称的ini
  • configdonotcheckdefaults :会写入到./Save内, 运行运行时的修改;打包以后不会自动生成ini文件, 可以手动复制到对应目录

  • CLASS(config=FileName):表示这个类默认条件下将配置信息保存到哪个配置文件,config后面的文件名可以是任意字符串。
  • UPROPERTY(globalconfig):不指定Section的情况下,标记config的这个属性在保存到配置文件里面的时候会保存在基类对应的Section部分。同理,加载的时候也会从基类对应的Section下加载。

注意要在UPROPERTY后加config关键字

一般的值类型变量都都可以配置,如FString,FRotator,TSubclassOf,但是不能添加继承于UObject的类,可以用FSlot***代替

然后我们需要将此类注册到游戏设置里面,一般是在模块启动的时候添加,看下面代码

void FGameSettingExtensionModule::RegisterSetting()
{
	if (ISettingsModule* SettingModule = FModuleManager::`GetModulePtr<ISettingsModule>`("Settings"))
	{
		SettingModule->RegisterSettings("Project", "Game", "ExtensionSetting",
			LOCTEXT("RuntimeSettingsName", "GameSetting"),
			LOCTEXT("RuntimeSettingsDescription", "Configure my GameSetting"),
			`GetMutableDefault<USettingObj>`()
		);
	}
}

void FGameSettingExtensionModule::UnregisterSetting()
{
	if (ISettingsModule* SettingsModule = FModuleManager::`GetModulePtr<ISettingsModule>`("Settings"))
	{
		SettingsModule->UnregisterSettings("Project", "Game", "ExtensionSetting");
	}
}

上面代码在模块启动和注销的时候分别调用

  • "Game":表示分类,对应的就是项目设置的Game标签
  • "ExtensionSetting":配置时的分类名称
  • "Configure my GameSetting":上图中的文字描述

使用自定义设置

因为我们本身就是一个蓝图函数库插件,我们直接在函数库类里面添加新的方法,如果不是此类插件,自己新建一个蓝图函数库类

需要注意的问题,编辑器类型的插件不能使用蓝图函数库

UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GameSettingExtension")
		static UObject* GetObjBySoftPath(const FSoftObjectPath& softPath);
	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GameSettingExtension")
		static UObject* GetObjRefBySoftPtr(const `TSoftObjectPtr<UObject>`& softPathPtr);

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GameSettingExtension")
		static UClass* GetClassBySoftPath(const FSoftClassPath& softPath);

	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GameSettingExtension")
		static UClass* GetClassByClassSoftPtr(const `TSoftClassPtr<UObject>`& softPathPtr);

我们申明4个变量对应的4个函数,因为例如FSoftObjectPath类型的变量在蓝图里面不方便使用,所以需要我们用C++ 封装一层

UObject* UGameSettingExtensionBPLibrary::GetObjBySoftPath(const FSoftObjectPath& softPath)
{
	UObject* obj = `LoadObject<UObject>`(NULL, *softPath.ToString());
	return obj;
}

UObject* UGameSettingExtensionBPLibrary::GetObjRefBySoftPtr(const `TSoftObjectPtr<UObject>`& softPathPtr)
{
	`TSoftObjectPtr<UObject>` p = `TSoftObjectPtr<UObject>`(softPathPtr);
	return p.Get();
}

UClass* UGameSettingExtensionBPLibrary::GetClassBySoftPath(const FSoftClassPath& softPath)
{
	UClass* c = `LoadClass<UObject>`(NULL, *softPath.ToString());
	return c;
}

UClass* UGameSettingExtensionBPLibrary::GetClassByClassSoftPtr(const `TSoftClassPtr<UObject>`& softPathPtr)
{
	`TSoftClassPtr<UObject>` p = `TSoftClassPtr<UObject>`(softPathPtr);
	return  p.Get();

}

image-20200831115229327

这样我们就实现了蓝图里面调用项目设置内的属性了

读写

只要继承自UObject类就有两个方法SaveConfig()LoadConfig()

	void SaveConfig( uint64 Flags=CPF_Config, const TCHAR* Filename=NULL, FConfigCacheIni* Config=GConfig, bool bAllowCopyToDefaultObject=true );

第一个参数Flags: 一般不用改变, 可以设置为CPF_GlobalConfig标记为全局

第二个参数Filename:保存的文件, 如果不填写则使用前面UCLASS定义的路径

第三个参数保持默认即可

	void LoadConfig( UClass* ConfigClass=NULL, const TCHAR* Filename=NULL, uint32 PropagationFlags=UE4::LCPF_None, class FProperty* PropertyToLoad=NULL );

LoadConfig()的功能是从配置文件里面读取特定的属性值并赋给指定类的对应属性。第一个参数指定了要把值赋给哪个类的属性,第二个参数表示到哪个配置文件找对应的配置信息,第三个参数表示是哪种标记(Config还是GlobalConfig),第四个参数表示指定要赋值的属性。

如果不指定第4个参数,就会读取配置并给所有标记UProperty(Config)的属性赋值。

可以在注册的位置就绑定Setting类的修改, 然后实时保存到ini文件内

if (ISettingsModule* SettingModule = FModuleManager::`GetModulePtr<ISettingsModule>`("Settings"))
	{
		ISettingsSectionPtr SettingsSection = SettingModule->RegisterSettings("Project", "Game", "SuperRoad",
			LOCTEXT("RuntimeSettingsName", "SuperRoad"),
			LOCTEXT("RuntimeSettingsDescription", "Configure my GameSetting"),
			`GetMutableDefault<USRDefaultSettings>`());
    if (SettingsSection.IsValid())
		{
			SettingsSection->OnModified().BindRaw(this, &FSuperRoadModule::HandleSettingsSaved);
		}
}
bool FSuperRoadModule::HandleSettingsSaved()
{
	USRDefaultSettings* s = `GetMutableDefault<USRDefaultSettings>`();
	if (s)
	{
		s->SaveConfig();
		SRLOG(TEXT("Save Settings"));
		return true;
	}
	return false;
}

当然, 运行时直接拿到对象修改变量是无法调用到这里的, 需要自己调用或者封装Save函数


还有一个方式也可以读写变量, 使用GConfig

GConfig可以理解为是一个全局的变量(类型是FConfigIni), 所有配置文件都可以通过此来获取和设置变量

以下是Wiki的案例

//in your player controller class
void AVictoryController::VictoryConfigGetTests()
{
    //Basic Syntax
    /*
    bool GetString( 
        const TCHAR* Section, 
        const TCHAR* Key, 
        FString&amp; Value, 
        const FString&amp; 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&amp; Filename=TEXT("") );
    GConfig->Flush(false,GGameIni);
}

注意: 写入操作以后需要调用GConfig->Flush(false,GGameIni);

否则配置文件不会修改

其他可配置变量

FSoftObjectPath

软对象路径,可以直接转换成FString然后从路径得到具体的类型

UObject* obj = `LoadObject<UObject>`(NULL, *softPath.ToString());

用meta参数类刷选特定类

meta = (AllowedClasses ="Material,StaticMesh")

FSoftClassPath

同上,得到一个蓝图类的UClass

UClass* c = `LoadClass<UObject>`(NULL, *softPath.ToString());

可以用meta参数来刷选特定类,与FSoftObjectPath的meta参数不一样

meta = (MetaClass = "MyActor")

TSoftObjectPtr

TSoftObjectPtr是封装了FSoftObjectPtr的模板,同样是用于“在给予文件路径下检测一个资源是否已经加载进了内存,获取资源对应的对象指针”,类似上面的一种写法, 直接帮我们省掉了“Cast”转为对象的过程

`TSoftObjectPtr<UObject>` p = `TSoftObjectPtr<UObject>`(softPathPtr);
return p.Get();//UObject*

实测发现该变量指向的是场景中的具体对象,与FSoftObjectPath指向不同

TSoftClassPtr

跟TSoftObjectPtr类似,只不过TSoftClassPtr是仅仅用于UClass*

`TSoftClassPtr<UObject>` p = `TSoftClassPtr<UObject>`(softPathPtr);
return  p.Get();

参考资料

WiKi:Legacy/Config Files, Read & Write to Config Files

Wiki:Legacy/CustomSettings

CSDN:UE4 Config配置文件详解