[UE5] EQS 101: Compare Candidates and Choose an Enemy's Hiding Spot

Created: 2026-07-23Last updated: 2026-09-06

Learn UE5's EQS through an enemy taking cover after being hit. Organizes candidate generation, filtering by condition, scoring by distance, and building a Context, illustrating visualization with the Testing Pawn through hooking it into a Behavior Tree.

The enemy that chases you works. Next you want it to flee to cover when shot. Reconfiguring "hiding coordinates" by hand every time you move a rock is a chore, though.

EQS (Environment Query System) is a mechanism that compares nearby candidates by condition and picks a location fitting the current situation. Combining "not visible to the player", "walkable from here", and "close to me" finds cover that adapts to the terrain.

EQS choosing a hiding spot from cover and the surrounding candidate points

What You'll Learn

  • That EQS consists of "generate candidates, exclude them, and score them"
  • How to build a Context deciding who you hide from
  • Combining not visible, walkable, and close to choose cover
  • Seeing why a spot was chosen in the Testing Pane

We confirm candidate points visually, then connect to logic moving a hit enemy to cover. Even without a gun, the N key deals damage so you can try it.

  • Four stages: generate candidates, exclude, score, and choose
  • How to build the Context deciding "who you are hidden from"
  • Confirming settings one at a time by reading dot colors and numbers
  • Integrating into a Behavior Tree, including when no cover is found

Sponsored

Separate what makes a spot "good"

Deciding "hidden and close" by score alone may let a fully exposed spot win on closeness. Separate the conditions that are essential for hiding from those you would merely like to satisfy.

Generating candidates, excluding those that fail conditions, scoring the rest, and picking one
StageEQS elementWhat we do here
Generate candidatesGeneratorCreate a grid of points around the enemy
Exclude by conditionTest's FilterDrop visible points and points you cannot walk to
Compare the restTest's ScoreScore points closer to the enemy higher
Pick oneRun ModeMake the highest-scoring candidate the destination

A Filter decides whether a candidate may remain, and a Score sets the priority among those that do. One Test can do both, but separating the roles at first makes settings easier to follow.

A query bundles up "what conditions to search by". Every execution examines the surroundings at that moment. EQS choosing alone does not move the enemy; passing the result to Move To is what makes it move.

Thinking of Behavior Tree as "what to do", AI Perception as "what was seen or heard", and EQS as "which candidate to choose" ties them together.

Display candidate points first

The foundation for the exercise is BP_Enemy, BP_EnemyAIController, BB_Enemy, and BT_Enemy from the Behavior Tree article. Get patrolling and chasing working with the enemy walking on the NavMesh.

For the test level, place a wide floor and two Cube walls taller than the enemy. Leave room to go around each end and ensure the NavMesh connects in front of and behind them.

1. Create a Grid

Search for Environment Query Editor in "Edit → Plugins". Enable it if it is off and restart.

Right-click in the Content Browser and create "Artificial Intelligence → Environment Query". Name it EQ_HideSpot . Once open, drag from Root and add Points: Grid .

ItemSetting
Generate AroundEnvQueryContext_Querier
Grid Half Size800
Space Between150
Projection Data's Trace ModeNavigation

The Querier is whoever is running the query. Run by the enemy it is the enemy; run by the Testing Pawn we place later it is that Pawn.

Grid Half Size is the reach from the center to the edge and Space Between is the spacing between points. The standard unit is cm, so this searches 8 m around at roughly 1.5 m intervals.

Projection aligns the created points to positions on the NavMesh. That does not guarantee you can walk to a point on a separate platform, though. Whether a path connects is checked separately later.

Specifying the range from the center and the spacing, then aligning the grid candidates to the NavMesh

2. Score by closeness alone

Right-click the Grid and add "Add Test → Distance".

ItemSetting
Test PurposeScore Only
Distance ToEnvQueryContext_Querier
Test ModeDistance 2D
Scoring EquationLinear
Scoring Factor-1.0

Distance compares the distance from the reference to each candidate. We use planar distance here, and Scoring Factor -1 prioritizes closer points .

Next create a Blueprint Class and choose EQSTestingPawn under "All Classes". Name it BP_EQSTester and place it on the level's floor.

Select that Pawn and configure the Details like this.

ItemSetting
Query TemplateEQ_HideSpot
Querying ModeAll Matching
Draw LabelsOn
Draw Failed ItemsOn

This is a Pawn that tries the query in place of the actual enemy and displays candidate points . Use Nav Agent dimensions that match the real enemy too.

Save and move the Pawn a little, and candidates and scores update relative to its position. At this stage, success is closer points scoring better. Changing to +1 prioritizes distant ones , so compare once and return to -1.

Scoring near the Testing Pawn higher, and comparing with the sign flipped so distant points score higher

In the display, remaining candidates are scored from red to green , with greener being better. Blue means excluded by a test. Do not read blue shades as score levels. Confirm the Draw Labels numbers alongside the colors.

Decide who you hide from

With close spots selectable, next we add "not visible to the player". That requires a Context — the target or position used as a reference for comparison .

Here, candidate generation and distance scoring use "myself" as the reference and visibility uses "the player".

The enemy as the center for candidates and closeness, and the player as the reference for who you hide from

Build a Context returning the player

A "Player" Context is not provided out of the box. We build a Blueprint that hands over your player.

So it works in the editor too, place exactly one BP_ThirdPersonCharacter in the level for the player. If one is already there, use it. Set that placed Actor's "Auto Possess Player" to Player 0 so you control that body on Play. Confirm in the Outliner during Play that there is one player of this Class.

BP_Enemy is a separate Class made by duplicating the player Blueprint, so it is not included in the search below. If you use an enemy made as a child Class, replace this with a Class that retrieves only the player.

  1. Choose EnvQueryContext_BlueprintBase under a Blueprint Class's "All Classes". Name it EQC_PlayerContext .
  2. Open Provide Actors Set from "Override" in "My Blueprint".
  3. Wire the white exec line from the entry into Get All Actors Of Class and from there into the Return Node.
  4. Set Actor Class to BP_ThirdPersonCharacter .
  5. Connect "Out Actors" into the Return Node's "Resulting Actors Set", then compile and save.
A Blueprint getting and returning the list of player Class Actors from Provide Actors Set

An Actors Set is the list of Actors used as references. We place only one here, so a single player is returned. That Actor exists before Play too, so the Testing Pawn can use the same Context.

Returning Get Player Character is another approach, but it is normally used during Play. Searching for a player not yet spawned in the editor cannot hand over a reference. We use a placed Actor here so visualization can be tried first.

Sponsored

Combine not visible, walkable, and close

1. Trace keeps the far side of a wall

Add a Trace test to EQ_HideSpot's Grid.

ItemSetting
Test PurposeFilter Only
ContextEQC_PlayerContext
Bool MatchOn
Trace ModeGeometry by Channel
Trace ChannelVisibility
Trace ShapeLine
Trace from ContextOn
Item Height Offset90
Context Height Offset0

Trace draws a line between the player and each candidate and checks whether something blocks it. Bool Match on here means "keep points where the line is blocked". Off keeps points with an unobstructed line of sight.

Keeping candidates blocked from the player by a wall and excluding candidates in plain sight

Drawing the line right along the floor can judge a small step as cover. Item Height Offset 90 raises the candidate side's endpoints 90 cm off the floor. The player side uses the Actor's position as is. For our Third Person Character, the reference is around the body's center rather than its feet.

The walls need Collision and must Block "Visibility". To confirm how Trace works, the Line Trace article is a useful reference.

This is a simple cover check for whether a single line at a specified height is blocked . It does not guarantee the head or whole body is fully hidden. Starting with tall walls makes the results easier to judge.

2. Pathfinding keeps points with a connected path

Add a Pathfinding test to the Grid.

ItemSetting
Test PurposeFilter Only
Test ModePath Exist
ContextEnvQueryContext_Querier
Path from ContextOn
Bool MatchOn
Skip UnreachableOn

Path Exist confirms that a route to the point exists. Even with candidates on another NavMesh island, it drops them when there is no way across.

Dropping candidates with no connected path even on the NavMesh, keeping walkable ones

Leave the Distance you added first with Distance To as Querier and Scoring Factor at -1. Do not change it to "close to the player".

The query now divides roles like this. EQS adjusts test execution order, so do not read the on-screen order like Blueprint exec lines.

ElementRole
GridCreates candidates around itself
Trace: Filter OnlyKeeps points where the player's line is blocked
Pathfinding: Filter OnlyKeeps points it can walk to
Distance: Score OnlyScores the closest remaining points highest

3. Confirm by moving the walls

Put BP_EQSTester in front of a wall and the player on the other side, specify the Query Template, and look at the results. Close points that are in plain sight being excluded while blocked, reachable points remain means the conditions work as intended.

Reading candidate exclusion and scores relative to the player's position, the wall, and the Testing Pawn

The diagram's scores illustrate closer candidates scoring higher. Actual numbers vary with the candidate layout.

Next move the player to the wall's side and nudge the Testing Pawn to recalculate. Watch whether points that hid you a moment ago get excluded. After moving a wall, also wait for the NavMesh to update.

If display gets heavy while editing a large query, temporarily clear the Testing Pawn's Query Template. Before widening the range, try coarser candidate spacing.

Hands-On: move to cover when hit

We now have "where to hide". Next we build "search when hit, move, and wait three seconds" into the Behavior Tree. When there is no cover or the move fails, it waits three seconds where it stands and returns to normal behavior.

Taking a hit as the cue to find cover, move there, and return to normal behavior after three seconds

1. Add two Keys to the Blackboard

Open BB_Enemy and add these.

KeyTypePurpose
NeedsCoverBoolThe cue to start hiding. Default false
HideLocationVectorThe destination chosen by EQS

A Blackboard is shared memory the Behavior Tree reads. NeedsCover is "does it need to hide" and HideLocation is "where to hide", kept separate. A Vector is the X, Y, Z trio representing a position.

2. Turn a hit into the cue

Confirm "Can Be Damaged" is on in BP_Enemy and add Event AnyDamage .

  1. Connect Get Controller's output into Get Blackboard 's Target.
  2. Connect Get Blackboard's Return Value into Set Value as Bool 's Target.
  3. Set Set Value as Bool's Key Name to NeedsCover and Bool Value to true.
  4. Wire the white exec line from Event AnyDamage into Set Value as Bool.
Setting NeedsCover true on the Blackboard used by the enemy's own Controller, from the enemy's AnyDamage

Event AnyDamage fires when damage arrives through Apply Damage or similar. Here we record the hiding cue separately from HP-reducing logic. If you already have this event, add this update to the existing logic.

3. Build a Task that clears the cue at the end

From BT_Enemy's "New Task", create BTT_EndCover with BTTask_BlueprintBase as the parent.

Add a variable FlagKey of type Blackboard Key Selector and turn on Instance Editable. That variable lets you choose "which Key to operate on" where the Task is placed.

Wire the white exec line Receive Execute AI → Set Blackboard Value as Bool → Finish Execute . Specify FlagKey for the Set's Key and false for Value, with Finish Execute's Success on.

BTT_EndCover setting the specified Bool key back to false and ending the Task successfully

Set BB Bool in the diagram is shorthand for Set Blackboard Value as Bool. That returns NeedsCover to false after hiding finishes. Finish Execute is the node telling the tree "this Task is done".

4. Put the hiding branch leftmost

Add a new Sequence "Hide" to BT_Enemy's top-level Selector. We want it prioritized over chasing and patrolling, so place it left of the existing branches.

Add a Blackboard Decorator to "Hide".

ItemSetting
Blackboard KeyNeedsCover
Key QueryIs Set
Notify ObserverOn Result Change
Observer AbortsLower Priority

For a Bool, Is Set checks whether it is true here. Lower Priority means NeedsCover turning true interrupts chasing or patrolling running to the right and enters the hiding branch.

Place these three as "Hide"'s children, from the left.

  1. A Sequence "Move to cover"
  2. Wait: Wait Time 3.0, Random Deviation 0
  3. BTT_EndCover: FlagKey NeedsCover

Then place Run EQS Query and Move To as siblings, left to right under "Move to cover". It is not a structure with Move To hanging under Run EQS Query.

A Behavior Tree running position selection and movement, a three-second wait, and clearing the cue inside the hiding Sequence

Run EQS Query uses these settings.

ItemSetting
Query TemplateEQ_HideSpot
Run ModeSingle Best Item
Blackboard KeyHideLocation
Update BBOn FailOn, where it is displayed

Single Best Item picks the highest-scoring remaining candidate. Set Move To's Blackboard Key to HideLocation too, with Acceptable Radius at 30 to start, Reach Test Includes Agent Radius and Goal Radius off, and Allow Partial Path off. Those settings get it near the chosen point.

Finally, add a Force Success Decorator to the inner Sequence "Move to cover".

If EQS finds no candidates, that Sequence fails and never reaches Move To. Force Success returns success to the outside, including when the move fails, so the following Wait and BTT_EndCover still run. It is not a judgment that hiding succeeded but a setting for proceeding to cleanup.

Skipping Move To when the query fails, and proceeding to the three-second wait and cue clearing whether it succeeds or fails

That avoids repeating the query with no interval when there are no candidates. Even with a stale HideLocation, the failing path does not proceed to Move To.

5. Deal damage with the N key

Instead of a gun, attack one enemy in the level with the N key to test.

  1. Select BP_Enemy in the level and open the Level Blueprint.
  2. Right-click the graph and create a reference to that placed enemy.
  3. Wire the N event's Pressed into Apply Damage .
  4. Specify the enemy reference for Damaged Actor and 1 for Base Damage. The other inputs can stay at their defaults for this check.
Sending Apply Damage to the placed enemy from the Level Blueprint's N key

Compile and save the Blueprints and save the Behavior Tree too. Play, click the game screen, and press N where the enemy and player can see each other. If N is used for something else, change this test event to a free key.

What to tryResult to watch
Deal damage with NNeedsCover becomes true and the hiding branch runs
Cover existsHideLocation updates and the enemy moves there
It could moveIt waits three seconds on arrival and returns to chasing or patrolling
Remove the walls so no candidates remainIt skips Move To, waits three seconds in place, and returns
Move the walls and try againThe next query picks a new hiding spot

This example does not extend the wait when hit mid-hide. While NeedsCover is already true, it continues that single sequence.

Once real projectiles call Apply Damage, the same hit logic replaces the N key.

Sponsored

How to look when it does not work

First check "can candidates be chosen" with the Testing Pawn, then "is that result being used" in the Behavior Tree.

SymptomWhere to check
No candidate points appearQuery Template, the Grid's range, projection to Navigation, the NavMesh, the Testing Pawn's position
Adding Trace leaves nothingWhether the Context returns Actors, whether one player is placed, whether the walls Block Visibility
Fully exposed spots remainBool Match, the Trace height, whether the line hits unintended floors or props
Distant points get chosenWhether Distance To is Querier and Scoring Factor is -1
The Preview succeeds but the enemy cannot moveThe Testing Pawn's and enemy's dimensions, Path Exist, the runtime NavMesh, Move To's result
Taking damage does not switch branchesCan Be Damaged, the runtime Blackboard's NeedsCover, branch order, Observer Aborts
It does not move after hidingThe Wait time, BTT_EndCover's FlagKey, the Finish Execute connection

During play, AI Debugging's EQS display also traces candidate scores and the chosen point. By default the apostrophe key opens AI Debugging and numpad 3 toggles the EQS display. If your layout or settings differ, check the project's Gameplay Debugger settings.

Note also that the Testing Pawn's results are what it found from that Pawn's position . When comparing distance from the enemy, place the Testing Pawn near the enemy too.

Bonus: extending it to your own game

Change what "close" means

Our Distance is straight-line distance. A spot just past a wall may require a long way around to reach.

To prioritize "the shorter walking distance", score with Pathfinding's Path Length . Grasp the mechanism with straight-line distance first and compare when actual movement looks unnatural.

Change how candidates are made and the conditions

The Grid searches the surroundings as an area. Points: Circle lines candidates up at a fixed distance from a center. Making the player the center gives a foundation for finding positions that maintain distance.

Searching the whole surroundings with a Grid versus searching around a target with a Circle

Both let you set who the center is. It is not fixed that "Grid is always yourself and Circle always the player".

To make direction a condition, the Dot test is available. It compares how aligned two directions are, useful for designs choosing a target's front or back.

Reselect when it becomes necessary

Our hiding spot is the position chosen by the query at the moment of the hit. If the player circles around while the enemy runs, that spot can become visible.

To extend to an enemy that stays hidden, add a mechanism reselecting when spotted or at a fixed interval. Before adding finer candidates, think about when you need to check again .

Simply heading toward a target only needs a normal Move To. That Move To also uses NavMesh paths, so it does not mean "you cannot avoid walls without EQS". EQS is for when you want to choose the destination itself from several candidates.

Summary

In EQS you generate candidates, exclude by essential conditions, and score and choose among the rest. Here we made "not visible" and "walkable" Filters and "close" a Score.

Moving walls or the player changes what gets chosen. Confirm those changes through dot colors and numbers first, then connect it to the enemy's movement once the query convinces you.

Further Reading

Unreal Engine Notes in this section98