【UE5】WorldSubsystemをC++で作成してTickをBPでオーバーライドしたい

以下の記事でSubsystemを継承したクラスをC++で作成してBP拡張する方法をメモしました。

https://qiita.com/n1sh1_vr/items/526aaec9a6e152518e86

この記事ではWorldSubsystem内でTickを使いたい場合のヘッダーファイルの最小構成をメモしておきます。

## クラス作成
まずは、RiderなりUE EditorなりでTickableWorldSubsystemを継承してクラス作成する。

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

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

```c++
//MyTickableWorldSubsystem→クラス名に置き換え
//YOURPROJECT→プロジェクト名に置き換え

#pragma once

#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
#include "MyTickableWorldSubsystem.generated.h"

/**
 * 
 */

UCLASS(Abstract, Blueprintable, MinimalAPI)
class UMyTickableWorldSubsystemHelperBase : 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);
    
    
public:
	// 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;
	}

	// ブループリントでオーバーライド可能なTickメソッド
	UFUNCTION(BlueprintNativeEvent, Category = "Subsystem")
	void TickHelper(float DeltaTime);
	virtual void TickHelper_Implementation(float DeltaTime) {}
    
};

UCLASS()
class YOURPROJECT_API UMyTickableWorldSubsystem : public UTickableWorldSubsystem
{
	GENERATED_BODY()

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

	// Tick関数
	virtual void Tick(float DeltaTime) override
	{
		// ヘルパークラスのTickを呼び出す
		if (SubsystemHelper)
		{
			SubsystemHelper->TickHelper(DeltaTime);
		}
	}
    
	// Tick優先度の取得
	virtual TStatId GetStatId() const override
	{
		RETURN_QUICK_DECLARE_CYCLE_STAT(UMyTickableWorldSubsystem, STATGROUP_Tickables);
	}
	
};
```

## UnrealEngine側での設定
### BP作成
MyTickableWorldSubsystemHelperBaseを継承したBPを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/7ce0c27d-0771-4e6b-9362-2e2f8f7f66f4.png)


### Tick, 関数のオーバーライド
用意した関数とTickがオーバーライドできるようになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/0c7e7889-5b29-489d-8b0a-b2b43acc2785.png)



### 関数呼び出しのための準備
拡張用BPの作成と、Subsystemの設定を行います。
GamemodeやレベルブループリントやGameInstanceで以下を実行してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/0f69213a-2fb0-43ab-8b9f-1ab04337f536.png)



### ALL COMPLETE!
オーバーライドした関数がどこからでも使えるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/44aff31a-597f-4c1b-9b87-ade2829e2441.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)

https://qiita.com/n1sh1_vr/items/526aaec9a6e152518e86