[UE4]利便性を上げるちょっとしたエンジン改造

- 本記事に関して
    - 確認バージョン: UE4.23.1
    -  エンジン改造のみ

ちょっとだけ何か弄りたくなった所を纏めました。

# ツールバーを整理する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/be374ad2-e650-473b-76b5-91077b27b97f.png)

## コンテンツブラウザ、マーケットボタンを消す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/d95e2c1d-f528-b95b-c8c2-db2393c241f0.png)
コンテンツブラウザはメニューバーから出せます。
マーケットプレイスはそうエディターから頻繁に行かない……
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/78f4fbbf-ffcc-4820-c3de-79ea5c1a6b44.png)


**After**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/05f45c1a-5a3e-a870-e8d6-202e0913cf86.png)
使用頻度の低いアイコンを消しました。

- **LevelEditorToolBar.cpp 1255行辺り : FLevelEditorToolBar::MakeLevelEditorToolBar内**
    - ツールバーに追加している箇所をビルドから除外します。
 
<details><summary>サンプルコード</summary><div>

```c++:LevelEditorToolBar.cpp
	ToolbarBuilder.BeginSection("Content");
#if 0 // [com04] 消す
	{
		ToolbarBuilder.AddToolBarButton( FLevelEditorCommands::Get().OpenContentBrowser, NAME_None, LOCTEXT( "ContentBrowser_Override", "Content" ), TAttribute<FText>(), TAttribute<FSlateIcon>(), "LevelToolbarContent" );
		if (FLauncherPlatformModule::Get()->CanOpenLauncher(true)) 
		{
			ToolbarBuilder.AddToolBarButton(FLevelEditorCommands::Get().OpenMarketplace, NAME_None, LOCTEXT("Marketplace_Override", "Marketplace"), TAttribute<FText>(), TAttribute<FSlateIcon>(), "LevelToolbarMarketplace");
		}
	}
#endif // [com04]
	ToolbarBuilder.EndSection();
```
</div></details>


## ビルド、コンパイルボタンを１クリックで動作させなくする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/3bc9b99d-c83d-34e2-0b44-82c8c9b08240.png)
「プレイ」ボタン押そうとして誤クリックでライティングビルドが始まったり、C++コンパイルが走ったりした人も多いはず……！
とはいえ使うボタンですので今回は消すのではなく、１クリックでは動作しないようにして誤クリックを防止します。

**After**
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Customize to prevent mis-clicking on &quot;Build&quot;. <a href="https://twitter.com/hashtag/UE4?src=hash&amp;ref_src=twsrc%5Etfw">#UE4</a> <a href="https://t.co/JH8dsRmG5Y">pic.twitter.com/JH8dsRmG5Y</a></p>&mdash; com04 (@com04) <a href="https://twitter.com/com04/status/1209549513276264448?ref_src=twsrc%5Etfw">December 24, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
１クリックでビルド/コンパイルが動作しないようになりました。
ビルド/コンパイルはサブメニューに移動しました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/afe67929-50c7-a6ab-cf20-4eb3e34dde66.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/92335120-b5d1-c667-77a4-5a918898f9f7.png)


### ビルドボタン

- **LevelEditorToolBar.cpp 1440行辺り : FLevelEditorToolBar::MakeLevelEditorToolBar内**
    - まずは「ビルド」ボタンを削除。右横の「▼」をボタン付きメニューとして扱います

<details><summary>コード</summary><div>

```c++:LevelEditorToolBar.cpp
	ToolbarBuilder.BeginSection("Compile");
	{
#if 1 // [com04]
		// Build menu drop down
		ToolbarBuilder.AddComboButton(
			FUIAction(),
			FOnGetContent::CreateStatic(&FLevelEditorToolBar::GenerateBuildMenuContent, InCommandList),
			LOCTEXT("BuildAll", "Build"),
			LOCTEXT("BuildAll", "Build"),
			FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Build"),
			false);
#else
		// Build			
		ToolbarBuilder.AddToolBarButton( FLevelEditorCommands::Get().Build, NAME_None, LOCTEXT("BuildAll", "Build") );

		// Build menu drop down
		ToolbarBuilder.AddComboButton(
			FUIAction(),
			FOnGetContent::CreateStatic( &FLevelEditorToolBar::GenerateBuildMenuContent, InCommandList ),
			LOCTEXT( "BuildCombo_Label", "Build Options" ),
			LOCTEXT( "BuildComboToolTip", "Build options menu" ),
			FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Build"),
			true);
#endif // [com04]
```
</div></details>

- **LevelEditorToolBar.cpp  1730行辺り FLevelEditorToolBar::GenerateBuildMenuContent内** 
    - このままだとビルドボタンが消えたままなので、クリック後に出てくるメニューに追加します

<details><summary>コード</summary><div>

```c++:LevelEditorToolBar.cpp
	// [com04] ----
	MenuBuilder.AddMenuEntry(FLevelEditorCommands::Get().Build, NAME_None, LOCTEXT("BuildAll", "Build"));
	// ---- [com04]

	MenuBuilder.BeginSection("LevelEditorLighting", LOCTEXT( "LightingHeading", "Lighting" ) );
	{
```
</div></details>

### コンパイルボタン

- **LevelEditorToolBar.cpp 1454行辺り : FLevelEditorToolBar::MakeLevelEditorToolBar内**
    - 「コンパイル」ボタンを削除。右側のオプション「▼」をボタン付きメニューとして表示します

<details><summary>サンプルコード</summary><div>

```c++:LevelEditorToolBar.cpp
		if ( FSourceCodeNavigation::IsCompilerAvailable() )
		{
#if 1 // [com04]
			ToolbarBuilder.AddComboButton(
				FUIAction(
					FExecuteAction(),
					FCanExecuteAction(),
					FIsActionChecked(),
					FIsActionButtonVisible::CreateStatic(FLevelEditorActionCallbacks::CanShowSourceCodeActions)),
				FOnGetContent::CreateStatic(&FLevelEditorToolBar::GenerateCompileMenuContent, InCommandList),
				LOCTEXT( "CompileMenuButton", "Compile" ),
				LOCTEXT( "CompileMenuButton", "Compile" ),
				FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Recompile"),
				false
			);
#else
			// Since we can always add new code to the project, only hide these buttons if we haven't done so yet
			ToolbarBuilder.AddToolBarButton(
				FUIAction(
					FExecuteAction::CreateStatic(&FLevelEditorActionCallbacks::RecompileGameCode_Clicked),
					FCanExecuteAction::CreateStatic(&FLevelEditorActionCallbacks::Recompile_CanExecute),
					FIsActionChecked(),
					FIsActionButtonVisible::CreateStatic(FLevelEditorActionCallbacks::CanShowSourceCodeActions)),
				NAME_None,
				LOCTEXT( "CompileMenuButton", "Compile" ),
				FLevelEditorCommands::Get().RecompileGameCode->GetDescription(),
				FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Recompile")
			);

#if WITH_LIVE_CODING
			ToolbarBuilder.AddComboButton(
				FUIAction(
					FExecuteAction(),
					FCanExecuteAction(),
					FIsActionChecked(),
					FIsActionButtonVisible::CreateStatic(FLevelEditorActionCallbacks::CanShowSourceCodeActions)), 
				FOnGetContent::CreateStatic( &FLevelEditorToolBar::GenerateCompileMenuContent, InCommandList ),
				LOCTEXT( "CompileCombo_Label", "Compile Options" ),
				LOCTEXT( "CompileComboToolTip", "Compile options menu" ),
				FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.Recompile"),
				true
				);
#endif
#endif // [com04]
```

</div></details>


- **LevelEditorToolBar.cpp 1900行辺り : FLevelEditorToolBar::GenerateCompileMenuContent辺り**
    - 消えた「コンパイル」ボタンをメニュー内に追加します

<details><summary>サンプルコード</summary><div>

```c++:LevelEditorToolBar.cpp
// #if WITH_LIVE_CODING // [com04] コメント化
TSharedRef< SWidget > FLevelEditorToolBar::GenerateCompileMenuContent( TSharedRef<FUICommandList> InCommandList )
{
#define LOCTEXT_NAMESPACE "LevelToolBarCompileMenu"

	const bool bShouldCloseWindowAfterMenuSelection = true;
	FMenuBuilder MenuBuilder( bShouldCloseWindowAfterMenuSelection, InCommandList );

	MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().RecompileGameCode );  // [com04] コンパイルボタン追加
	
#if WITH_LIVE_CODING // [com04]
	MenuBuilder.BeginSection("LiveCodingMode", LOCTEXT( "LiveCodingMode", "General" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_Enable );
	}
	MenuBuilder.EndSection();

	MenuBuilder.BeginSection("LiveCodingActions", LOCTEXT( "LiveCodingActions", "Actions" ) );
	{
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_StartSession );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_ShowConsole );
		MenuBuilder.AddMenuEntry( FLevelEditorCommands::Get().LiveCoding_Settings );
	}
	MenuBuilder.EndSection();
#endif // [com04]

	return MenuBuilder.MakeWidget();

#undef LOCTEXT_NAMESPACE
}
// #endif // [com04] コメント化
```

</div></details>

- **LevelEditorToolBar.h 36行辺り : FLevelEditorToolBar  GenerateCompileMenuContent辺り**
    - ヘッダのコメントアウトも解除しておきます

<details><summary>サンプルコード</summary><div>

```c++:LevelEditorToolBar.h
// #if WITH_LIVE_CODING // [com04]
	/**
	 * Generates menu content for the compile combo button drop down menu
	 *
	 * @return	Menu content widget
	 */
	static TSharedRef< SWidget > GenerateCompileMenuContent( TSharedRef<FUICommandList> InCommandList );
// #endif // [com04]
```

</div></details>


# Viewportのツールバーに機能を追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/a645e8d8-cfa7-4050-8d1d-fa83b982a7dd.png)
エディター上のViewport、割と使いやすいです。
この左上の「▼」にプロジェクト固有の処理を追加します。内部的には普通にslateを追加する感じです。


- **SLevelViewportToolBar.h 194行辺り : SLevelViewportToolBar  OnScreenPercentageValueChanged辺り**
    - 登録するウィジェット（今回はSSpinBox）生成関数と、そのウィジェットのイベント周りの関数を宣言します

<details><summary>サンプルコード</summary><div>

```c++:LevelEditorToolBar.h
	void OnScreenPercentageValueChanged(int32 NewValue);

// [com04] ----
	/* 追加するウィジェット作る */
	TSharedRef<SWidget> GenerateProjectParamMenu() const;
	/* パラメーター数値 */
	int32 OnGetProjectParamValue() const;
	/* パラメーターの数値が変わったイベント */
	void OnProjectParamValueChanged(int32 NewValue);
	int32 TestValue = 0;
// ---- [com04]
```

</div></details>

- **SLevelViewportToolBar.cpp 1140行辺り : SLevelViewportToolBar::OnScreenPercentageValueChanged辺り**
    - 宣言した関数を実装します。SSpinBox生成とそのイベントハンドリングです。

<details><summary>サンプルコード</summary><div>

```c++:LevelEditorToolBar.cpp
// [com04] ----
/* 追加するウィジェット作る */
TSharedRef<SWidget> SLevelViewportToolBar::GenerateProjectParamMenu() const
{
	return
		SNew(SBox)
		.HAlign(HAlign_Right)
		[
			SNew(SBox)
			.Padding(FMargin(4.0f, 0.0f, 0.0f, 0.0f))
		.WidthOverride(100.0f)
		[
			SNew(SSpinBox<int32>)
			.Font(FEditorStyle::GetFontStyle(TEXT("MenuItem.Font")))
		.MinValue(0)
		.MaxValue(100)
		.Value(this, &SLevelViewportToolBar::OnGetProjectParamValue)
		.OnValueChanged(const_cast<SLevelViewportToolBar*>(this), &SLevelViewportToolBar::OnProjectParamValueChanged)
		]
		];
}
/* パラメーター数値 */
int32 SLevelViewportToolBar::OnGetProjectParamValue() const
{
	// 今の値を返す
	return TestValue;
}
/* パラメーターの数値が変わったイベント */
void SLevelViewportToolBar::OnProjectParamValueChanged(int32 NewValue)
{
	// プロジェクトの何かする
	TestValue = NewValue;
}
// ---- [com04]

TSharedRef<SWidget> SLevelViewportToolBar::GenerateFarViewPlaneMenu() const
```

</div></details>

- **SLevelViewportToolBar.cpp 555行辺り : SLevelViewportToolBar::GenerateOptionsMenu辺り**
    - メニューを構築している所に差し込みます

<details><summary>サンプルコード</summary><div>

```c++:LevelEditorToolBar.cpp
			OptionsMenuBuilder.AddWidget(GenerateScreenPercentageMenu(), LOCTEXT("ScreenPercentage", "Screen Percentage"));
// [com04] ----
			OptionsMenuBuilder.AddWidget(GenerateProjectParamMenu(), LOCTEXT("ProjectParam", "project param ya"));
// ---- [com04]
		}
		OptionsMenuBuilder.EndSection();
```

</div></details>

# マテリアルエディターのStat欄に使用しているMaterial Parameter Collectionを出力する
マテリアルでは[Material Parameter Collection](https://docs.unrealengine.com/en-US/Engine/Rendering/Materials/ParameterCollections/index.html)を最大2つまで使用できます
3つ以上になるとエラーが出ます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/d34b1ce6-55db-c2f4-8ae4-2a994291e926.png)

しかし、今現在、何個使っているのか、何の[Material Parameter Collection](https://docs.unrealengine.com/en-US/Engine/Rendering/Materials/ParameterCollections/index.html)を使っているのかが分かりません。

という事で出力してみました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/d4ab276e-4479-a645-9def-2c2b48372802.png)

エラー時もどれ使ってるのか分かるように
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58518/12f058d6-8983-0415-760b-e7f11b79cbb8.png)


- **MaterialEditor.cpp 2144行辺り : FMaterialEditor::UpdateMaterialinfoList_Old内**
    - Statの結果を出している所に差し込みます

<details><summary>サンプルコード</summary><div>

```c++:MaterialEditor.cpp
			}

			// [com04] ----
			if (MaterialForStats)
			{
				FString CollectionString;

				/* マテリアルに入っているMPCのリスト */
				for (const auto& CollectionInfo : MaterialForStats->MaterialParameterCollectionInfos)
				{
					if (CompileErrors.Num())
					{
						/* エラー出てる時は全部出力する */
					}
					else
					{
						bool Used = false;
						/* コンパイル時に使用されているMPCのリスト */
						FMaterialRenderProxy* RenderProxy = MaterialForStats->GetRenderProxy();
						if (RenderProxy)
						{
							for (const auto& Collection : RenderProxy->UniformExpressionCache[GMaxRHIFeatureLevel].ParameterCollections)
							{
								if (Collection == CollectionInfo.StateId)
								{
									Used = true;
									break;
								}
							}
						}
						/* このMPC使ってない */
						if (Used == false)
						{
							continue;
						}
					}
					const UMaterialParameterCollection* Collection = CollectionInfo.ParameterCollection;
					if (Collection)
					{
						if (CollectionString.IsEmpty())
						{
							CollectionString = Collection->GetName();
						}
						else
						{
							CollectionString += FString::Printf(TEXT(", %s"), *Collection->GetName());
						}
					}
				}
				if (!CollectionString.IsEmpty())
				{
					FString CollectionMessage(FString::Printf(TEXT("Collectins: %s"), *CollectionString));
					TempMaterialInfoList.Add(MakeShareable(new FMaterialInfo(CollectionMessage, FLinearColor::Yellow)));
					TSharedRef<FTokenizedMessage> Line = FTokenizedMessage::Create(EMessageSeverity::Info);
					Line->AddToken(FTextToken::Create(FText::FromString(CollectionMessage)));
					Messages.Add(Line);
				}
			}
			// ---- [com04]
			

			FString FeatureLevelName;
			GetFeatureLevelName(FeatureLevel,FeatureLevelName);
			for(int32 ErrorIndex = 0; ErrorIndex < CompileErrors.Num(); ErrorIndex++)
```

</div></details>