UE5 GameplayCueTranslator を使ってみる

# 概要
UnrealEngine5 の GameplayCue の機能 GameplayCueTranslator を使ってみたメモ書きです。
GameplayCueTranslator はゲームプレイキュー呼び出し時のタグを条件を設定して翻訳する機能です。


## 更新履歴
| 日付 | 内容 |
|:-----|:----|
|2025/03/14| 初版 |

## 参考
以下の記事を参考にいたしました、ありがとうございます。
[GASDocumentation](https://github.com/tranek/GASDocumentation)
[第15回ぷちコン「たぬ吉の大冒険」振り返りその4 GameplayCue編](https://wvigler.hatenablog.com/entry/2021/05/10/103406)

## 関連する過去記事
[UE4 GameplayAbility Pluginについてのメモ](https://qiita.com/unknown_ds/items/6d4438646827bbe08f4a)
[UE5 GameplayEffectの適用に関するメモ](https://qiita.com/unknown_ds/items/93f1f51d0009856c85e4)


## 環境
Windows10
Visual Studio 2022
UnrealEngine 5.5.1

## 関連ソース
"Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Public\GameplayCueTranslator.h"
"Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Private\GameplayCueTranslator.cpp"


# GameplayCueについて
GameplayCue は GameplayAbilitySystem での機能の１つで主にエフェクトやサウンド等を管理するシステムです。UEの通信同期にも対応をしているようです。

## GameplayCueTranslator について
GameplayCueTranslator とは、通常ゲームプレイキューはゲームプレイタグを指定して再生をしますが、このゲームプレイタグを条件を見て差し替える機能です。
例えば、`GameplayCue.Chara.UseItem`というゲームプレイタグでキューを指定するとして、この`Chara`の部分を条件を見て `Hero` や `Minion` といった文字列に変換することができます。結果として `GameplayCue.Hero.UseItem` や `GameplayCue.Minion.UseItem` というゲームプレイキューを指定することができます。



# GameplayCueTranslatorを使ってみる

## 実装準備
### GameplayCueで使用するタグを設定する
ゲームプレイキューで使用するタグは `GameplayCue`のサブタグである必要があります。
例えば以下のような設定をします。

![GameplayCueTag.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/37cc027f-4940-bf75-8949-7ac184ba9530.png)

`Any` になっているところを `Fire`や`Ice`に差し替えるのを目指します。


### UGameplayCueTranslator を実装する
`UGameplayCueTranslator`を継承したクラスを作成します。`GetTranslationNameSpawns` と `GameplayCueToTranslationIndex` のオーバーライドが必要です。
`GetTranslationNameSpawns` では変換するための文字列の定義を用意します。
`GameplayCueToTranslationIndex` では引数でゲームプレイキュー実行時のターゲットアクターとゲームプレイキューパラメータが入ってきますのでこれを見て先に定義した差し替え定義のインデックスを返します。
以下、サンプルコード。

```cpp:MyGameplayCueTranslator.h
#include "GameplayCueManager.h"

UCLASS()
class UMyGameplayCueTranslator00 : public UGameplayCueTranslator
{
	GENERATED_BODY()

public:

	// ネームスワップ定義
	virtual void GetTranslationNameSpawns(TArray<FGameplayCueTranslationNameSwap>& SwapList) const override
	{
		{  // 0
			FGameplayCueTranslationNameSwap _Temp;
			_Temp.FromName = FName(TEXT("Any"));
			_Temp.ToNames.Add( FName(TEXT("Fire")) );
			SwapList.Add(_Temp);
		}
		{  // 1
			FGameplayCueTranslationNameSwap _Temp;
			_Temp.FromName = FName(TEXT("Any"));
			_Temp.ToNames.Add( FName(TEXT("Ice")) );
			SwapList.Add(_Temp);
		}
		{	// 2
			FGameplayCueTranslationNameSwap _Temp;
			_Temp.FromName = FName(TEXT("Any"));
			_Temp.ToNames.Add( FName(TEXT("Wind")) );
			SwapList.Add(_Temp);
		}

	}

	// 適用するスワップ定義をインデックスで返す
	virtual int32 GameplayCueToTranslationIndex(const FName& _TagName, AActor* _TargetActor, const FGameplayCueParameters& _Parameters) const
	{
    	// パラメータの AbilityLevel を見て差し替えインデックスを返す
		switch(_Parameters.AbilityLevel) {
		case 1: return(0);
		case 2: return(1);
		case 3: return(2);
		}
		
		return(INDEX_NONE);
	}
};
```

これで準備ができました。`GameplayCueManager` が勝手に初期化時にUGameplayCueTranslatorを基に変換用ルックアップテーブルを作成します。
もし不要になる場合などは `IsEnable()` を実装して `false`を返すことで除外されます。

```cpp:MyGameplayCueTranslator.h
// 使用可能かどうか。
virtual bool IsEnabled() const override { return(false); }

```



## 実行テスト
ゲームプレイキューを実行するには `GameplayAbilitysystemComponent::ExecuteGameplayCue` 等を使用しますが、タグの差し替えを確認するだけの場合は、`FGameplayCueTranslationManager::TranslateTag` を使って確認するのがお手軽です。


```cpp:MyActor.cpp
#include "AbilitySystemGlobals.h"
#include "GameplayCueManager.h"

// ゲームプレイキューを呼びだす
FGameplayCueParameters	_Params;
_Params.Instigator = this;
_Params.EffectCauser = this;
_Params.Location = this->GetActorLocation();
_Params.Normal = FVector::ZeroVector;
_Params.AbilityLevel = 1;		// ←この値でタグを差し替える
FGameplayTag _Tag = FGameplayTag::RequestGameplayTag(FName("GameplayCue.DamageToken.Any"));

// ゲームプレイキューを実行する
// GetAbilitySystemComponent()->ExecuteGameplayCue(_Tag, _Params);

// 差し替えの確認用デバッグコード
auto _GCM = UAbilitySystemGlobals::Get().GetGameplayCueManager();
if (_GCM) {
	auto _SrcTag = _Tag;
	_GCM->TranslationManager.TranslateTag(_Tag, this, _Params);
	UE_LOG(LogTemp, Log, TEXT("Tanslate : %s -> %s"), *_SrcTag.ToString(), *_Tag.ToString());
}

```


## デバッグ用ツール

### コンソールコマンド
`GameplayCue.PrintGameplayCueTranslator`とコンソールコマンド実行することで変換のルックアップテーブルを確認することができます。

```terminal:コンソールコマンド
GameplayCue.PrintGameplayCueTranslator
```

### GameplayCueエディタ
UEエディタの [ツール]->[GameplayCueエディタ]からゲームプレイキュータグの一覧を確認することができます。この時に変換されるタグも表示されます。

![GameplayCueEditor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/8d07ea3c-4654-bd8c-6df1-f9f3879fdec5.png)

変換名を表示するには `FGameplayCueTranslationNameSwap` のエディタ専用データに名前を設定すれば表示されます。

```cpp:MyGameplayCueTranslator
	FGameplayCueTranslationNameSwap _Temp;
	_Temp.FromName = FName(TEXT("Any"));
	_Temp.ToNames.Add( FName(TEXT("Fire")) );
#if WITH_EDITOR
	// ゲームプレイキューエディタでの表示名の設定
	_Temp.EditorData.EditorDescription = FName(TEXT("ToFire"));
#endif
	SwapList.Add(_Temp);

```

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303806/79a20b77-7e2c-4200-9fd0-04e915117b48.png">



# まとめ
状況に応じたゲームプレイキューの差し替えのコードを一元化できるので少し便利な機能だとは思いますが、積極的に使うケースは少ないような気がします。