UE5 GameplayAbilityでのGameplayTagを使ったバインド処理についてのメモ

# 概要
UnrealEngine5 のゲームプレイタグのバインド処理関連についてのメモです。
`GameplayAbilities` プラグインを使っています。

## 更新履歴
| 日付 | 内容 |
|:-----|:----|
|2023/07/14| 初版 |
|2024/06/05|  UnregisterGameplayTagEvent について追記 |


## 参考
以下の記事を参考にいたしました、ありがとうございます。
[UE公式：FGameplayTagBlueprintPropertyMap](https://docs.unrealengine.com/5.1/en-US/API/Plugins/GameplayAbilities/FGameplayTagBlueprintPropertyMap/)
[FGameplayTagBlueprintPropertyMap - The Tag Watcher](https://dev.epicgames.com/community/learning/tutorials/n2nJ/unreal-engine-fgameplaytagblueprintpropertymap-the-tag-watcher)
[UE公式：UGameplayTagsManager](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/API/Runtime/GameplayTags/UGameplayTagsManager?utm_source=chatgpt.com)

## 関連過去記事
[UE4 GameplayTagについてのメモ](https://qiita.com/unknown_ds/items/87d6405323d8626eadef)

## 環境
Windows10
Visual Studio 2022
UnrealEngine 5.1.1

## 関連ソース
"\Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\GameplayEffectTypes.h"
"\Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\AbilitySystemComponent.h"


# FGameplayTagBlueprintPropertyMap を使ったGameplayTagの監視
Lyraサンプルにも使われているアニメーションクラスでのバインド例です。

## UAnimInstanceクラスでのタグ変更情報受け取り
アニメーションBPの基底クラスをC++で定義し、`FGameplayTagBlueprintPropertyMap`を持ちます。

```cpp:MyAnimInstance.h
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "GameplayEffectTypes.h"
#include "MyAnimInstance.generated.h"

UCLASS()
class MYPROJECT_API UMyAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
	
public:
	// プロパティマップ初期化
	void InitializeWithAbilitySystem(UAbilitySystemComponent* ASC){
		check(ASC);
		GameplayTagPropertyMap.Initialize(this, ASC);
	}

	// プロパティマップ
	UPROPERTY(EditDefaultsOnly, Category = "GameplayTags")
	FGameplayTagBlueprintPropertyMap	GameplayTagPropertyMap;

#if WITH_EDITOR
	EDataValidationResult IsDataValid(TArray<FText>& ValidationErrors) override{
		Super::IsDataValid(ValidationErrors);
		
		GameplayTagPropertyMap.IsDataValid(this, ValidationErrors);
		
		return ((ValidationErrors.Num() > 0) ? EDataValidationResult::Invalid : EDataValidationResult::Valid);
	}
#endif // WITH_EDITOR

};
```

`AbilitySystemComponent`を渡して初期化をします。

```cpp:MyCharacter.cpp
void AMyCharacter::PossessedBy(AController * NewController)
{
	Super::PossessedBy(NewController);

	// プロパティマップ初期化
	auto _AnimInst = Cast<UMyAnimInstance>(GetMesh()->GetAnimInstance());
	if(_AnimInst){
		_AnimInst->InitializeWithAbilitySystem(GetAbilitySystemComponent());
	}
}
```

`MyAnimInstance`を継承したAnimBPを開き`Gameplay Tag Property Map` の `Property Mappings` に紐づけ設定をします。
以下の例では `Ability.Skill.Ability3`というタグが追加された場合、bool変数の`IsAbility3`が`true`となり、タグが削除されたら `false`になります。
![AnimBP.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/b31c820d-930d-763e-f88a-1449bfb9a899.png)



# ゲームプレイタグの追加/削除で処理を行う
状態変化用にタグを設定をしてその開始/終了時に処理を呼びたいケースでの想定です。

## AbilitySystemComponentに処理を登録する
指定したゲームプレイタグの変化で処理を呼ぶ場合は、`RegisterGameplayTagEvent` を使います。ゲームプレイタグを指定しない場合は `RegisterGenericGameplayTagEvent` で可能です。
以下コード例。

```cpp:MyCharacter.h
#pragma once

#include "CoreMinimal.h"
#include "GameplayTags.h"
#include "MyCharacter.generated.h"

UCLASS()
class MYPROJECT_API AMyCharacter : public ACharacter
{
	GENERATED_BODY()
public:
    // ...省略...

	// 指定タグが追加/削除された場合に呼ばれる処理
	virtual void TagChangeEvent(const FGameplayTag _CallBackTag, int32 _NewCount);
	// 何らかのタグが追加/削除された場合に呼ばれる処理
	virtual void AddTagEvent(const FGameplayTag _CallBackTag, int32 _NewCount);
};
```

```cpp:MyCharacter.cpp
void AMyCharacter::PossessedBy(AController * NewController)
{
	Super::PossessedBy(NewController);

    // 指定タグ
	FGameplayTag _GpTag(FGameplayTag::RequestGameplayTag(FName("Ability.Skill.Ability3")));
	// 指定タグが追加/削除時に呼ぶ処理をバインド
	GetAbilitySystemComponent()->RegisterGameplayTagEvent(_GpTag, EGameplayTagEventType::NewOrRemoved).AddUObject(this, &AMyCharacter::TagChangeEvent);
	// 指定タグが加算時に呼ぶ処理をバインド
	GetAbilitySystemComponent()->RegisterGameplayTagEvent(_GpTag, EGameplayTagEventType::AnyCountChange).AddUObject(this, &AMyCharacter::TagChangeEvent);

	// 何らかのタグが追加/削除時のイベントをバインド(新規/削除のみ加算は除く)
	GetAbilitySystemComponent()->RegisterGenericGameplayTagEvent().AddUObject(this, &AMyCharacter::AddTagEvent);
}

void AMyCharacter::TagChangeEvent(const FGameplayTag _CallBackTag, int32 _NewCount)
{
	UE_LOG(LogTemp, Log, TEXT("TagChangeEvent %s %d"), *_CallBackTag.GetTagName().ToString(), _NewCount);
}

void AMyCharacter::AddTagEvent(const FGameplayTag _CallBackTag, int32 _NewCount)
{
	UE_LOG(LogTemp, Log, TEXT("AddTagEvent %s %d"), *_CallBackTag.GetTagName().ToString(), _NewCount);
}
```

登録された処理を外すには登録時の `FDelegateHandle` を保持して `UnregisterGameplayTagEvent`で行います。

```cpp:.cpp
// ゲームプレイタグの変化時の処理を外す
_ASC->UnregisterGameplayTagEvent(DeletageHandle, _Tag, EGameplayTagEventType::NewOrRemoved);

```


他にも GameplayEffect追加時には`OnGameplayEffectAppliedDelegateToSelf` に処理を登録できるようです。


# まとめ
`GameplayAbility` でのタグ関連処理がたくさんあり把握しきれていません。