Unreal Engine AI: Mastering Behavior Trees & EQS with Blueprints

A comprehensive guide to creating intelligent, dynamic, and environment-aware AI agents.

Learning Objectives

By the end of this lesson, you will be able to:

Module 1: Conceptual Foundations & Project Setup

Part A: Theory - The AI Trio

We'll be working with three key components that work together:

Key Analogy: A Behavior Tree is the recipe, the Blackboard holds the ingredients, and the AI Controller is the chef following the recipe.

Part B: Project Setup

  1. Create a Project:

    Start with the Third Person template. This gives us a playable character and a simple environment to work in.

  2. Create the AI Character:

    Duplicate the BP_ThirdPersonCharacter and rename it to BP_AI_Character. Place one in the level.

  3. Enable EQS:

    EQS is a plugin! Go to Edit > Plugins, search for "Environment Query System" and enable it. Restart the editor.

  4. Create Core AI Assets:

    In the Content Browser, create a new folder called "AI". Inside, create the following assets:

    • Blueprint Class > AI Controller: Name it AIC_Enemy.
    • Artificial Intelligence > Behavior Tree: Name it BT_Enemy.
    • Artificial Intelligence > Blackboard: Name it BB_Enemy.
  5. Link the Assets:

    Step 1: Open BP_AI_Character, select the top-level component (Self), and in the Details panel, set AI Controller Class to your AIC_Enemy.

    Step 2: Open AIC_Enemy. In its Event Graph, on Event On Possess, add the nodes: Run Behavior Tree. Select your BT_Enemy as the asset.

Module 2: The Brain - Building a Basic Behavior Tree

Part A: The Blackboard (Memory)

Open BB_Enemy. This is where we define our AI's memory. Let's add two keys:

  1. Click "New Key" and select Object. Name it TargetActor. In its details, set the Base Class to "Actor".
  2. Click "New Key" again and select Vector. Name it TargetLocation.

Part B: The Behavior Tree Structure

Open BT_Enemy. Make sure its Blackboard Asset is set to BB_Enemy in the Details panel.

BTs flow from the Root, down and left-to-right.

  1. Create a Selector:

    Drag from the Root and add a Selector node. A Selector tries each child from left to right and stops as soon as one succeeds. This is perfect for "either-or" logic (e.g., "Attack Player OR Patrol").

  2. Create a Patrol Sequence:

    Drag from the Selector (on the right side) and add a Sequence node. A Sequence executes all its children from left to right and fails if any one of them fails. This is for step-by-step actions.

  3. Build the Patrol Logic:

    From the Sequence, add the following built-in tasks:

    • Task 1: Move To. In its Details, set the Blackboard Key to our TargetLocation.
    • Task 2: Wait. Set duration to 2 seconds.
At this point, our AI will try to move to a `TargetLocation` that is never set, so it won't do anything yet. That's our next step!

Module 3: The Senses - Introduction to Environment Query System (EQS)

Instead of creating custom Blueprint tasks or services for every spatial query, we will use Unreal's built-in, powerful **Environment Query System (EQS)**. This is the modern, data-driven way to have AI perceive and understand the world.

An EQS Query consists of:

Part A: Creating a Patrol Query

First, we'll replace the custom `BTT_FindPatrolLocation` task with a reusable EQS query.

  1. Create the EQS Asset:

    In your "AI" folder, right-click and choose Artificial Intelligence > Environment Query. Name it EQS_FindRandomPatrolPoint.

  2. Add a Generator:

    Open the new query. The root node is the Generator. Choose Points: On Circle. This will generate test points in a ring around the AI (the "Querier"). Set the Circle Radius to 1500 and Points on Circle to 16.

  3. Add a Test:

    Right-click the Generator and add a Pathfinding test. This is crucial. It will automatically filter out any points the AI cannot navigate to, preventing it from getting stuck.

  4. Test the Query:

    Use the EQS Testing Pawn in the editor toolbar to preview your query in the level. You should see a ring of green spheres representing valid patrol points.

Part B: Creating a Player Detection Query

Next, we'll replace the simple Sphere Overlap service with a more robust EQS query for finding the player.

  1. Create the EQS Asset:

    Create another EQS asset and name it EQS_FindPlayer.

  2. Add a Generator:

    This time, for the Generator, choose Actors Of Class. This generator finds actors directly instead of points.

    • Set Searched Actor Class to your player character blueprint (e.g., BP_ThirdPersonCharacter).
    • Set Search Radius to 2000. This is our AI's "sight" range.
  3. Add Tests:

    Add two tests to the generator:

    • Test 1: Distance. This is redundant with the search radius, but it's good practice. It scores closer targets higher.
    • Test 2: Trace. This checks for line of sight. Set Context to "Querier" (the AI) and Trace To to "Items" (the actors found by the generator). This ensures the AI can actually *see* the player and isn't targeting them through walls.

Module 4: The Brain - Assembling the Behavior Tree with EQS

Now that we have our "senses" (EQS Queries), we can build our AI's "brain" (Behavior Tree) to use them. This approach is much cleaner and more powerful.

Part A: The Player-Sensing Service

A Service is still the perfect tool to periodically run our perception query.

  1. Create a New Service:

    Open BT_Enemy. Right-click the main Selector node, choose Add Service > New Service, and name it BTS_CheckForPlayer.

  2. Implement the Service Logic:

    In BTS_CheckForPlayer, override Receive Tick AI. Instead of doing overlaps, we will run our EQS query.

    [Event Receive Tick AI] -> [Run EQS Query] (Query Template: EQS_FindPlayer, Run Mode: Single Best Item) | (OnQueryFinished Event) -> [Get Query Results as Actors] -> [Get (a copy) from array, index 0] | (Is Valid branch) -> [True (Player Found)]: [Set Blackboard Value as Object] (Key: TargetActor, Value: The found actor) -> [False (No Player)]: [Clear Blackboard Value] (Key: TargetActor)
    Why This is Better: Our Service is now incredibly simple. All the complex logic for finding the player (distance, line of sight) is contained and visualized within the `EQS_FindPlayer` query itself, making it easy to debug and reuse.

Part B: Building the Behavior Tree Logic

With our perception service running, the Behavior Tree can now react to the `TargetActor` key.

  1. Create the "Chase/Attack" Branch:

    To the main `Selector`, add a `Sequence` node on the left. This will be our highest-priority behavior.

    • Decorator: Right-click the Sequence and add a Blackboard decorator. Set it to observe `TargetActor` and check if it "Is Set". This branch will only run if the AI has a target.
    • Task 1: Add a Move To task. In its details, set the `Blackboard Key` to `TargetActor`. The AI will now move directly towards the player it has found.
    • Task 2 (Optional): Add a Wait task (e.g., 1 second) to simulate an "attack" cooldown.
  2. Create the "Patrol" Branch using EQS:

    To the main `Selector`, add another `Sequence` node to the right of the first one. This is the fallback behavior.

    • Task 1: Run EQS Query. Add the built-in Run EQS Query task.
      • Set Query Template to your EQS_FindRandomPatrolPoint.
      • Set Blackboard Key to TargetLocation. This task will run our query and store the best point in the Blackboard.
    • Task 2: Move To. Add a Move To task that reads from the TargetLocation key.
    • Task 3: Wait. Add a Wait task (e.g., 3 seconds) before finding a new patrol point.

Module 5: Advanced Integration & Final Tree

Now, let's combine everything and add the "Find Cover" logic for a more intelligent AI.

Part A: Creating the Cover Query

  1. Create EQS_FindCover Asset:

    Create a new EQS query named EQS_FindCover:. This will find a point that is hidden from the player.

  2. Configure Generator and Tests:
    • Generator: Points: On Circle:. Radius ~2000.
    • Test 1: Trace. This is the most important test. We want to find a point that does not have line of sight to the player.
      • Set Context to "Items" (the generated points).
      • Set Trace To to a context referencing the Player. You'll need to create a new Blackboard key for the EQS to get this context. In BB_Enemy:, create a new key named PlayerActor_EQSContext: of type Actor. In the BTS_CheckForPlayer: service, set this key at the same time you set TargetActor:.
      • In the Trace test, set Test Purpose to Filter Only: and enable Invert. This will discard any points that CAN see the player.
    • Test 2: Distance. Set this to score points that are a medium distance from the PlayerActor_EQSContext: higher. We don't want to hide right next to the player or too far away.
    • Test 3: Pathfinding. As always, ensure the AI can reach the point.

Part B: The Final Behavior Tree

We will now restructure our tree for the final, more intelligent logic: "If I see a player, run for cover. If I don't see a player, patrol."

Root └── Selector (with BTS_CheckForPlayer Service) ├── Sequence (Flee to Cover) │ ├── Decorator: Blackboard (TargetActor Is Set) │ ├── Task: Run EQS Query (EQS_FindCover -> sets TargetLocation) │ ├── Task: Wait (0.2s, gives EQS time to process) │ └── Task: Move To (TargetLocation) └── Sequence (Patrol) ├── Task: Run EQS Query (EQS_FindRandomPatrolPoint -> sets TargetLocation) ├── Task: Move To (TargetLocation) └── Task: Wait (3s)
Congratulations! You have now built a robust AI where every major decision about "where to go" is handled by the Environment Query System. The Behavior Tree simply orchestrates *when* to ask these questions and what to do with the answers. This is a highly modular, debuggable, and scalable approach to AI development in Unreal Engine.