「アンデッドにだけ強く効く聖水」を作りたい。敵をスケルトン、ゾンビ、グールと一体ずつ並べて判定していると、新しい敵を足すたびに、聖水の条件も直すことになります。
Gameplay Tag を使うと、「この敵はアンデッドの仲間」と分かる名札を付けられます。聖水は Enemy.Undead という分類を調べるだけ。後から同じ分類の敵を足しても、聖水の判定を増やさずに済みます。
この記事でわかること
- タグの階層を使って、同じ仲間をまとめて判定する方法
- タグの辞書と、敵が実際に持つタグの違い
- Has Tagの「Exact Match」と、Any・Allの使い分け
- アンデッドはHP0、オオカミはHP80になる聖水の実験
この記事はBlueprintだけで試せます。変数を作り、ノードをつなぐ操作が分かれば進められます。C++やGameplay Ability System(GAS)の導入は必要ありません。
種類を列挙する代わりに、分類で調べる
敵の種類ごとに「Skeletonなら特効、Zombieなら特効」と書くと、聖水や魔法のあちこちに同じ分類の判断が散らばります。問題は、敵の種類が多いことだけでなく、同じ仲間かどうかを毎回並べ直していること です。

Gameplay Tagなら、敵へ分類を持たせ、使う側は必要な分類を問い合わせます。「アンデッドに含まれる敵」を増やす作業が、主に敵の設定で済むようになります。
Enum は、用意した候補から一つを選ぶための型です。Enumでも「種族」の項目を別に作ったり、分類する関数を一つにまとめたりする方法はあります。Enumでは分類できない、というわけではありません。
| 表したいこと | 選び方の例 |
|---|---|
| 今は待機・追跡・攻撃のどれか一つ | Enumで現在の状態を選ぶ |
| この敵はアンデッドで、毒状態でもある | Gameplay Tag Containerで複数の性質を持つ |
| オン・オフだけを覚えたい | boolで十分なこともある |
一つの状態に絞る設計は、EnumとSwitchのステートマシンでも扱っています。タグへ置き換える前に、「同時に持ちたい性質なのか」を考えると、役割を分けやすくなります。
Gameplay Tagは、階層のある名札
タグは、.(ドット)で大きな分類から細かな分類へ区切ります。
Enemy.Undead.Skeleton なら、「敵」の中の「アンデッド」の中の「スケルトン」です。左が大きな分類、右へ行くほど具体的 になります。

親の分類で調べると、その下の子タグも一致します。スケルトンへ Enemy.Undead.Skeleton を付ければ、Enemy.Undead でも、さらに広い Enemy でも見つけられます。
ただし、逆向きは成立しません。Enemy.Undead しか分からない相手が、スケルトンなのかゾンビなのかは決められないためです。

Blueprintの Has Tag では、Exact Match をオフにすると親タグを含めて調べ、オンにするとそのタグ自体を持っているかだけを調べます。一致した結果はtrue、不一致はfalseで返ります。今回の聖水は「アンデッド全般」に効かせるため、オフにします。
なお、名前を識別する Name 型にドットを入れても、この親子の照合が自動で付くわけではありません。Gameplay Tagには、タグの辞書と階層を使った判定が用意されています。
タグを辞書へ登録し、変数で使う
タグの準備は、使えるタグを登録する 作業と、敵へ実際に付ける 作業に分かれます。辞書へスケルトンのタグを登録しただけでは、配置した敵にそのタグは付きません。
Project Settingsで登録する
- 「Edit → Project Settings」を開き、「Project」の「GameplayTags」へ進みます。
- 「Import Tags From Config」を有効にします。
- 「Gameplay Tag List」の「Manage Gameplay Tags」を開き、追加用の「+」を押します。
- Nameに
Enemy.Undead.Skeleton、Commentに用途を入力します。SourceはDefaultGameplayTags.iniを選び、「Add New Tag」で 確定します。 - 同じ手順で
Enemy.Undead.ZombieとEnemy.Beast.Wolfを登録します。

長い名前を最初から入力すれば、EnemyやEnemy.Undeadといった中間の階層も作られます。ツリーを開いて、3つの末端タグがそれぞれの場所にあるか確かめてください。
タグ一つと、タグの集合
Blueprintの変数には、次の型を使います。
| 型 | 持てるもの | 今回の用途 |
|---|---|---|
| Gameplay Tag | タグ一つ | 聖水が調べたい Enemy.Undead |
| Gameplay Tag Container | 複数のタグをまとめた集合 | 敵の分類や状態 |
Container(コンテナ) は、タグをまとめて入れる入れ物です。たとえば一体の敵に、Enemy.Undead.Skeleton と Status.Debuff.Poison を同時に持たせられます。Gameplay Tag型の変数を配列にする操作とは別で、「Gameplay Tag Container」という型を選びます。
ActorのDetailsに最初からある「Tags」とも別です。あちらはName型のActor Tagsで、「Actor Has Tag」で調べます。この記事では、自作のEnemyTags変数を Has Tag へ渡します。
保存先はSourceで決まる
今回の登録先は Config/DefaultGameplayTags.ini です。たとえば、次のような一覧が保存されます。
[/Script/GameplayTags.GameplayTagsSettings]
+GameplayTagList=(Tag="Enemy.Undead.Skeleton",DevComment="スケルトン")
+GameplayTagList=(Tag="Enemy.Undead.Zombie",DevComment="ゾンビ")
+GameplayTagList=(Tag="Enemy.Beast.Wolf",DevComment="オオカミ")
機能ごとに分けるなら、GameplayTagsの設定からSourceを追加し、Config/Tags 内の別ファイルへ登録できます。直接iniを編集した場合は、読み直すためにエディタを再起動します。まずは設定画面から登録すれば十分です。
Has Tag・Any・All・Matches Tagの違い
敵が Enemy.Undead.Skeleton と Status.Debuff.Poison を持っている例で考えます。以下は、すべて Exact Matchがオフ の場合です。
| ノード | 質問 | 結果 |
|---|---|---|
| Has Tag | Enemy.Undeadを持つ? | true |
| Has Tag | Enemy.Beastを持つ? | false |
| Has Any Tags | Enemy.UndeadかEnemy.Beastの、どちらかを持つ? | true |
| Has All Tags | Enemy.UndeadとStatus.Debuffの、両方を持つ? | true |
| Has All Tags | Enemy.UndeadとImmune.Fireの、両方を持つ? | false |
Anyは「どれか」、Allは「すべて」 です。Has Any TagsとHas All Tagsでは、調べられる側をTag Containerへ、条件のタグ集合をOther Containerへ渡します。
一つのタグ同士を比べる場合は Matches Tag です。Tag Oneに相手のタグ、Tag Twoに調べたい分類を渡します。Tag One=Enemy.Undead.Skeleton、Tag Two=Enemy.Undead ならtrueですが、逆にするとfalseです。

「毒状態のアンデッドだけに効く」のように条件を増やしたくなったら、Has All Tagsが候補になります。毒や炎耐性のタグも試す場合は、使う名前を先に辞書へ登録してください。次の実践では、登録済みの敵のタグとHas Tag一つで、アンデッドかどうかを確かめます。
実践の準備:3種類の敵を並べる
Third PersonのBlueprintプロジェクトで、スケルトン・ゾンビ・オオカミに見立てた箱を並べます。聖水を使うと、アンデッドには100ダメージ、それ以外には20ダメージを与えます。通常の5倍に当たる特効です。
今回はタグの判定を見やすくするため、Hキーでレベル内の練習用の敵へ一度ずつ作用させます。投げる動作や距離による判定は、基本が動いてから足す部分とします。

敵の親クラスを作る
コンテンツブラウザから「Blueprint Class → Actor」を作り、BP_EnemyBase と名付けます。Componentsの「Add」からStatic Meshを追加し、Body と名前を付けます。
BodyのStatic Meshには、エンジンの基本形状 Cube を指定します。コンテンツの表示設定で「Show Engine Content」を有効にすると探せます。Collision Presetsは NoCollision にします。今回は接触ではなく、敵のイベントを呼んでダメージを与えるため、コリジョンは不要です。
次の変数を作り、Compileして既定値を設定します。
| 変数 | 型 | 既定値 |
|---|---|---|
| EnemyTags | Gameplay Tag Container | 空 |
| EnemyName | Text | Enemy |
| Health | Float | 100.0 |

EnemyTagsとEnemyNameは、子BPのClass Defaultsで変更します。まずは個体ごとの上書きを作らず、Instance Editableはオフで進めます。
子クラスに、それぞれのタグを付ける
BP_EnemyBaseを右クリックし、「Create Child Blueprint Class」で3つの子BPを作ります。各BPを開き、「Class Defaults」で次の値を設定してください。
| 子BP | EnemyTagsへ選ぶタグ | EnemyName |
|---|---|---|
| BP_Skeleton | Enemy.Undead.Skeleton | Skeleton |
| BP_Zombie | Enemy.Undead.Zombie | Zombie |
| BP_Wolf | Enemy.Beast.Wolf | Wolf |
EnemyTagsの編集欄を開き、辞書のツリーから該当する末端タグを選びます。親タグを別途追加する必要はありません。たとえばBP_Skeletonには、Skeletonのタグ一つだけを入れます。
3体をレベルに置き、Player Startの前へ、左からSkeleton・Zombie・Wolfの順に間隔を空けて並べます。レベルへ置くのは子BPで、タグが空のBP_EnemyBaseそのものではありません。
敵にHPを減らして表示する処理を作る
BP_EnemyBaseのEvent GraphにCustom Eventを追加し、ApplyHolyDamage と名付けます。Custom Event は、自分で名前を付けて呼べる処理の入口です。今回は聖水から呼び出し、受け取ったダメージで敵のHPを減らします。
イベントを選び、DetailsのInputsへ入力を一つ追加します。名前を Damage、型をFloatにしてCompileしてください。このイベントを子BPが引き継ぎます。
新しいHPを計算する
HealthのGetとDamageを減算ノードへ入れます。上の入力がHealth、下の入力がDamage です。結果をClamp (Float)へ渡し、Min=0.0、Max=100.0にします。

Clampは、指定範囲より小さければ下限、大きければ上限に収める処理です。残りHPが20のときに100ダメージを受けても、結果は-80ではなく0になります。
ApplyHolyDamageの白い実行線をSet Healthへつなぎ、計算した値をSet Healthへ入れます。その後にPrint Stringを実行します。

図の M はDamageの値、N はClampの結果、L は次に作る表示文字列の目印です。実際は同じグラフで線をつなぎます。記号そのもののノードは作りません。
どの敵のHPか分かるように表示する
Format Textノードを作り、Formatに {Enemy}: HP {HP} と入力します。追加されたEnemyピンにはEnemyNameのGet、HPピンにはHealthのGetをつなぎます。

Format Textは、波括弧の場所へ値を埋めるノードです。出力を To String (Text) で変換し、Print StringのIn Stringへつなぎます。Duration=5.0にし、Print to ScreenとPrint to Logを有効にします。
Set Healthの後にPrint Stringが動くので、Skeleton: HP 0 のように更新後の値が読まれます。この練習では、HP0の箱も残して数字を比べます。
聖水で敵のタグを調べる
ActorのBPをもう一つ作り、BP_HolyWater と名付けます。変数 TargetTag をGameplay Tag型で作り、Compile後の既定値に Enemy.Undead を選びます。Instance Editableをオンにして、配置した聖水でも対象を変えられるようにします。
Event Graphへ、引数のないCustom Event UseHolyWater を作ります。
敵を一体ずつ取り出す
UseHolyWater → Get All Actors of Class → For Each Loop の白い線をつなぎます。Actor ClassはBP_EnemyBaseです。Out Actorsの配列をFor Each LoopのArrayへ渡します。

配列 は、見つけた敵を並べた一覧です。For Each Loop は、その一覧から一体ずつ取り出して処理するノードです。BP_EnemyBaseの子であるSkeleton・Zombie・Wolfも集まります。
図の A はLoop Bodyの実行線、B はArray Elementの参照です。参照 は、レベルにいるどの個体を扱うか指定するものです。Bが「今回調べて、ダメージを送る一体」を表します。CompletedとArray Indexは今回は使いません。
その敵がアンデッドか調べる
Array Elementからドラッグして、EnemyTagsのGetを作ります。Targetに、その敵の参照がつながります。EnemyTagsの出力から Has Tag を作り、TargetTag変数をTagへ渡します。Exact Matchのチェックを外してください。

Has Tagの結果は bool です。trueなら条件に一致、falseなら不一致を表します。この赤い結果をBranchのConditionへつなぎ、Loop Bodyの白い線をBranchの実行入力へつなぎます。

図の C はHas Tagの結果、D はBranchのTrue側、E はFalse側です。Has Tagは値を返すノードなので、白い実行線を通しません。
一致した敵には100、それ以外には20を送る
True側とFalse側に、Apply Holy Damageを呼ぶノードを一つずつ置きます。両方のTargetに、同じArray Elementの参照をつなぎます。True側のDamageは100.0、False側は20.0です。

呼び出しノードは、Array Elementからドラッグして Apply Holy Damage を検索すると作れます。敵側で定義したCustom Eventを呼ぶ操作です。BP_HolyWaterに同名のCustom Eventを新 しく作る必要はありません。
これで、聖水は敵の名前を一つも列挙せず、Enemy.Undeadに含まれるかで威力を選べます。敵側は渡されたDamageでHPを減らすだけです。
Hキーで使い、結果を確かめる
BP_HolyWaterをレベルへ一つ置きます。見た目はなくて構いません。今回はレベル内の練習用の敵すべてが対象なので、置く位置で威力は変わりません。
OutlinerでそのBP_HolyWaterを選択し、レベルエディタの「Blueprints → Open Level Blueprint」を開きます。右クリックの「Create a Reference to BP_HolyWater」か、Outlinerからのドラッグで、配置した個体への参照を作ります。
Hキーのイベントを追加し、PressedからUse Holy Waterを呼びます。TargetへBP_HolyWaterの参照をつなぎます。Released側はつなぎません。

各BPをCompileして保存し、Playします。ゲーム画面をクリックしてから、Hを 一度だけ 押してください。
| 敵 | 特効に一致するか | ダメージ | 残りHP |
|---|---|---|---|
| Skeleton | true | 100 | 0 |
| Zombie | true | 100 | 0 |
| Wolf | false | 20 | 80 |
敵を取り出す順番は固定ではないため、ログの並び順ではなく敵名と値を見ます。画面の文字が消えたら、Output Logで読み返せます。
Hをもう一度押すと、Wolfは60になります。比較実験のたびにStopしてPlayし直せば、全員のHPが100から始まります。
| 結果がおかしい場合 | 確認する場所 |
|---|---|
| ログが一つも出ない | HのPressed、配置した聖水の参照、UseHolyWaterの白線、Actor Class |
| 全員がHP80 | 子BPのEnemyTags、TargetTag=Enemy.Undead、Exact Matchがオフか |
| 全員がHP0 | TargetTagが広すぎるEnemyになっていないか、False側のDamage=20か |
| 同じ敵だけ何度も減る | EnemyTagsのTargetとApply Holy DamageのTargetが、両方Array Elementか |
| 表示が減る前のHP | Set HealthからPrint Stringへ、順に実行しているか |
新しい敵と、別の特効を試す
聖水を変更せず、レイスを足す
辞書に Enemy.Undead.Wraith を追加します。BP_EnemyBaseの子として BP_Wraith を作り、EnemyTagsへそのタグ、EnemyNameへWraithを設定します。レベルに箱を一つ足し、Playし直してHを押します。
WraithもHP0になれば成功です。BP_HolyWaterは、最初から「BP_EnemyBaseの仲間を集め、Enemy.Undeadで調べる」形なので、新しい種類を聖水へ書き足す必要がありません。
対象のタグだけを変える
次はStopし、配置したBP_HolyWaterのTargetTagを変えて試します。毎回Playし直し、Hを一度押します。
| TargetTag | Skeleton | Zombie | Wolf |
|---|---|---|---|
| Enemy.Undead | HP0 | HP0 | HP80 |
| Enemy.Beast | HP80 | HP80 | HP0 |
| Enemy.Undead.Skeleton | HP0 | HP80 | HP80 |
親タグで問うと広い仲間に、細かなタグで問うと狭い分類に効きます。「敵が持つタグ」と「攻撃が調べるタグ」を別にしたため、同じBPを別の特効として使えます。

おまけ:先に知っておくと良いこと
タグを付けるだけで、効果が発生するわけではない
毒のタグを付けても、自動でHPが減るわけではありません。タグは状態を表すデータで、その状態のとき何をするかは処理で決めます。
実行中に付け外しするなら、EnemyTagsへAdd Gameplay TagやRemove Gameplay Tagを使います。ただし、複数の毒が重なったときに、一つが切れただけでタグを消してよいかは別の問題です。タグの有無だけで、重なった回数や残り時間までは管理されません。
空の条件でAllを呼ぶとtrueになる
Has All TagsのOther Containerが空なら、「必要なタグが一つもない」ためtrueになります。Has Any Tagsでは、見つける候補がないのでfalseです。条件をデータから読み込む場合は、空を許すかも決めておきます。
名前変更やC++定義も、使っている場所を確認する
登録済みタグの名前は、管理画面から変更できます。変更後は、参照するアセットや保存済みデータも確認します。コード中に文字列で書いたタグ名まで、自動ですべて直るとは考えないでください。
C++なら、NativeGameplayTags.hのマクロでタグを定義し、コードから変数として参照する方法があります。変数名の誤りはコンパイルで見つけられますが、定義に書くタグ文字列の意味や綴りまで保証されるわけではありません。今回のBPによる実践では不要です。
攻撃範囲やスキルへ広げる
今回のGet All Actors of Classは、タグの違いを比べるための取得方法です。範囲攻撃へ広げるときは、当たり判定やLine Traceなどで対象を選び、その相手のEnemyTagsを同じように調べます。タグはコリジョンの設定を変えるものではありません。
複雑な能力や効果を組み合わせたい場合は、Gameplay Ability Systemでもタグが使われます。まずは今回のように、どの分類を調べると何が変わるかを、小さな実験で確かめておくと理解しやすくなります。
タグの階層を作るときは、「まとめて扱いたい分類があるか」を基準にしてみてください。見た目や材質などの情報をすべて長い名前へ詰めるより、実際に判定したいまとまりに絞ると、使う側の処理も読みやすくなります。
まとめ
- タグは階層で持てるので、
Enemy.Undeadのようにまとめて判定できる - 辞書(登録したタグ)と、実際に持たせたタグは別物
- Exact Matchを外すと、下の階層もまとめて拾える
- Any・Allで「どれか1つ」と「すべて」を分ける
設計するときの問いかけは、「この判定は、あとで仲間が増えるか」 です。増えるならタグの階層で受けておきます。
データ側で持たせるなら Data Asset、能力として組むなら GAS入門 へ進んでください。