[UE4] Subsystem, GameplayAbilityに関する講演で使用したC++コードについて

# はじめに
2021/10/2 に開催された「[オンライン出張ヒストリア！ゲーム開発勉強会2021](https://historia.co.jp/archives/22783/)」にて、『目指せ脱UE4初心者！？知ってると開発が楽になる便利機能を紹介 DataAsset, Subsystem, GameplayAbility編 』というタイトルで講演をしました。

スライド：https://www.slideshare.net/historia_Inc/ue4-dataasset-subsystem-gameplayability
講演動画：https://www.youtube.com/watch?v=2L1Dor22Kjs

この講演ではSubsystem, GameplayAbilityを使うために必要なC++コードを紹介したのですが、オンライン上のスライドですとコピペがしづらいのでC++コード部分を抽出して記事化しました。

# Subsystem
## p68 GameInstance Subsystemの場合 - 準備 -
```cpp
#pragma once

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

UCLASS()
class ○○○_API UMyGameInstanceSubsystem
 : public UGameInstanceSubsystem
{
	GENERATED_BODY()

public:
	UFUNCTION(BlueprintCallable)
	int32 GetScore(){ return Score; }

private:
	int32 Score;
};
```

## p74 BPから作成可能に（要コンパイル）
```cpp
#pragma once

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

UCLASS(Blueprintable, Abstract)
class ACTIONRPG_API UMyWorldSubsystem : public UWorldSubsystem
{
	GENERATED_BODY()
	
};
```

## p82 拡張用BPのプロパティを持たせるには
```cpp
#pragma once

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

// 拡張用BPのベースとなるクラス
UCLASS(Abstract, Blueprintable, MinimalAPI, meta = (ShowWorldContextPin))
class UMyGameInstanceSubsystemHelperBase : public UObject
{
	GENERATED_BODY()
};

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

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

};
```

## p84 C++側で生成と設定をする手も ( .h側 )
```cpp
#pragma once

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

UCLASS(Abstract, Blueprintable, MinimalAPI, meta = (ShowWorldContextPin))
class UMyGameInstanceSubsystemHelperBase : public UObject
{
    GENERATED_BODY()
};

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

public:

	UMyGameInstanceSubsystem();

	virtual void Initialize(FSubsystemCollectionBase& Collection) override;
	virtual void Deinitialize() override;

	UPROPERTY(Transient, BlueprintReadOnly)
	UMyGameInstanceSubsystemHelperBase* SubsystemHelper;

private:
	TSubclassOf< class UMyGameInstanceSubsystemHelperBase > SubsystemHelperClass;
};
```

## p85,86 C++側で生成と設定をする手も ( .cpp側 )
```cpp
#include "MyGameInstanceSubsystem.h"
#include "Runtime/CoreUObject/Public/UObject/ConstructorHelpers.h"

UMyGameInstanceSubsystem::UMyGameInstanceSubsystem()
{
	// 拡張用BPのクラス情報を検索・取得。パスの指定には注意
	// .ini や プロジェクト設定でBPアセットのパスを設定できるようにしておくと親切
	static ConstructorHelpers::FClassFinder<UMyGameInstanceSubsystemHelperBase> BluePrintFile(TEXT("/Game/Subsystem/BP_MyGameInstanceSubsystemHelper"));
	if (BluePrintFile.Class) 
	{
		SubsystemHelperClass = (UClass*)BluePrintFile.Class;
	}
}

void UMyGameInstanceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	SubsystemHelper = nullptr;	

	if (SubsystemHelperClass)
	{
		// 拡張用BPを生成・設定
		SubsystemHelper = NewObject<UMyGameInstanceSubsystemHelperBase>(GetTransientPackage(), SubsystemHelperClass);
	}
}

void UMyGameInstanceSubsystem::Deinitialize()
{
	SubsystemHelper = nullptr;
}
```

## p88 C++側で関数の準備をする場合
```cpp
UCLASS(Abstract, Blueprintable, MinimalAPI, meta = (ShowWorldContextPin))
class UMyGameInstanceSubsystemHelperBase : public UObject
{
	GENERATED_BODY()

public:
	// 処理の中身はBP側で書くことを宣言
	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent)
	void TestFunc();
};
```
# Gameplay Ability
## p130 .h側
```cpp
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "AbilitySystemComponent.h"
#include "MyBlueprintFunctionLibrary.generated.h"

UCLASS()
class ACTIONRPG_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()


	UFUNCTION(BlueprintCallable, meta = (DefaultToSelf = "Target"))
	static void GiveAbilityToComponent(AActor* Target, UAbilitySystemComponent* AbilitySystem, TArray<TSubclassOf<class UGameplayAbility>> AbilityList);
	
};
```

## p131 .cpp側
```cpp
#include "MyBlueprintFunctionLibrary.h"

void UMyBlueprintFunctionLibrary::GiveAbilityToComponent(AActor* Target, UAbilitySystemComponent* AbilitySystem, TArray<TSubclassOf<class UGameplayAbility>> AbilityList)
{
	if (AbilitySystem && Target)
	{
		int32 inputID(0);
		if (AbilityList.Num() > 0)
		{
			for (auto Ability : AbilityList) 
			{
				if (Ability)
				{
					AbilitySystem->GiveAbility(FGameplayAbilitySpec(Ability.GetDefaultObject(), 1, inputID++));
				}
			}
		}
		AbilitySystem->InitAbilityActorInfo(Target, Target);
	}
}
```
## p132 プロジェクト側の .Build.cs
```cpp
PrivateDependencyModuleNames.AddRange(
new string[] {
	"GameplayAbilities"
 	}
);

```

# さいごに
DataAsset, Subystem, GameplayAbilityはいいぞ！

おしまい