【UE5】GameplayTagの使い方
プログラマーの尾関です。今回はGameplayTagの使い方を解説します。
GameplayTagとは
GameplayTagと通常のTagの違いは、大まかに以下の図のようになります。
例えばアクターの詳細から設定できる “Tag” は、すべてが同列に存在するユニークな文字のリストです。
れに対してGameplayTagでは、Tagに階層構造を持たせることができます。
このようにカテゴリ分けしたタグを渡すことができるため、enumよりも柔軟なデータ構造にできます。
BPから渡すときもこのように一覧表示されるので選びやすいです。
GameplayTagを使用するメリット・デメリット
GameplayTagを使用するメリットは以下のとおりです。
メリット | 説明 |
タグの選択・入力が簡易化される | タグを指定する際に「リストから選択するだけ」なので入力が楽 |
入力ミスを防ぐ | タグ名を一元管理できるので「タグ名の打ち間違えによるエラーを防ぐ」ことができる |
階層化・カテゴリ分けできる | タグを階層化して「カテゴリ分けや管理がしやすく」なる |
デメリットとしては「タグの階層をどのように定義すべきかを決める必要がある」という問題があります。また C++ からアクセスする場合には、結局文字列指定となるので事前にしっかりと考えて決める必要があります。
- さらに、C++上での文字列からGameplayTagへの変換は必ず FGameplayTag::RequestGameplayTag() を通すこととなり、変換できなかった (存在しないタグ名を指定した) 際にはエラーを返すので、タイプミスはある程度防ぐことができます。
GameplayTagを使用するには
GameplayTagは標準でエディタ上では有効となっています。
プロジェクト設定を開いて、プロジェクト > GameplayTagsから設定できます。
デフォルトだと GameplayTags は何も設定されていないので「新しいゲームプレイタグを追加」をクリックしてタグ名を指定し、「新しいタグを追加」を選びます。
なおタグ名は “.” (ピリオド) で階層を区切ります。
するとタグが階層構造で作成されました。
GameplayTagの保存先
なお、この設定はデフォルトだと “DefaultGameplayTags.ini” というファイルに書き出されます。
[/Script/GameplayTags.GameplayTagsSettings]
ImportTagsFromConfig=True
WarnOnInvalidTags=True
ClearInvalidTags=False
AllowEditorTagUnloading=True
AllowGameTagUnloading=False
FastReplication=False
InvalidTagCharacters="\"\',"
NumBitsForContainerSize=6
NetIndexFirstBitSegment=16
+GameplayTagList=(Tag="Delegate.Battle.Begin",DevComment="")
保存先は「新しいゲームプレイタグを追加」のところの「ソース」に記載されていて、もし新しく GameplayTagsのソースを定義したい場合には「新しいタグソースを追加」のところから追加します。
BPでGameplayTagを使用する
動作の確認用として、BPの引数に Gameplay Tag を指定してみます。
すると Gameplay Tagを引数として渡せるようになりました。
C++でGameplayTagsを使用する
C++でGameplayTagsを使うには *.Build.cs の PublicDependencyModuleNames.AddRange()に “GameplayTags” の定義が必要となります。
public class TestInput : ModuleRules
{
public TestInput(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] {
"Core"
, "CoreUObject"
, "Engine"
, "InputCore"
, "HeadMountedDisplay"
, "EnhancedInput"
, "GameplayTags" // ←これを追加する
});
...
“GameplayTagContainer.h” を #includeする
FGameplayTagを使用するには “GameplayTagContainer.h” を #includeする必要があります。
プログラム内部でのTagの扱い
プログラム内部でのTagの扱いは以下のような違いがあります。
タグの種類 | データ型 | 基本的な使い方 |
・Actor tags ・Component tags | Name型配列のプロパティ (TArray<FName> Tags) | TArray の関数で指定のタグを含んでいるかどうかをチェックする。 ・bool Contains([FName]): 指定のタグ名を含んでいるかどうか |
Gameplay Tags | FGameplayTag構造体 | ・行構造にFGameplayTagを定義してデータを紐づけるキーとして使用する ・FGameplayTagを TMap のキーとして登録するとFGameplayTagを指定するだけでデータが取得できるようになる ・BlueprintCallableの引数に FGameplayTag を指定させてBPからの呼び出し処理を分岐する |
C++上にタグ名を定数として定義する
C++でタグ名を指定する場合は文字列となりますが、UE_DEFINE_GAMEPLAY_TAG_STATIC() で定義すると、最初の引数が定数として定義されるので複数の場所で参照する場合に便利です。
// タグを定数として定義する
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_ABC, TEXT("A.B.C"));
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_ANIMATION_IDLE, TEXT("Animation.Idle"));
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_ANIMATION_RUN, TEXT("Animation.Run"));
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_STATE_POISON, TEXT("State.Poison"));
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_STATE_SLEEP, TEXT("State.Sleep"));
使うときは以下の通りです。
// 待機アニメーションタグを登録する.
ComputedTags.AddTag(TAG_ANIMATION_IDLE);
GameplayTagsの運用
ゲーム内での使用例
機能 | タグ名 | 説明 |
色の定義 | Color | 基本カラー、共通カラー。データテーブル でタグに対応する色を設定するなど |
デバッグメニューの分類 | DebugMenu | Layer(モーダルなど表示の種類)、機能ごとのカテゴリ |
イベントディスパッチャーの分岐 | Delegate | バトル・会話イベント・宿屋・マップ・プレイヤーなどの状態や画面などのイベントディスパッチャーの種別を分岐するために使用 |
デバイス入力の分類 | Input | Gamepad, MouseAndKeyboard, Touch |
プラットフォームの分類 | Platform | |
UIの分類 | UI | Action(押下時の挙動), Common(見た目のスタイル), Dialog(ダイアログの種類), Layer(用途・機能), Layout(UIグループ) |
AActorやUObjectのプロパティにする
リスト選択となるので、Actor tags を使うよりもタグ付けがしやすくなります。
複数のタグ指定:TArray<GameplayTag>
例えばActorのメンバ変数などに TArray<GameplayTag> を定義して、EditAnywhereにすると、エディタ上で GameplayTag を複数設定できます。タグを複数指定したいときに便利です。
複数のタグ指定2:GameplayTagContainer
TArrayの方が使いやすいので “GameplayTagContainer” は基本的に使いません。ただし子孫タグの判定がしたいときには使ってもよいかもしれない…。
タグをキーにする:TMap<GameplayTag, データ>
Actorのメンバ変数などに TMap<GameplayTag, データ> を定義して、EditAnywhereすると、エディタ上でGameplayTagをキーにデータを設定できるようになります。
データテーブルのキーにする
行構造に GameplayTag を定義すると、そのデータを取得するためのキーとして使用できます。コンパイル不要でデータ種別(タグ)を増やせるのが便利です。
選択可能なタグの絞り込みを可能にする
行構造の UPROPERTY() の meta に Categories を指定すると、エディタ上で選択可能なタグがその階層以下のみとなります。
USTRUCT(BlueprintType)
struct FDialogBoxInfo : public FTableRowBase {
GENERATED_BODY()
public:
// ダイアログの種類(タグ) "UI.Dialog.Box"以下のタグのみ選択可能にする.
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "UI.Dialog.Box"))
FGameplayTag DialogTag;
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (MultiLine = "true"))
FText Message;
// ダイアログの選択肢の種類(タグ) "UI.Dialog.Choice"以下のタグのみ選択可能にする.
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "UI.Dialog.Choice"))
FGameplayTag ChoiceTag;
public:
virtual FString GetRefName()override { return DialogTag.ToString(); }
};
FGameplayTagの関数・ノード
GameplayTag と文字列を相互に変換する
BPでの変換 [FGameplayTag→FNameに変換]
BP の場合は “Get Tag Name” ノードで FGameplayTag を FName型 (文字列) に変換することができます。
- BPには「文字列→FGameplayTag」に変換するノードが存在しないため、デバッグ用などで 文字列を変換したい場合には C++ で実装する必要があります。
C++での相互変換
C++上では基本的に文字列となるので、FGameplayTagへの変換を行うには FGameplayTag::RequestGameplayTag(FName& TagName, bool ErrorIfNotFound=true) を使用します。
// 文字列から FGameplayTag に変換する
FGameplayTag UCPP_FGameplayTag::FStringToTag(FString InTagName)
{
FGameplayTag tag;
tag.RequestGameplayTag(FName(InTagName));
return tag;
// static関数でも同じことができます.
//return FGameplayTag::RequestGameplayTag(FName(InTagName)));
}
なお、第二引数に false を指定するとタグ文字が間違ってもエラーを返さなくなります(デフォルト true)。
文字列への変換をする場合、FGameplayTagには GetTagName() で文字列表現の FName を取得できます。
タグ同士の比較
“==” を使用する場合
タグが完全に一致する場合のみ true を返します。
[Match Tag]ノードを使用する場合
Match Tag は以下の条件が成立した場合のみtrueを返します
- 「Tag One」が「Tag Two」と同じタグ
- 「Tag One」が「Tag Two」の子孫タグとなる
この画像では One が “Delegate.Battle.Begin” で Twoが “Delegate.Battle” となり「OneはTwoの子」になるので、true を返します。
逆に「親が異なる」場合には false を返します。
[Match Any]ノードを使用する場合
Match Any は 複数のタグを指定して、OR条件 (いずれかの一致) で true を返すノードとなります。
GameplayTagContainer
GameplayTagContainerは、GameplayTagを複数保持できるデータ型です。
TArray::Contains() だと完全一致のみの判定となりますが、GameplayTagContainerだと HasTag() で子孫タグかどうかを判定できます。
参考
- UE4 GameplayTagについてのメモ
- UnrealEngine GamePlayTagを使おう!
- UE4 GameplayTag Advent Calendar 2019: GameplayTagsの使い方を集めたページ