【UE5】SubsystemをC++で作成してBP拡張する方法メモ

自分用メモくらいの記事なのでわからない点があれば質問をコメントなりXでお願いしますmm

SubsystemをC++で作成して拡張用のBPを持たせる方法をおかずさんが過去に紹介してくださっていました。

https://www.docswell.com/s/historia_Inc/5WVYJK-ue4-dataasset-subsystem-gameplayability

その他記事やツヨツヨな方に助けていただいて、WorldContextがないと使えなかったノード(Delay, SpawnActorFromClassなど)も使えるようにしました。

## クラス作成
まずは、RiderなりUE EditorなりでGameInstanceSubsystemを継承してクラス作成する。
(どういったライフサイクルのSubsystemを使いたいかで親クラスは選択する。)


## C++
.cppは今回いじる必要なし。

作成したヘッダーファイル（.h）を編集します


#### .hのコード
```c++:MyGameInstanceSubsystem.h
/*
 * MyGameInstanceSubsystem
 * → 作成したクラス名に置換する
 * 
 * YOURPROJECT_API
 * → (プロジェクト名)_APIに置換する
 */

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/GameInstanceSubsystem.h"
#include "MyGameInstanceSubsystem.generated.h"


UCLASS(Abstract, Blueprintable, MinimalAPI)
class UMyGameInstanceSubsystemHelperBase : public UObject
{
	GENERATED_BODY()
//関数を足したいときはこの下に連ねる。
    
public:
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent) //処理の中身はBPで書くと宣言
	void FunctionFirst();
    
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
    //引数がint型のValueの場合
	void FunctionSecond(int32 Value);

    UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	//引数がint型のIntParam、Float型のFloatParamで、戻り値がName型のNameVal、String型のStringValの場合
	void FunctionThird(int IntParam, float FloatParam, FName& NameVal, FString& StringVal);


    // GetWorld()の実装をオーバーライド。WorldContextの必要なノードを使えるようになる。
	virtual class UWorld* GetWorld() const override
	{
		do
		{
			if (HasAnyFlags(RF_ClassDefaultObject)) break; // CDOでは無効
			if (!GetOuter()) break; // Outerがなければ無効
			if (GetOuter()->HasAnyFlags(RF_BeginDestroyed)) break; // 死に掛けてたら無効
			if (GetOuter()->IsUnreachable()) break; // 参照したらあかん事になってたら無効
            
			return GetOuter()->GetWorld();
		} while (0);
        
		return nullptr;
	} 
    
};


UCLASS()
class YOURPROJECT_API UMyGameInstanceSubsystem : public UGameInstanceSubsystem
{
	GENERATED_BODY()

public:
	// 拡張用BPのプロパティ
	UPROPERTY(Transient, BlueprintReadWrite)
	UMyGameInstanceSubsystemHelperBase* SubsystemHelper;
};
```

#### Tips: 関数の引数と戻り値

型の後ろに&をつけると戻り値になる。
```c++:
void FunctionThird(int32 Param1, float Param2, FName& Val1, FString& Val2);
//引数がint32型のParam1とfloat型のParam2、
//戻り値がFName型のVal1とFString型のVal2
//の例
```

## UnrealEngine側での設定

#### 作成したMyGameInstanceSubsystemHelperBase的なものを継承したBP作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/7e8823aa-3a38-4422-a70a-1dadc2997631.png)

作成したBPで関数をオーバーライドします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/4a833133-4f74-4e21-8d82-7751368cd5b7.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/bd7526e1-634c-4ac4-87f4-19cc96cb9f66.png)


#### 関数呼び出しのための準備
GamemodeやレベルブループリントやGameInstanceで以下を実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/19b26c4e-2197-44c1-87c5-0de081109a96.png)


#### ALL COMPLETE!
オーバーライドした関数がどこからでも使えるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/a50009a7-ea74-4dca-b27b-46ac85f75721.png)


---
## おまけ

もし関数の引数や戻り値にEnumを使いたい際は合わせてどうぞ

https://qiita.com/n1sh1_vr/items/24363cb4869092dd5c6d


## 参考

https://www.docswell.com/s/historia_Inc/5WVYJK-ue4-dataasset-subsystem-gameplayability

https://qiita.com/tukigaselio/items/7641ac38aabb9996963d

http://kagring.blog.fc2.com/blog-entry-679.html

https://kinnaji.com/2023/06/25/hideworldcontext/

https://tech.flying-rat.studio/post/ue-subsystems.html

恒吉星光さんの投稿 (https://x.com/seiko_dev/status/1530475319572180993)