UE5 GameplayEffectComponentについてのメモ

# 概要
UnrealEngine5 のゲームプレイエフェクトコンポーネントについてのメモ書き。
UE5.3からゲームプレイエフェクトの機能がコンポーネントごとに切り分けられています。


## 更新履歴
| 日付 | 内容 |
|:-----|:----|
|2024/04/12| 初版 |
|2024/10/18| URemoveOtherGameplayEffectComponent の追記 |


## 参考
以下の記事を参考にいたしました、ありがとうございます。
[UE公式：UGameplayEffect](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/UGameplayEffect?application_version=5.3)
[UE公式：UGameplayEffectComponent](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/UGameplayEffectComponent?application_version=5.3)
[UE公式：ゲームプレイエフェクト](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/gameplay-effects-for-the-gameplay-ability-system-in-unreal-engine)
[GameplayEffect - その3 Expiration と Tags 編](https://goolee.hatenablog.com/entry/2018/11/26/201153)
[GameplayEffect - その4 Immunity と Granted Abilities 編](https://goolee.hatenablog.com/entry/2018/11/27/212833)
[UE公式：FGameplayEffectQuery](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/FGameplayEffectQuery?application_version=5.3)
[UE公式：FTagcontainerAggretator](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/FTagContainerAggregator?application_version=5.3)
[UE公式：FGameplayTagRequirements](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/FGameplayTagRequirements?application_version=5.3)
[UE公式：FInheritedTagContainer](https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/FInheritedTagContainer?application_version=5.3)


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

## 環境
Windows10
Visual Studio 2022
UnrealEngine 5.3.2

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



# GameplayEffectComponent について
5.3よりゲームプレイエフェクトの機能はコンポーネント化されました。
5.2までは以下のように使用するしないに関わらずパラメータが全て表示されていたため、機能別にコンポーネント化して整理したようです。
### 5.2でのゲームプレイエフェクト
![GameplayEffect52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/1fb46731-49da-2d7c-ec66-bc1019f76015.png)



## 利用可能なゲームプレイエフェクトコンポーネント

|ゲームプレイエフェクトコンポーネント | 説明 |
|:-----|:----|
|UAbilitiesGameplayEffectComponent|アクティブ中にゲームプレイエフェクトのターゲットに追加のゲームプレイアビリティを付与します。|
|UAdditionalEffectsGameplayEffectComponent|特定の条件に基づいてアクティベートを試みる追加のゲームプレイエフェクトを設定できます。|
|UAssetTagsGameplayEffectComponent|ゲームプレイエフェクトアセットが所有するタグ。これらはアクタに転送「されません」。|
|UBlockAbilityTagsGameplayEffectComponent|オーナーのゲームプレイエフェクトのターゲットアクタに対するゲームプレイタグに基づいて、ゲームプレイアビリティのアクティベーションをブロックすることができます。|
|UChanceToApplyGameplayEffectComponent|ゲームプレイエフェクトが適用される確率を設定できます。|
|UCustomCanApplyGameplayEffectComponent|CustomApplicationRequirement クラスを使用して、このゲームプレイエフェクトの適用/非適用を設定することができます。|
|UGameplayEffectUIData_TextOnly|テキストのみを含む UIデータ。これは主に、UGameplayEffectUIData のサブクラスの例として使用されます。|
|UImmunityGameplayEffectComponent|耐性によって他の GameplayEffectSpec の適用をブロックします。|
|URemoveOtherGameplayEffectComponent|特定の条件に基づいて他のゲームプレイエフェクトを削除することができます。|
|UTargetTagRequirementsGameplayEffectComponent|この GE を適用したり、実行を継続したりする必要がある場合にターゲット (ゲームプレイエフェクトのオーナー) に必要なタグ要件を指定します。|
|UTargetTagsGameplayEffectComponent|ゲームプレイエフェクトのターゲット (「オーナー」と言う場合もあります) にタグを付与します。|


### UAbilitiesGameplayEffectComponent
追加のゲームプレイアビリティを付与します。

|設定パラメータ | 説明 |
|:-----|:----|
|Ability| 発動させるゲームプレイアビリティ|
|Level| アビリティレベル|
|Removal Policy| 指定アビリティを終了させる条件を指定|

![AbilitiesGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/de64ce17-ad64-3a5c-9bc4-44928b224b16.png)

動作をすると `UGameplayAbility::OnAvatarSet` が呼ばれるのでここでアビリティをパッシブ発動をするようにオーバーライドをしてのコード追加が必要です。
以下C++コード例。

```c++:.h
UCLASS()
class SAMPLE00_API UPGA_Test : public UPlayerGameplayAbility
{
	GENERATED_BODY()
// ..省略..

 	// アバター設定or切り替え時の処理
	virtual void OnAvatarSet(const FGameplayAbilityActorInfo* _ActorInfo, const FGameplayAbilitySpec& _Spec) override;
};
```

```c++:.cpp
void UPGA_Test::OnAvatarSet(
	const FGameplayAbilityActorInfo* _ActorInfo,
	const FGameplayAbilitySpec& _Spec
	)
{
	Super::OnAvatarSet(_ActorInfo, _Spec);

	// GEからのパッシブ発動
	if (_Spec.GameplayEffectHandle.WasSuccessfullyApplied()) {
		_ActorInfo->AbilitySystemComponent->TryActivateAbility(_Spec.Handle, false);
	}
}

```


### UAdditionalEffectsGameplayEffectComponent
特定条件にて追加でゲームプレイエフェクトを付与します。

|設定パラメータ | 説明 |
|:-----|:----|
|On Application Gameplay Effects| 適用時に追加で適用される他のGE |
|On Complete Always| 終了方法に関係なく完了したときに適用されるGE |
|On Complete Normal| 持続時間によって自然に切れたときに適用されるGE |
|On Complete Permatruely| 途中で期限切れになった場合に適用するGE (強制削除、タグのクリアなど)|

![AdditionalEffectGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/28a1de67-522d-01a0-4f12-26ff378fed2c.png)



### UAssetTagsGameplayEffectComponent
ゲームプレイエフェクトが所有するゲームプレイタグを指定します。

|設定パラメータ | 説明 |
|:-----|:----|
|CombinedTags| 追加するタグから削除するタグを除いたタグ（編集不可）|
|Added| 追加するタグ|
|Removed| 削除するタグ|

![AssetTagsGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/4b41dc4b-2226-c460-fe69-a1f4fe6da89f.png)

ゲームプレイ適用時コールバックなどで取り出して別途処理するようなケースで使います。
以下コード例。

```C++
// ゲームプレイエフェクト適用時コールバック
void AAppCharacterBase::OnActiveGameplayEffectAddedCallback(
	UAbilitySystemComponent* _TargetASC,
	const FGameplayEffectSpec& _SpecApplied,
	FActiveGameplayEffectHandle _ActiveHandle
)
{
	// アセットタグ
	FGameplayTagContainer _SpecAssetTags;
	_SpecApplied.GetAllAssetTags(_SpecAssetTags);
 	// タグの表示
	UE_LOG(LogTemp, Log, TEXT("%s"), *_SpecAssetTags.ToString());
}
```



### UBlockAbilityTagsGameplayEffectComponent
アビリティのブロックタグの追加/削除ができます。

|設定パラメータ | 説明 |
|:-----|:----|
|CombinedTags| 追加するタグから削除するタグを除いたタグ（編集不可）|
|Added| 追加するアビリティブロックタグ|
|Removed| 削除するアビリティブロックタグ|

![BlockAbilityTagGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/c1a42908-1e61-959f-fbe1-62e8330e6f6b.png)

ゲームプレイアビリティが持つ `BlockAbilitiesWithTag` とは別にゲームプレイエフェクトの適用中にブロックしたいアビリティをタグで設定できます。



### UChanceToApplyGameplayEffectComponent
ゲームプレイエフェクトの適用確率[0.0 - 1.0]を設定できます。

![ChanceToApplyGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/78d8be0b-2afa-4024-16b9-c4cfb4c1f1f3.png)



### UCustomCanApplyGameplayEffectComponent
ゲームプレイエフェクトの適用/非適用を `CustomApplicationRequirement`クラスを使っての設定ができます。

![CustomCanApplyGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/04f3786c-2881-cb87-5afb-f330aecc01e4.png)

単純な適用確率ではなく、ゲーム内キャラクター等のパラメータなどから適用の可否が決まるようなケースで使用できます。
以下カスタムクラスのコード例。

```C++:GameplayCAR_Example.h
#pragma once

#include "CoreMinimal.h"
#include "GameplayEffectCustomApplicationRequirement.h"
#include "GameplayCAR_Example.generated.h"

UCLASS()
class SAMPLE00_API UGameplayCAR_Example : public UGameplayEffectCustomApplicationRequirement
{
	GENERATED_BODY()
	
public:
	// ゲームプレイエフェクトを適用するかどうかを返す
	bool CanApplyGameplayEffect_Implementation(const UGameplayEffect* _GameplayEffect, const FGameplayEffectSpec& _Spec, UAbilitySystemComponent* _ASC) const override;
};
```

```C++:GameplayCAR_Example.cpp
// ゲームプレイエフェクトを適用するかどうかを返す
bool UGameplayCAR_Example::CanApplyGameplayEffect_Implementation(const UGameplayEffect* _GameplayEffect, const FGameplayEffectSpec& _Spec, UAbilitySystemComponent* _ASC) const
{
	const auto _EffectCauser = _Spec.GetContext().GetEffectCauser();

	// キャラクターを取得
	auto _Character = Cast<ACharacter>(_EffectCauser);
	// 何らかのパラメータから適用するかを決定する
	auto _Value = _Character->GetValue();
	if( _Value < 0.0f){
		return(true);
	}
	 else{
		float _Prob = Value; 
		if( (_Prob > 1.f - SMALL_NUMBER) || (FMath::FRand() < _Prob) ){
			return(true);
		}
		else{
			return(false);
		}
	}
}
```



### UGameplayEffectUIData_TextOnly
テキストのみ。特に何もしません。

![GamepleyEffectUIDataTextOnly.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/21348e99-bf6b-073b-1705-82ebc58480bf.png)


### UImmunityGameplayEffectComponent
条件に基づいて他のゲームプレイエフェクトの適用を妨げます。

|設定パラメータ | 説明 |
|:-----|:----|
|Owning Tag Query|ここに設定されたアセットタグ/付与されたタグと一致していた場合適用されない|
|Effect Tag Query|ここに設定されたアセットタグと一致していた場合適用されない|
|SourceSpec Tag Query| GamepleyEffectSepc作成時にキャプチャされたSpecタグと一致していた場合適用されない |
|SourceAggregate Tag Query| GamepleyEffectSepc作成時にキャプチャされたAggregatedタグと一致した場合適用されない |
|Modifying Attribute|ここに設定されたアトリビュートを変更するGEは適用されない|
|Effect Souce|ここに設定されたオブジェクトが発生源のGEは適用されない|
|Effect Definition|ここに設定されたGEは適用されない|

![ImmunityGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/f3bbcbc0-10b8-40d4-83c3-fdc8e648246f.png)



### URemoveOtherGameplayEffectComponent
他のゲームプレイエフェクトを削除します。
削除される処理がこのコンポーネントを持つゲームプレイエフェクト適用時に行われるためタイミングに注意が必要です。（該当のGEのハンドルを使って判定する場合など）

|設定パラメータ | 説明 |
|:-----|:----|
|Owning Tag Query|ここに設定されたアセットタグ/付与されたタグと一致していた場合削除される|
|Effect Tag Query|ここに設定されたアセットタグと一致していた場合削除される|

![RemoveOtherGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/68dcd3ee-a129-798b-0703-2e0d89ad9bc3.png)


### UTargetTagRequirementsGameplayEffectComponent
ゲームプレイエフェクトの適用に必要なタグを指定します。

|設定パラメータ | 説明 |
|:-----|:----|
|ApplicationTagRequirements|このGameplayEffectを対象に適用するためのタグの条件。これは適用時に合否が決まる。失敗した場合、このGEは適用されない。|
|OngoingTagRequirements|GameplayEffectが適用中に、これらのタグ要件をGameplayEffectが「オン」か「オフ」かを判断するために使用します。GE自体は解除されず、一時的なオン/オフのみ。|
|RemovalTagRequirements|タグの条件を満たすとこのGameplayEffectが解除される。|

![TargetTagRequirementsGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/6bf82f3b-18c4-cbba-7427-92e475b968b0.png)



### UTargetTagsGameplayEffectComponent
ターゲットにタグを付与します。

|設定パラメータ | 説明 |
|:-----|:----|
|Combined Tags|継承したタグと追加したタグから削除したタグを除いたタグ|
|Added|親のタグに加えて持つタグ|
|Removed|親が持っていた場合は削除する必要があるタグ|

![TargetTagsGEComponent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/6d76d6d8-6cee-eca2-1449-e6db02109cac.png)

設定した付与タグは `FGameplayEffectSpec::GetAllGrantedTags` で取得できるので、ゲームプレイエフェクト適用カスタムクラス`UGameplayEffectCustomApplicationRequirement`などから取得して判定に使うことができます。

```C++:GameplayEffectSpecから取得
const FGameplayEffectSpec& _Spec;

FGameplayTagContainer _Tags;
_Spec.GetAllGrantedTags(_Tags);

UE_LOG(LogTemp, Log, TEXT("%s"), *_Tags.ToString());
```

また付与されたタグは `AbilitySystemComponent::GetOwnedGameplayTags` で取得することもできます。
以下コード例。

```c++:所有しているゲームプレイタグの表示
	// キャラクターのアビリティコンポーネント
	auto _TargetASC = _TargetCharacter->GetAbilitySystemComponent();

	FGameplayTagContainer _Tags;
	_TargetASC->GetOwnedGameplayTags(_Tags);
 	
	// ゲームプレイタグの表示
	UE_LOG(LogTemp, Log, TEXT("%s"), *_Tags.ToString());

```


# 自作のGameplayEffectComponentの作成について
`UGameplayEffectComponent` を継承してクラスを作成することで追加可能です。
必要なメソッドを継承して処理を書く必要があります。
以下テンプレートコード例。


```C++:TestGameplayEffectComponent.h
#pragma once

#include "GameplayEffectComponent.h"
#include "GameplayEffectTypes.h"
#include "TestGameplayEffectComponent.generated.h"


UCLASS()
class SAMPLE00_API UTestGameplayEffect : public UGameplayEffectComponent
{
	GENERATED_BODY()
	
public:
	// 適用可能かどうかを返す
	virtual bool CanGameplayEffectApply(const FActiveGameplayEffectsContainer& _ActiveGEContainer, const FGameplayEffectSpec& _GESpec) const override;

	// ゲームプレイエフェクトがコンテナに追加されるとき（期間）に呼ばれる処理
	virtual bool OnActiveGameplayEffectAdded(FActiveGameplayEffectsContainer& _ActiveGEContainer, FActiveGameplayEffect& _ActiveGE) const override;

	// ゲームプレイエフェクトが実行されるとき（即時or期間の周期ごと）に呼ばれる処理
	virtual void OnGameplayEffectExecuted(FActiveGameplayEffectsContainer& _ActiveGEContainer, FGameplayEffectSpec& _GESpec, FPredictionKey& _PredictionKey) const;

	// ゲームプレイエフェクトが最初に適用されるときに呼ばれる処理
	virtual void OnGameplayEffectApplied(FActiveGameplayEffectsContainer& _ActiveGEContainer, FGameplayEffectSpec& _GESpec, FPredictionKey& _PredictionKey) const;

	// ゲームプレイエフェクトが変更されたときに呼ばれる処理
	virtual void OnGameplayEffectChanged() const;

protected:
	// ゲームプレイエフェクトが削除されるときに呼ばれる処理
	virtual void OnActiveGameplayEffectRemoved(const FGameplayEffectRemovalInfo& _RemovalInfo, FActiveGameplayEffectsContainer* _ActiveGEContainer) const;

};
```


```C++:TestGameplayEffectComponent.cpp
#include "./TestGameplayEffectComponent.h"

// 適用可能かどうかを返す
bool UTestGameplayEffect::CanGameplayEffectApply(const FActiveGameplayEffectsContainer& _ActiveGEContainer, const FGameplayEffectSpec& _GESpec) const
{
	return true;
}

// ゲームプレイエフェクトがコンテナに追加されるとき（期間）に呼ばれる処理
bool UTestGameplayEffect::OnActiveGameplayEffectAdded(FActiveGameplayEffectsContainer& _ActiveGEContainer, FActiveGameplayEffect& _ActiveGE) const
{
	UAbilitySystemComponent* _ASC = _ActiveGEContainer.Owner;
	if (!ensure(_ASC)){
		return false;
	}
	
	// We don't allow prediction of expiration (on removed) effects
	// 有効期限（削除時）の影響を予測することは許可されていません
	if (_ActiveGEContainer.IsNetAuthority()){
		// When this ActiveGE gets removed, so will our events so no need to unbind this.
		// この ActiveGE が削除されると、イベントも削除されるため、これをバインド解除する必要はありません。
		_ActiveGE.EventSet.OnEffectRemoved.AddUObject(this, &UTestGameplayEffect::OnActiveGameplayEffectRemoved, &_ActiveGEContainer);
	}

	
	return true;
}

// ゲームプレイエフェクトが実行されるとき（即時or期間の周期ごと）に呼ばれる処理
void UTestGameplayEffect::OnGameplayEffectExecuted(FActiveGameplayEffectsContainer& _ActiveGEContainer, FGameplayEffectSpec& _GESpec, FPredictionKey& _PredictionKey) const
{
}

// ゲームプレイエフェクトが最初に適用されるときに呼ばれる処理
void UTestGameplayEffect::OnGameplayEffectApplied(FActiveGameplayEffectsContainer& _ActiveGEContainer, FGameplayEffectSpec& _GESpec, FPredictionKey& _PredictionKey) const
{
}

// ゲームプレイエフェクトが変更されたときに呼ばれる処理
void UTestGameplayEffect::OnGameplayEffectChanged() const
{
}

// ゲームプレイエフェクトが削除されるときに呼ばれる処理
void UTestGameplayEffect::OnActiveGameplayEffectRemoved(const FGameplayEffectRemovalInfo& _RemovalInfo, FActiveGameplayEffectsContainer* _ActiveGEContainer) const
{
	FScopedActiveGameplayEffectLock ActiveScopeLock(*_ActiveGEContainer);

	const FActiveGameplayEffect* ActiveGE = _RemovalInfo.ActiveEffect;
	if (!ensure(ActiveGE))
	{
		UE_LOG(LogTemp, Error, TEXT("FGameplayEffectRemovalInfo::ActiveEffect が OnActiveGameplayEffectRemoved に設定されていませんでした"));
		return;
	}

	UAbilitySystemComponent* ASC = _ActiveGEContainer->Owner;
	if (!ensure(IsValid(ASC)))
	{
		UE_LOG(LogTemp, Error, TEXT("ActiveGEContainer が OnActiveGameplayEffectRemoved で無効でした"));
		return;
	}

}

```


# まとめ
ゲームプレイエフェクトを調べているとゲームプレイタグの種類がいろいろあってややこしいですね。