【Unreal Engine】EQS入門:候補を比べて、敵の隠れ場所を選ぶ

作成: 2026-07-23最終更新: 2026-09-06

UE5のEQSを、被弾した敵が物陰へ隠れる例で学びます。候補の生成、条件による除外、距離の採点、Contextの作り方を整理し、Testing Pawnでの可視化からBehavior Treeへの接続まで図解します。

追いかけてくる敵はできた。次は、撃たれたら物陰へ逃げてほしい。でも、岩を置き直すたびに「隠れる座標」まで手で設定し直すのは大変です。

EQS(Environment Query System) は、周囲の候補を条件で比べ、今の状況に合う場所を選ぶ仕組みです。「プレイヤーから見えない」「そこまで歩ける」「自分から近い」を組み合わせると、地形に合わせて隠れ先を探せます。

物陰と周囲の候補点から、隠れ場所を選ぶEQSのイメージ

この記事でわかること

  • EQSが「候補を出す・除外する・点数を付ける」で成り立っていること
  • 誰から隠れるかを決めるContextの作り方
  • 見えない・歩ける・近いを組み合わせて隠れ先を選ぶ手順
  • Testing Pane で、なぜその地点が選ばれたかを見る方法

今回は、候補点を目で確認してから、被弾した敵が隠れ先へ移動する処理へつなぎます。銃がなくても、Nキーでダメージを与えて試せます。

  • 候補を作る、除外する、採点する、選ぶ、という4つの段階
  • 「誰から見えないか」を決めるContextの作り方
  • 点の色と数値を見て、設定を一つずつ確かめる方法
  • 隠れ先が見つからない場合も含めたBehavior Treeへの組み込み

Sponsored

「良い場所」の条件を分ける

「見えにくくて、近い場所」を点数だけで決めると、丸見えでも近さで勝ってしまうかもしれません。隠れるために欠かせない条件と、できれば満たしたい条件を分けましょう。

候補を作り、条件に合わない点を除外し、残りを採点して一つ選ぶ
段階EQSの要素今回すること
候補を作るGenerator敵の周囲に格子状の点を作る
条件で除外するTestのFilter見える点、歩いて行けない点を外す
残りを比べるTestのScore敵に近い点ほど高く採点する
一つ選ぶRun Mode最高点の候補を移動先にする

Filterは、候補として残してよいかの判定。Scoreは、残った候補の優先順位です。一つのTestで両方を行うこともできますが、最初は役割を分けると設定を追いやすくなります。

クエリ(Query) は、この「どんな条件で探すか」をまとめたものです。実行するたびに、その時点の周囲を調べます。EQSが選んだだけでは敵は動かず、結果をMove Toへ渡して初めて移動します。

Behavior Treeが「何をするか」、AI Perceptionが「何を見聞きしたか」、EQSが「どの候補を選ぶか」を担当する、と考えるとつながります。

まずは候補点を表示する

実践の土台には、Behavior Tree記事の BP_Enemy、BP_EnemyAIController、BB_Enemy、BT_Enemy を使います。巡回と追跡が動き、NavMeshの上を敵が歩けるところまで用意してください。

テスト用のレベルには広い床と、敵の背丈より高いCubeの壁を2つほど置きます。壁の両端を回り込める幅を空け、壁の前後がNavMeshでつながるようにします。

1. Gridを作る

「Edit」→「Plugins」で Environment Query Editor を検索します。無効なら有効にして、再起動します。

コンテンツブラウザで右クリックし、「Artificial Intelligence」→「Environment Query」を作ります。名前は EQ_HideSpot です。開いたらRootから線を延ばし、Points: Grid を追加します。

項目設定
Generate AroundEnvQueryContext_Querier
Grid Half Size800
Space Between150
Projection DataのTrace ModeNavigation

Querier(クエリア) は、このクエリを実行している側です。敵が実行すれば敵、後で置くTesting Pawnが実行すればそのPawnが基準になります。

Grid Half Sizeは中心から端までの広さ、Space Betweenは点の間隔です。標準の単位はcmなので、ここでは周囲8mまで、約1.5mおきに探します。

投影(Projection) は、作った点をNavMesh上の位置へ合わせる処理です。ただし、別の足場にある点まで歩いて行けるとは限りません。道がつながるかは、後で別に調べます。

中心からの範囲と点の間隔を指定し、格子状の候補をNavMeshへ合わせる

2. 近さだけで採点してみる

Gridを右クリックして「Add Test」→「Distance」を追加します。

項目設定
Test PurposeScore Only
Distance ToEnvQueryContext_Querier
Test ModeDistance 2D
Scoring EquationLinear
Scoring Factor-1.0

Distanceは、基準から候補までの距離を比べます。ここでは平面上の距離を使い、近い点を優先するためにScoring Factorを-1にします。

次にBlueprint Classを作り、「All Classes」で EQSTestingPawn を選びます。名前は BP_EQSTester とし、レベルの床の上へ配置します。

そのPawnを選び、詳細で次のように設定してください。

項目設定
Query TemplateEQ_HideSpot
Querying ModeAll Matching
Draw Labelsオン
Draw Failed Itemsオン

これは、実際の敵の代わりにクエリを試し、候補点を表示するPawnです。Nav Agentの体格も、実際の敵に合う値を使います。

保存してPawnを少し動かすと、その位置を基準に候補と点数が更新されます。この段階では、近くの点ほど好条件になっていれば成功です。+1へ変えると遠くが優先されるので、一度比べてから-1へ戻してみてください。

Testing Pawnの近くを高く採点し、点数の符号を変えると遠くが高くなる比較

表示では、残った候補が赤から緑で採点され、緑ほど好条件です。青は、テストで除外された点です。青の濃淡を得点の高さとして読みません。色だけでなく、Draw Labelsの数値も確かめましょう。

誰から隠れるかを決める

近い場所が選べたので、次は「プレイヤーから見えない」という条件を加えます。そこで必要なのが Context(コンテキスト)=比較の基準となる相手や位置です。

今回は、候補の生成と距離の採点は「自分」、見えるかどうかは「プレイヤー」を基準にします。

候補を作る中心と近さの基準は敵、隠れる相手の基準はプレイヤー

プレイヤーを返すContextを作る

「Player」というContextが最初から用意されているわけではありません。自分のプレイヤーを渡すBlueprintを作ります。

ここでは、編集画面でも試せるよう、プレイヤー用の BP_ThirdPersonCharacterをレベルへ1体だけ配置します。すでに置いてある場合は、その1体を使ってください。その配置したActorの「Auto Possess Player」を Player 0 にし、Playしたらその体を操作できるようにします。Play中のアウトライナでも、このClassのプレイヤーが1体であることを確かめます。

BP_EnemyはプレイヤーBlueprintを複製して作った別Classなので、次の検索には含まれません。子Classとして作った敵を使っている場合は、プレイヤーだけを取得できるClassに置き換えてください。

  1. Blueprint Classの「All Classes」で EnvQueryContext_BlueprintBase を選ぶ。名前は EQC_PlayerContext
  2. 「My Blueprint」の「Override」から Provide Actors Set を開く。
  3. 入口から Get All Actors Of Class、そこからReturn Nodeへ白い実行線をつなぐ。
  4. Actor Classを BP_ThirdPersonCharacter にする。
  5. 「Out Actors」をReturn Nodeの「Resulting Actors Set」へつなぎ、コンパイル・保存する。
Provide Actors Setから、プレイヤーClassのActor一覧を取得して返すBlueprint

Actors Setは、基準にするActorの一覧です。今回は1体だけを置くので、一人のプレイヤーが返ります。Play前にもそのActorが存在するため、Testing Pawnから同じContextを使えます。

Get Player Characterを返す方法もありますが、通常はPlay中に使うものです。編集画面でまだ生成されていないプレイヤーを探しても、基準を渡せません。今回は可視化を先に試すため、配置済みのActorを使っています。

Sponsored

見えない・歩ける・近いを組み合わせる

1. Traceで、壁の向こう側を残す

EQ_HideSpotのGridへ Trace テストを追加します。

項目設定
Test PurposeFilter Only
ContextEQC_PlayerContext
Bool Matchオン
Trace ModeGeometry by Channel
Trace ChannelVisibility
Trace ShapeLine
Trace from Contextオン
Item Height Offset90
Context Height Offset0

Traceは、プレイヤーと候補点の間に線を引き、途中で何かに当たるかを調べます。今回のBool Match=オンは、「線が遮られる点を残す」という意味です。オフにすると、遮られずに見通せる点を残す設定になります。

プレイヤーから壁で遮られる候補を残し、見通せる候補を除外する

線を床すれすれに引くと、段差まで隠れ場所だと判定することがあります。Item Height Offsetの90は、候補側の始点・終点を床から90cm上げる設定です。プレイヤー側はActorの位置をそのまま使います。今回のThird Person Characterでは、足元ではなく体の中心付近が基準です。

壁にはCollisionがあり、「Visibility」をBlockする必要があります。Traceの仕組みを確かめたい場合は、Line Traceの記事も参考になります。

これは、指定した高さの一本の線が遮られるかという簡単な隠れ先の判定です。頭や体全体が完全に隠れることまでは保証しません。最初は背の高い壁を使うと、結果を判断しやすくなります。

2. Pathfindingで、道がつながる点を残す

Gridへ Pathfinding テストを追加します。

項目設定
Test PurposeFilter Only
Test ModePath Exist
ContextEnvQueryContext_Querier
Path from Contextオン
Bool Matchオン
Skip Unreachableオン

Path Existは、そこまでの経路があるかの確認です。NavMesh上の別の島に候補があっても、渡る道がなければ外せます。

NavMesh上でも道がつながらない候補は外し、歩いて行ける候補を残す

最初に作ったDistanceは、Distance ToがQuerier、Scoring Factorが-1のままです。「プレイヤーに近い場所」へ変えないようにしてください。

これでクエリは、次の役割分担になります。テストの実行順はEQSが調整するため、画面の並びをBlueprintの実行線のようには読みません。

要素担当
Grid自分の周囲に候補を作る
Trace:Filter Onlyプレイヤーからの線が遮られる点を残す
Pathfinding:Filter Only自分から歩いて行ける点を残す
Distance:Score Only残った中で、自分に近い点を高くする

3. 壁の配置を変えて確かめる

BP_EQSTesterを壁の手前、プレイヤーを反対側に置き、Query Templateを指定して結果を見ます。近い点でもプレイヤーから丸見えなら除外され、壁で遮られた到達可能な点が残れば、狙った条件になっています。

プレイヤーの位置、壁、Testing Pawnを基準に、候補の除外と得点を読む

図の点数は、近い候補ほど高くなることを示す例です。実際の数値は候補の配置によって変わります。

次はプレイヤーを壁の横へ動かし、Testing Pawnも少し動かして再計算します。さっきまで隠れられた点が除外されるかを見てください。壁を動かした場合はNavMeshの更新も待ちます。

大きなクエリを編集中、表示が重いときはTesting PawnのQuery Templateをいったん外します。範囲を広げる前に、候補の間隔を粗くして試しましょう。

実践:被弾したら隠れ先へ動く

ここまでで「どこに隠れるか」ができました。次は「被弾したら探し、移動して3秒待つ」をBehavior Treeへ組み込みます。隠れ先がない場合や移動に失敗した場合も、その場で3秒待って通常の行動へ戻します。

被弾をきっかけに隠れ先を探して移動し、3秒後に通常の行動へ戻る

1. Blackboardに2つのKeyを追加する

BB_Enemyを開き、次を追加します。

Key用途
NeedsCoverBool隠れる処理を始める合図。初期値はfalse
HideLocationVectorEQSで選んだ移動先

Blackboardは、Behavior Treeが読む共有メモです。NeedsCoverは「隠れる必要があるか」、HideLocationは「どこへ隠れるか」と分けます。Vectorは位置を表すX・Y・Zの組です。

2. 被弾を合図へ変える

BP_Enemyで「Can Be Damaged」がオンになっていることを確認し、Event AnyDamage を追加します。

  1. Get Controllerの出力を、Get Blackboard のTargetへつなぐ。
  2. Get BlackboardのReturn Valueを、Set Value as Bool のTargetへつなぐ。
  3. Set Value as BoolのKey Nameを NeedsCover、Bool Valueをtrueにする。
  4. Event AnyDamageからSet Value as Boolへ、白い実行線をつなぐ。
敵のAnyDamageから、自分のControllerが使うBlackboardのNeedsCoverをtrueにする

Event AnyDamageは、Apply Damageなどでダメージを受け取ったときに呼ばれます。ここではHPを減らす処理と別に、隠れる合図を記録します。すでに同じイベントがある場合は、既存処理へこの更新を追加してください。

3. 最後に合図を戻すTaskを作る

BT_Enemyの「New Task」から、BTTask_BlueprintBase を親にした BTT_EndCover を作ります。

変数 FlagKeyBlackboard Key Selector 型で追加し、Instance Editableをオンにします。これは、Taskを置いた場所で「どのKeyを操作するか」を選ぶための変数です。

Receive Execute AI → Set Blackboard Value as Bool → Finish Execute と白い実行線をつなぎます。SetのKeyにFlagKey、Valueにfalseを指定し、Finish ExecuteのSuccessはオンです。

BTT_EndCoverで指定されたBoolキーをfalseへ戻し、Taskを成功で終える

図のSet BB Boolは、Set Blackboard Value as Boolを短く表記したものです。これで、隠れる処理が終わったあと、NeedsCoverをfalseへ戻せます。Finish Executeは「このTaskは終わった」と木へ知らせるノードです。

4. 隠れる枝を一番左へ置く

BT_Enemyの最上位Selectorへ、新しい Sequence「隠れる」 を追加します。追跡や巡回より優先したいので、既存の枝より左に置きます。

「隠れる」にBlackboard Decoratorを追加します。

項目設定
Blackboard KeyNeedsCover
Key QueryIs Set
Notify ObserverOn Result Change
Observer AbortsLower Priority

BoolのIs Setは、ここではtrueかどうかの判定です。Lower Priorityにより、NeedsCoverがtrueになると、右側で実行していた追跡や巡回を中断して隠れる枝へ入ります。

「隠れる」の子には、左から次の3つを置きます。

  1. Sequence 「隠れ先へ移動」
  2. Wait:Wait Time=3.0、Random Deviation=0
  3. BTT_EndCover:FlagKey=NeedsCover

さらに「隠れ先へ移動」の子へ、Run EQS QueryとMove Toを兄弟として左から並べます。Run EQS Queryの下へMove Toをぶら下げる構造ではありません。

隠れるSequenceの中で位置選びと移動、3秒待機、合図の解除を順番に実行するBehavior Tree

Run EQS Queryは次の設定です。

項目設定
Query TemplateEQ_HideSpot
Run ModeSingle Best Item
Blackboard KeyHideLocation
Update BBOn Fail表示される場合はオン

Single Best Itemで、残った候補の最高点を一つ選びます。Move ToのBlackboard Keyも HideLocation にし、Acceptable Radiusはまず30、Reach Test Includes Agent RadiusとGoal Radiusはオフ、Allow Partial Pathはオフにします。選んだ点の近くまで行くための設定です。

最後に、内側のSequence「隠れ先へ移動」へ Force Success Decoratorを付けます。

EQSで候補が見つからなければ、そのSequenceは失敗し、Move Toへ進みません。移動に失敗した場合も含め、Force Successで外側には成功として返し、次のWaitとBTT_EndCoverを実行させます。隠れることに成功した、という判定ではなく、後始末へ進めるための設定です。

クエリ失敗ならMove Toを飛ばし、成功しても失敗しても3秒待機と合図解除へ進む

これにより、候補がないときにクエリだけを間隔なく繰り返すことを避けられます。以前のHideLocationが残っていても、クエリに失敗した経路ではMove Toへ進みません。

5. Nキーでダメージを与えてみる

銃の代わりに、レベルの敵1体をNキーで攻撃して試します。

  1. レベルでBP_Enemyを選び、Level Blueprintを開く。
  2. グラフを右クリックし、その配置した敵の参照を作る。
  3. NイベントのPressedを Apply Damage へつなぐ。
  4. Damaged Actorに敵の参照、Base Damageに 1 を指定する。その他の入力は、この動作確認では既定のままでよい。
Level BlueprintのNキーから、配置した敵へApply Damageを送る

Blueprintをコンパイルして保存し、Behavior Treeも保存します。Playしてゲーム画面をクリックし、敵とプレイヤーが互いに見える位置でNを押してください。Nを別の操作で使っている場合は、この確認用イベントを空いているキーへ変えます。

試すこと見る結果
Nでダメージを与えるNeedsCoverがtrueになり、隠れる枝へ入る
隠れ先があるHideLocationが更新され、敵がその位置へ移動する
移動できた到着後3秒待ち、追跡や巡回へ戻る
壁をなくして候補をなくすMove Toへ進まず、その場で3秒待って戻る
壁の位置を変えて再度試す次のクエリで隠れ先を選び直す

この例では、隠れている途中の被弾で待ち時間を延長しません。NeedsCoverがすでにtrueの間は、その一回の処理を続けます。

実際の弾がApply Damageを呼ぶようになったら、Nキーの代わりに同じ被弾処理が使えます。

Sponsored

うまくいかないときの見方

まず、Testing Pawnで「候補が選べるか」、次にBehavior Treeで「その結果を使えているか」を分けて見ます。

症状確認するところ
候補点が出ないQuery Template、Gridの範囲、Navigationへの投影、NavMesh、Testing Pawnの位置
Traceを足すと何も残らないContextがActorを返しているか、プレイヤーが1体配置されているか、壁がVisibilityをBlockするか
丸見えの場所が残るBool Match、Traceの高さ、意図しない床や小物に線が当たっていないか
遠い点が選ばれるDistance ToがQuerierか、Scoring Factorが-1か
Previewでは成功、敵は移動できないTesting Pawnと敵の体格、Path Exist、実行時のNavMesh、Move Toの結果
ダメージを受けても枝が変わらないCan Be Damaged、実行中のBlackboardのNeedsCover、枝の左右、Observer Aborts
隠れたあと動かないWaitの時間、BTT_EndCoverのFlagKey、Finish Executeの接続

実行中はAI DebuggingのEQS表示でも、候補の点数や選ばれた点を追えます。既定ではアポストロフィキーでAI Debuggingを開き、テンキー3でEQS表示を切り替えます。キーボード配列や設定でキーが異なる場合は、プロジェクトのGameplay Debugger設定を確認してください。

また、Testing Pawnの結果は、そのPawnの位置から調べた結果です。敵からの距離と比べるときは、Testing Pawnも敵の近くへ置きます。

おまけ:自分のゲームへ広げる

「近い」の意味を変える

今回のDistanceは直線距離です。壁のすぐ向こうでも、大回りしなければ着けない場所があります。

「歩く距離が短い方」を優先するなら、Pathfindingの Path Length で採点する方法があります。まずは直線距離で仕組みをつかみ、実際の移動が不自然なときに比べてみましょう。

候補の作り方と条件を変える

Gridは周囲を面で探す形です。Points: Circleを使えば、中心から一定の距離に候補を並べられます。中心をプレイヤーにすれば、距離を保つ位置を探す土台になります。

Gridで周囲一帯から探す場合と、Circleで相手の周囲の候補を探す場合

どちらも、中心に誰を指定するかを設定します。「Gridは必ず自分、Circleは必ずプレイヤー」と決まっているわけではありません。

方向を条件にしたいときは Dot テストも使えます。二つの向きがどれだけそろっているかを比べるもので、相手の前方や背後を選ぶ設計に役立ちます。

必要になったときに選び直す

今回の隠れ先は、被弾時のクエリで選んだ位置です。走っている間にプレイヤーが横へ回れば、その場所が見えるようになることもあります。

常に隠れ続ける敵へ広げるなら、見つかったときや一定間隔で選び直す仕組みを足します。候補を細かく増やす前に、いつ調べ直す必要があるかを考えましょう。

単純に相手へ向かうだけなら、通常のMove Toで足ります。そのMove ToもNavMeshの経路を使うので、「EQSがないと壁を避けられない」という意味ではありません。EQSは、行き先そのものを複数の候補から選びたいときに使います。

まとめ

EQSでは、候補を作り、欠かせない条件で除外し、残りを採点して選びます。今回なら「見えない」「歩ける」をFilterに、「近い」をScoreに分けました。

壁やプレイヤーを動かすと、選ばれる場所も変わります。まずは点の色と数値でその変化を確かめ、納得できるクエリになってから、敵の移動へつなげてみてください。

参考資料

Unreal Engine このセクションのノート98