UnrealEngineのPlugin化のすすめ

# 概要
本記事はUnrealEngine5(以下UE5)のコンテンツのプラグイン化について記載します。
また、個人開発に焦点を絞りますがプロジェクトのソース管理についても少し触れます。

# 対象
UEで実装はしているが、プラグインの作成は初めての方を対象とします。実装は基本的にブループリントですが、仕様上、プラグインの準備として文字ベースの実装も若干扱います。

# 背景
複数のプロジェクトで汎用的に使用する処理を、都度作成していては二度手間となってしまいます。
既存プロジェクトからマイグレートする場合もディレクトリ構成が煩雑になりがちです。
また、依存関係が把握しにくく移植が難しい場合があります。
そうした問題をプラグインとして作成することで、依存関係が明確になり汎用的に使用する処理を再利用しやすくなります。

# 空のプラグインの作成
プラグインのひな型を作るのは簡単です。

メインメニュー＞Edit＞Plugins
![スクリーンショット 2023-11-03 165941.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/f86785dc-0f8e-1114-58a8-a9032e910c05.png)

プラグインダイアログ＞Add
![スクリーンショット 2023-11-03 170024.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/bf79441a-ceeb-5f1e-b49d-c82583dd4dba.png)

プラグイン追加ダイアログ＞Blank
※作成するプラグインは任意ですが、今回は空のプラグインを作成します。
![スクリーンショット 2023-11-03 172856.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/f866f12f-b241-200c-089b-577414972491.png)

以上でプラグインのひな型は作成完了です。
作成したプラグインが一覧に追加され、有効になっていれば成功です
![スクリーンショット 2023-11-03 173356.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/33c9eb7d-c737-683f-7910-0bbc9f46b91f.png)

※新規プラグイン作成時にエラーとなる場合がありますが、おそらくエンジンビルドのキャッシュが邪魔しているためです。
私の場合はプロジェクト内の「Binaries」「Intermediate」ディレクトリを削除してUE5を再起動すれば解消しました。

# プラグインの実装
空のプラグインで作成されたソースの内容を見てみます。
![スクリーンショット 2023-11-03 175249.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/bfe6542d-d69b-73a3-486b-628dd8f97fa0.png)


Plugins＞MyPlugin配下のファイルが今回作成した空のプラグインとなります。

```
Resources
　プラグイン一覧で表示されるアイコン

    Source＞MyPlugin＞Private＞MyPlugin.pp
    　プラグインの実装
     
    Source＞MyPlugin＞Public＞MyPlugin.h
    　プラグインのヘッダ
     
    Source＞MyPlugin＞Public＞MyPlugin.Build.cs
    　プラグインのモジュールルール
     
Source＞MyPlugin.uplugin
    　プラグインの記述子ファイル
```

### プラグインに機能を追加する場合
「プラグインの実装」および「プラグインのヘッダ」に追記します。
または、「.pp」「.h」ファイルを追加し機能を実装します。
主にクラスごとにファイルを分けるのが一般的かと思います。

### 他のプラグインの機能に依存する場合
「プラグインのモジュールルール」に追記します。
※各モジュールごとにモジュールルールを個別に作成します。

### 一つのプラグイン内で複数のモジュールを持つ場合
「プラグインの記述子ファイル」に追記します。
 ※Source＞MyPlugin配下が一つのモジュールです。

## 具体例
本題のプラグイン内容ですが、実例があったほうが説明しやすいので稚拙ながら私が作成中のプラグインの実装から抜粋して説明しようと思います。

https://github.com/koiusa/RidingSystemPlugin

内容といたしましたては、サードパーソン視点で二足歩行メカに乗り降りする機能一式です。
効果音とエフェクトも実装しています。

ファイル構成は以下となります。

C++実装
\Plugins\RidingSystem\Source
![スクリーンショット 2023-11-04 035755.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/9b6736f0-ac78-f919-9ff3-54040add5415.png)

BP実装
\Plugins\RidingSystem\Content
![スクリーンショット 2023-11-04 041247.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/bf6d307c-c629-5ed0-6d4e-2d44b55a0e2f.png)


### #プラグインのモジュール（C++実装）
#### AdditionlalInputContext
EnhanthdInput用の独自実装のInputContextです。
InputMappingContextのModifiersとして使用します。
 
 <img width="100" alt="スクリーンショット 2023-11-04 044609.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/ba9dc5e2-e21d-fff5-ef77-7c7cfceefb4b.png">

![スクリーンショット 2023-11-04 044512.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/7932d4de-ab06-e934-af1c-1428070014cd.png)

今回の例では、Dualshock4の十字キーの入力値は加工しないと扱いづらいのですが、BPで加工すると煩雑になります。また、コントローラーの汎用的に使用するものなのでこのようにしています。

#### BasicCharacterAttributes
GameplayAbilitySystem(以下GAS)で使用するプロパティ（HPやスタミナ）の定義です。
プロジェクト毎に作成した場合、プロパティを参照している依存関係の範囲が特定しずらく移植が困難です。
プラグインの中で定義すれば依存関係が明確になり、移植時もプラグインディレクトリをコピーすればよいので運用しやすくなります。

#### RidingSystem
エディターで空のプラグインを作成したときのテンプレートのままです。
空実装で、不要ですが今後の拡張の余地のため残しています。

### #プラグインのモジュールルール
各モジュールの<モジュール名>.Build.cs

モジュールごとの説明は冗長となるので、AdditionalInputContext.csから引用します。

```C#:AdditionalInputContext.cs
// Some copyright should be here...

using UnrealBuildTool;

public class AdditionalInputContext : ModuleRules
{
	public AdditionalInputContext(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
		
		PublicIncludePaths.AddRange(
			new string[] {
				// ... add public include paths required here ...
			}
			);
				
		
		PrivateIncludePaths.AddRange(
			new string[] {
				// ... add other private include paths required here ...
			}
			);
			
		
		PublicDependencyModuleNames.AddRange(
			new string[]
			{
				"Core",
				// ... add other public dependencies that you statically link with here ...
			}
			);
			
		
		PrivateDependencyModuleNames.AddRange(
			new string[]
			{
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
				"InputCore",
                "EnhancedInput",
				// ... add private dependencies that you statically link with here ...	
			}
			);
		
		
		DynamicallyLoadedModuleNames.AddRange(
			new string[]
			{
				// ... add any modules that your module loads dynamically here ...
			}
			);
	}
}
```

#### DependencyModuleNames
実例のプラグインが依存している機能をエディターに伝えるために記述します。
PrivateとPublicがありますが、ちょっと調べ切れていないので詳しく説明できないですが、スコープを限定するほうがよいのでPravateで記述して不具合が出た場合Publicにするのがよいでしょう。

以下はデフォルトの記述です
```C#
				"CoreUObject",
				"Engine",
				"Slate",
				"SlateCore",
```
以下はインプットに関連するもので、実例のモジュールはEnhancedInputに依存しているので追記します。
各モジュールの依存関係に合わせて変更する部分となります。
```C#
				"InputCore",
                "EnhancedInput",
```

### #プラグインの記述子
UE5エディターに実際に組み込むモジュールを記載します。
モジュールを実装していてもここに記述しないとエディターに反映されません。
つまり、実装のみ記載してエディターでの公開は先延ばしにするということも可能です。

<プラグイン名>.uplugin
```json:RidingSystem.uplugin
{
	"FileVersion": 3,
	"Version": 1,
	"VersionName": "1.0",
	"FriendlyName": "RidingSystem",
	"Description": "This is RidingSystem",
	"Category": "Other",
	"CreatedBy": "koiusa",
	"CreatedByURL": "",
	"DocsURL": "",
	"MarketplaceURL": "",
	"SupportURL": "",
	"CanContainContent": true,
	"IsBetaVersion": true,
	"IsExperimentalVersion": false,
	"Installed": false,
	"Modules": [
		{
			"Name": "RidingSystem",
			"Type": "Runtime",
			"LoadingPhase": "Default"
		},
		{
			"Name": "AdditionalInputContext",
			"Type": "Runtime",
			"LoadingPhase": "Default"
		},
		{
			"Name": "BasicCharacterAttributes",
			"Type": "Runtime",
			"LoadingPhase": "Default"
		}
	],
	"Plugins": [
		{
			"Name": "RawInput",
			"Enabled": true
		},
		{
			"Name": "EnhancedInput",
			"Enabled": true
		},
		{
			"Name": "GameplayAbilities",
			"Enabled": true
		},
		{
			"Name": "PhysicsControl",
			"Enabled": true
		},
		{
			"Name": "WaveformEditor",
			"Enabled": true
		}
	]
}
```

#### Modules
UE5エディターで使用可能にするモジュールを記述します
実例のプラグインはモジュールが３つあるのでモジュールの項目に記述します。
AdditionalInputContext
BasicCharacterAttributes
RidingSystem

#### Plugins
プラグインが依存している他のプラグインを記述します。
ここに記載があるとUE5エディターのプラグイン一覧で自動的に有効無効を設定されます。
実例のプラグインでは以下の他のプラグインに依存しています。

入力関連
　RawInput
　EnhancedInput
 
キャラクタのステータス関連
　GameplayAbilities
 
メカ乗り降り関連
　PhysicsControl
 
効果音関連
　WaveformEditor

### #プラグインのモジュール（BP実装）
準備としてプラグインのディレクトリ配下にCntentディレクトリを作成します。
Plugins\\<プラグイン名>\Content

実例のプラグインはRidingSystemなので以下のようになります。
Plugins\RidingSystem\Content

UE5エディタ内ではコンテンツブラウザのプラグインに表示されます。
![スクリーンショット 2023-11-04 072342.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/926c1dd3-aa57-b8dd-14bb-d0a956530a8e.png)
※カラーは見やすさのため設定しました。

#### 新規BPコンテンツをプラグインとして作成
BPコンテンツの実装は普段プロジェクトのContentディレクトリに作成している実装を、プラグインのContentのディレクトリに作成するだけです。

#### 既存のBPコンテンツをプラグインとして作成
既存のBPコンテンツをプラグインとする場合、関連している実装をプラグインのContentのディレクトリに移動してください。
コピーした場合は、プラグインのディレクトリにコピーされたBPが内部で参照しているパスはコピー元と同じものなのでプロジェクトに残っている実装が参照されることになります。

![スクリーンショット 2023-11-04 072638.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/07e30de2-fd93-516f-de34-5ea9cb9a1430.png)

関連BPの移動漏れやパスの再設定の漏れがある場合、他のプロジェクトでプラグインを使用した場合、参照エラーとなるので注意が必要です。

ファイル移動後は、コンテンツブラウザのContentディレクトリを右クリック＞Fix Up Redirectorsを実行し、リファレンスを修正しておくほうが良いです。
![スクリーンショット 2023-11-04 102416.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/14767088-cd80-c3e9-ae5f-8ed3e31b688a.png)


#### 補足
これは個人の見解ですが、
プラグインのBP実装のディレクトリのまとめ方としては、BPのファイルの種類ではなく、機能ごとに分けるのがいいと思います。UE5のエディターはフィルター機能が優秀なのでファイルの種類は検索で絞れます。
また、機能ごとで分けるほうが依存関係が明確になるので可読性と移植性が向上します。
機能をまたがる共通のBPが出てきた場合どうするのかということですが、共通で使用されているということは汎用性があるということなので、
それはプラグインに新たにまとめてモジュールとしてするのも検討するのもアリかと思います

# プラグインの使用
プロジェクト内にPluginsディレクトリをなければ作成、すでにあればそこに対象のプラグインディレクトリを一式配置すれば使用可能になります。
詳細は各ラグインのマニュアルに従ってください。

# プロジェクト管理
プラグインは複数のプロジェクトで共通して使用するもののため、いずれかのプロジェクトで更新した際に、別のプロジェクトでも同期をとりたい場合があるかと思います。
修正内容をコピーして貼り付けてもいいですが、面倒な上にコピー漏れなども発生します。

そこで便利なのが、ジャンクションという機能です。
ジャンクションはターゲットにしたディレクトリの参照を作成し、どちらか一方を変更した場合に両方に変更が適用されるというものです。
他にも、ファイルを同期させる機能おしてハードリンク、シンボリックリンクというものがありますが、今回の用途ではジャンクションが適切かと思います。
私はWindowsを使っているのでWindowsで説明しますが、Macでも同様の機能はあると思います。
まずプラグインを任意の場所に配置します。
そしてプラグインを使用するプロジェクトを任意の場所に配置します。

私の場合、このようにしています。
![スクリーンショット 2023-11-04 104357.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/5e282743-9cbf-3496-af46-9e73dea2fb31.png)

RidingSystemPlugin
 今回参考に使用したgithubから取得したプラグインのディレクトリ
 
Sandbox_UE5_2
 プラグインを使用するプロジェクトのディレクトリ
 プロジェクトにPluginsディレクトリがなければ作成して下さい。
 中身は空で問題ないです。
 
ディレクトリの準備ができたらコマンドを実行します。
タスクバーの検索で「コマンド」と検索し、「管理者として実行」します
![スクリーンショット 2023-11-04 110045.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953463/b540edb2-a4ec-e571-95ac-fb5056ce369d.png)

リンク＝プロジェクトのディレクトリ\Plugins\プラグイン名
ターゲット＝プラグインのディレクトリのパス
以下のコマンドでジャンクションが生成されます。
```shell:コマンド
mklink /j <リンク> <ターゲット>
```

```shell:コマンド例
mklink /j D:\Work\koiusa\Sandbox_UE5_2\Plugins\RidingSystem D:\Work\koiusa\RidingSystemPlugin\Plugins\RidingSystem
```

コマンドプロンプト使ったことがない方は抵抗があるかもしれませんが、慣れれば便利に感じるのでいろいろ試してみてください。

以上、プラグイン作成とファイル同期の方法でした。

# 追記
今回、説明で使用した実例のプラグインですが、ライセンスはMITとしているのでなに役立てていただけると嬉しいです。
個人的にメカアクションゲームを作るために育てているのでフィードバックなどいただけると励みになります。