UnrealEngine4で自分のゲーム専用のProjectSettingsを追加する

# 前提
- UnrealEngine 4.24.1 (Launcher engine)
- VisualStudio 2019
    - C++プロジェクトのビルドができる環境なら何でも良い
- ある程度のUnrealC++についての知識
    - UPROPERTYとはなんぞや？レベルの解説はしません

# 前フリ
AnswerHubで話題が出たのでサンプルを作りました
最低限動くものを目指したので、具体的な部分は各自で読み替えていただきたく

UE4.24.1で検証していますが、この内容自体はもっと前のバージョンでも適用可能です


# 作り方
まずC++プロジェクトを作成しておきましょう
テンプレートは何でも良いです
例では、ThirdPersonTemplate（StarterContentsあり）を使用します

## 設定用のClassを作る
新規C++クラスから、UObject継承クラスを1つ作成しましょう
`MyGameSettings`とでもしておきます

VisualStudioに移ったら、以下のように書いておきましょう
Propertyに関しては、お好きなものをチョイスしてください
今回はActorのClassReferenceを使います

また、それぞれのUPROPERTYに`config`と書き加えておきましょう

```MyGameSettings.h

#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "MyGameSettings.generated.h"

UCLASS(config=Game, defaultconfig)
class CPP424_API UMyGameSettings : public UObject
{
	GENERATED_BODY()

public:

	UMyGameSettings(const FObjectInitializer& ObjectInitializer);

	UPROPERTY(EditAnywhere, config, Category="MyGameSettings", meta = (AllowedClasses = "Actor"))
	TSubclassOf< class AActor > DefaultSpawnActor;
	
};

```

## 設定をProjectSettingsで編集できるようにする

ProjectSettingsに項目を追加します
Moduleロード時に登録するようにします

テンプレートからではこのModuleのクラスがないため、ここは手書きで作ることになります
（Plugin開発ではModuleが独立しているのでModuleクラスがあります）

C++のソース内に`[ProjectName].h`があります（自分のProject名で置き換えてね）
これがModuleにあたります
せっかくなのでこれを使いましょう

```[ProjectName].h
#pragma once

#include "CoreMinimal.h"
#include "MyGameSettings.h"

#if WITH_EDITOR
// SettingsモジュールはEditor用のため、Packaging時には参照しないようにしておく
#include "ISettingsModule.h"
#include "ISettingsSection.h"
#include "ISettingsContainer.h"
#endif

#define LOCTEXT_NAMESPACE "FCustomSettingsModule"

class FCustomSettingsModule : public FDefaultGameModuleImpl
{
	// モジュールロード時
	virtual void StartupModule() override
	{
		// 設定を登録する
		RegisterSettings();
	}

	// モジュールアンロード時
	virtual void ShutdownModule() override
	{
		if (UObjectInitialized()) {
			// 設定を登録解除する
			UnregisterSettings();
		}
	}

	virtual bool SupportsDynamicReloading() override
	{
		// DynamicReloadingはON
		return true;
	}

private:
	// 設定を保存する
	bool HandleSettingsSaved()
	{
		UMyGameSettings* Settings = GetMutableDefault<UMyGameSettings>();
		bool ResaveSettings = false;

		if (ResaveSettings)
		{
			Settings->SaveConfig();
		}

		return true;
	}

	// 設定を登録する
	void RegisterSettings()
	{

#if WITH_EDITOR
		// Settingsモジュールを取得
		if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
		{
			// 設定用のカテゴリーを追加する
			ISettingsSectionPtr SettingsSection = SettingsModule->RegisterSettings("Project", "CustomSettings", "General",
				LOCTEXT("RuntimeGeneralSettingsName", "General"),
				LOCTEXT("RuntimeGeneralSettingsDescription", "MyConfiguration"),
				GetMutableDefault<UMyGameSettings>());

			if (SettingsSection.IsValid())
			{
				// 更新時の自動保存処理を登録
				SettingsSection->OnModified().BindRaw(this, &FCustomSettingsModule::HandleSettingsSaved);
			}
		}
#endif
	}

	// 設定を登録解除する
	void UnregisterSettings()
	{
#if WITH_EDITOR
		// Settingsモジュールを取得
		if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings"))
		{
			// カテゴリーごと削除する
			SettingsModule->UnregisterSettings("Project", "CustomSettings", "General");
		}
#endif
	}
};

#undef LOCTEXT_NAMESPACE
```

`[ProjectName].cpp`の方も書き換えます（自分のProject名で置き換えてね）
`FDefaultGameModuleImpl`の部分が、先程書いたクラス名になっていればOKです

```
#include "[ProjectName].h"
#include "Modules/ModuleManager.h"

//IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, [ProjectName], "[ProjectName]");
IMPLEMENT_PRIMARY_GAME_MODULE(FCustomSettingsModule, [ProjectName], "[ProjectName]");

```

ここまで出来たらコンパイルしましょう
コンパイルが完了したら、ProjectSettingsをチェックしてみてください
ProjectSettingsに項目が追加されているのがわかります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117589/ab8f7339-0e66-7014-1e94-f0b12a958f29.png)


## おまけ
せっかくなのでBlueprintで使えるようにしてみましょう
BlueprintFunctionLibraryを追加します

```MySettingsFunctionLibrary.h
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyGameSettings.h"
#include "MySettingsFunctionLibrary.generated.h"

/**
 *
 */
UCLASS()
class CPP424_API UMySettingsFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()


public:
	// デフォルトActorのクラス名を取得する
	UFUNCTION(BlueprintPure, Category = "MyGameSettings")
		static TSubclassOf<class AActor> GetDefaultSpawnActorClass()
	{
		// 設定をロードする
		UMyGameSettings* Settings = GetMutableDefault<UMyGameSettings>();

		return Settings->DefaultSpawnActor;

	}

};

```

こうしておけばProjectSettingsで設定した値が、Blueprint内で使えますね
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117589/95da8ba2-3a95-ab93-3ea7-85ad3cd4b05e.png)




## 注意点
SettingsモジュールはEditor用のモジュールであり、Package時には参照できません
なのでSettingsモジュールを使っている部分は必ず`#if WITH_EDITOR ~ #endif`で囲んでおき、Packaging時に参照されないようにしておきましょう
この点を知らないと、下記のようなエラーが出てくるため、大変混乱することになります

```
fatal error C1083: Cannot open include file: 'ISettingsModule.h': No such file or directory
```

UnrealEngineのC++に慣れてる人はこれでだいたい察せますが、慣れてない人は「なんで？デバッグでは動いてたじゃん」と叫ぶことになるでしょう


# 参考資料
https://wiki.unrealengine.com/CustomSettings