You hide behind a pillar and the enemy tracks your exact position anyway. Or the moment you vanish it returns to patrol as if nothing happened. Building enemy-like behavior means separating what it knows from what it has lost track of .
AI Perception is UE's mechanism for giving AI sight and hearing. You receive notifications like "I saw someone" and "there was a noise" and connect them to the enemy's decisions.
What You'll Learn
- A design splitting noticing, remembering, and acting
- Deciding Sight's distance, angle, and occlusion
- Telling spotted, lost, and heard apart from the notification
- Adding a branch that investigates a clue and then returns to patrol
We build an enemy that chases what it spots, investigates the clue position when it loses sight, waits 5 seconds, and returns to patrol . Even without seeing you, a noise sends it to that position.
- Split noticing, remembering, and acting
- Prepare the watcher and the watched
- Decide distance, angle, and occlusion with Sight
- Check the affiliation setting and the Player tag
- Split notifications by which sense they came from
- Make a noise the AI can hear with the N key
- Hands-On: investigate the clue and return to patrol
- Perception expiry and search time are separate
- Bonus: Good to Know Up Front
- Summary
Split noticing, remembering, and acting
When footsteps are heard, what AI Perception reports is "a sound happened at this place". Whether to wait there or walk toward it is decided separately.

| Mechanism | Its role here |
|---|---|
| AI Perception | Gathers information about what was seen and heard |
| Blackboard | The notes storing the target and the place to check |
| Behavior Tree | Reads the notes and picks chase, investigate, or patrol |
The hands-on continues from Behavior Tree Basics. We use the BP_Enemy , BP_EnemyAIController , BB_Enemy , and BT_Enemy built there. Get the enemy patrolling and chasing the player on sight first.
This article extends the sight settings and the ApplySightUpdate function. It matters that the previous notification logic and the new logic do not run at the same time . The connections to replace are shown in the steps.
Prepare the watcher and the watched
The watcher is BP_EnemyAIController . Open it and add AI Perception from "Add Component". If you added it in the Behavior Tree article, use that component.
The AI Controller is the side operating the enemy character. Gathering the perception settings here makes it easy to pass what it receives to the Blackboard.

On the watched side, BP_ThirdPersonCharacter , confirm the following.
- Add
Playerto the Actor "Tags" in "Class Defaults". That is separate from Component Tags. - Add an AI Perception Stimuli Source .
- Turn on "Auto Register as Source".
- Add AISense_Sight from the "+" on "Register as Source for Senses".
A Stimuli Source is the component for "registering this Actor as a perception target". Beyond attaching it, you specify which senses it registers for.
Standard Sight also has default behavior that auto-registers Pawns (including Characters). We register explicitly here so the steps do not depend on settings differences. Use the same method when making an ordinary Actor a sight target.
Hearing is not a mechanism where footsteps arrive automatically just from registering. We add the step that notifies from the sound-producing side later.
Decide distance, angle, and occlusion with Sight
Add an AI Sight config to AI Perception's "Senses Config". If one exists, open it and match these values. Distances are in cm.
| Setting | Value here | Meaning |
|---|---|---|
| Sight Radius | 1200 | The distance at which it first spots you. 12 m |
| Lose Sight Radius | 1600 | The distance boundary for losing someone spotted. 16 m |
| Peripheral Vision Half Angle | 60 | 60 degrees to one side of forward. 120 total |
| Auto Success Range from Last Seen Location | -1 | Disables the keep-seeing exception |
| Max Age | 5 | Expiry of old perception data. Not a behavior wait |
| Starts Enabled | On | Enables sight from the start of play |
Two radii exist so the boundary does not flicker
If the spotting distance and the losing distance were the same, crossing that boundary would toggle spotted and lost repeatedly. Widening the losing distance means someone once spotted stays seen even at a slightly greater distance .

That does not mean anything inside Lose Sight Radius is visible through walls. Beyond distance, the orientation and occlusion conditions must be met too.
Half Angle is half of the total field of view
Half Angle is half the angle. 60 means 60 degrees left and 60 degrees right of forward, 120 total. 90 gives 180 degrees ahead, not sight all the way behind.

Make the visible wall and the sight-blocking wall agree
Occlusion is line of sight being blocked by a wall or similar. Standard Sight tests whether the space between the watcher and the target is blocked.
For the experiment, place a large box or wall the player can hide behind entirely. Set its collision to Block Visibility . Block means blocking tests on that channel.
In projects that changed the channel used, match "Project Settings" → "AI System" → "Default Sight Collision Channel". Details are in the collision channel article.
Check the affiliation setting and the Player tag
Even with the distance and angle right, the affiliation to detect setting can exclude a target. "Detection by Affiliation" is that filter.
| Entry | Target |
|---|---|
| Detect Enemies | Hostile parties |
| Detect Neutrals | Neutral parties |
| Detect Friendlies | Allies |
A player with no team set, as here, is treated as neutral. Turn Detect Neutrals on for Sight. Being an enemy Blueprint does not make UE automatically assign the player to a hostile team.

Including neutrals means things other than the player get reported too. So after receiving a notification, let only targets with the Player tag through using Actor Has Tag .
The affiliation setting decides "what the sensor picks up"; the Actor Tag decides "which of those the game logic uses". Splitting them also prevents enemies chasing each other.
Split notifications by which sense they came from
Add an event from the "+" on On Target Perception Updated in AI Perception's details. Use the existing one if there is one.
The event gives you the target Actor and a Stimulus summarizing this perception result. Do not overthink "stimulus": it is notification data holding when, where, and what was sensed .
Create Break AIStimulus from Stimulus to extract its contents.
| Entry | Meaning here |
|---|---|
| Successfully Sensed | Whether this stimulus was sensed. A true/false value |
| Stimulus Location | The position recorded in the stimulus. Stored as the investigation point |
| Tag | The name given to a sound. Useful for kinds like Footstep or Gunshot |
For sight, Successfully Sensed true means spotted and false means lost. But sound notifications arrive at the same event . Treating every false as "lost the player" risks stopping the chase merely because sound information went stale.

The node that inspects the kind is Get Sense Class for Stimulus . Pass it a Stimulus and it returns a class representing the sense kind such as AISense_Sight or AISense_Hearing . The concrete wiring comes in the hands-on.
Note that sight notifies about visibility changes, and hearing about newly reported sounds. It is not a per-frame position-tracking event.
Make a noise the AI can hear with the N key
Playing footsteps through the speakers and telling the AI "footsteps happened here" are separate. Let's try the notification alone first, without real audio.
On the listening side's AI Perception, add an AI Hearing config to "Senses Config".
| Setting | Value here |
|---|---|
| Hearing Range | 2000 (20 m) |
| Detect Neutrals | On |
| Max Age | 5 |
| Starts Enabled | On |
Turn Detect Neutrals on for Hearing too , not just Sight.
Next open the player's BP_ThirdPersonCharacter Event Graph. Add a keyboard N event and connect a white exec wire from "Pressed" to Report Noise Event .
| Pin | Connection / value |
|---|---|
| Noise Location | Get Actor Location's Return Value (Target is Self) |
| Loudness | 1.0 |
| Instigator | Self |
| Max Range | 0 |
| Tag | Footstep |

Instigator is "the Actor that made this sound". Here it is the player, so pass Self. The receiving side checks whether that target has the Player tag.
Loudness is the sound's strength, and Max Range is a distance cap set on the sound side. We use Loudness 1 and Max Range 0, imposing no cap on the sound side, so the listener's Hearing Range covers 20 m . The relationship between loudness and distance changes with setting combinations, so leaving Loudness at 1 and adjusting the range is clearest at first.

Standard Hearing is not blocked simply because a wall blocks Visibility the way Sight is. Reacting to sound through a wall is normal in this experiment. To model how much a wall dampens sound, add a separate test.
To link it to footsteps later, call the same notification from Animation Notify instead of the N key.
Hands-On: investigate the clue and return to patrol
Now we write perception notifications into the Blackboard and connect them to the enemy's behavior. The distinction here is chasing someone you can see versus investigating a place you know they were .

While investigating it stays at that place for 5 seconds. We do not add a search animation that walks around yet; we confirm the behavior switch.
1. Add a note for the investigation point
Add LastKnownLocation to BB_Enemy with type Vector . A Vector here is the type representing a position with X, Y, and Z. Together with the two from the Behavior Tree article, we use these three.
| Key name | Type | What it stores |
|---|---|---|
| TargetActor | Object (Base Class: Actor) | The target currently visible and being chased |
| LastKnownLocation | Vector | The investigation coordinates from sight or sound |
| PatrolLocation | Vector | The next patrol point |
An Actor reference points at the same target even as it moves. A Vector is the coordinate at the moment it was stored. When relying on sound alone, use the coordinate so the enemy does not track the current position of someone it has not seen.
2. Make one entry point for notifications
Prepare these three functions in BP_EnemyAIController . The existing ApplySightUpdate gets one more input and updated contents.
| Function | Inputs |
|---|---|
| HandlePerceptionNotice | NoticeActor: Actor Object Reference, NoticeStimulus: AIStimulus |
| ApplySightUpdate | SeenActor: Actor Object Reference, bSensed: Boolean, SensedLocation: Vector |
| ApplyNoiseUpdate | bSensed: Boolean, NoiseLocation: Vector |
First connect the event's exec output to HandlePerceptionNotice , passing Actor to NoticeActor and Stimulus to NoticeStimulus. Replace the previous event's direct connection to ApplySightUpdate with this.

Inside HandlePerceptionNotice, wire this order.
- Pass NoticeActor to Actor Has Tag 's Target with Tag
Player. - Connect the result to a Branch 's Condition. Connect the function entry's exec output to the Branch and end on the False side.
- From True, connect to Get Sense Class for Stimulus 's exec input, passing NoticeStimulus to Stimulus.
- Create Equal (Class) from Return Value with the compared class
AISense_Sight. Connect the result to the next Branch's Condition, and Get Sense Class for Stimulus's exec output to that Branch. - On True, call ApplySightUpdate. On False, use another Equal (Class) and Branch to check for
AISense_Hearingand call ApplyNoiseUpdate only when true.

The values passed to the two calls are below. Create Break AIStimulus from NoticeStimulus to extract them.
| Function called | Values to pass |
|---|---|
| ApplySightUpdate | SeenActor = NoticeActor, bSensed = Successfully Sensed, SensedLocation = Stimulus Location |
| ApplyNoiseUpdate | bSensed = Successfully Sensed, NoiseLocation = Stimulus Location |
Actor Has Tag and Equal (Class) inspect values and take no white exec wire. Get Sense Class for Stimulus, however, has white exec pins. Do not skip it just because of the name Get.
At this stage, placing Print Strings at both functions' entries showing Sight and Hearing confirms things. Compile and save each Blueprint, then Play and try stepping into view, hiding behind a wall, and pressing N. Once eye and ear notifications arrive separately, you can add the next logic.
The Player Actor Tag says "who", the sense class says "eye or ear", and the sound Tag Footstep says "what kind of sound". Renaming the sound tag to Gunshot leaves the sense kind as Hearing.
3. Write the visible target and the coordinate to investigate separately
When reading and writing the Blackboard in the AI Controller, build nodes from Get Blackboard (Target = Self) 's Return Value . The Target for Set Value as Object , Set Value as Vector , Get Value as Object , and Clear Value is all this Blackboard Component.
That Blackboard Component is the part for operating on this running enemy's notes . It is not where you connect the BB_Enemy asset from the Content Browser or the enemy Character. Keep the existing On Possess → Run Behavior Tree (BT_Enemy) .
If ApplySightUpdate still holds the Behavior Tree article's logic, replace it with the following. Connect the entry to a Branch with bSensed as Condition. Since the shared entry already checked the Player tag, this function handles the perception result.
True: the target became visible
- Set Value as Object with Key Name
TargetActorand Object Value SeenActor. - Then Clear Value with Key Name
LastKnownLocation.
Once the target is visible, chasing takes priority and the old investigation point is cleared.

False: lost the target you were chasing
- Read
TargetActorwith Get Value as Object and compare it with SeenActor using Equal (Object) . - Connect the result to a Branch's Condition, and route the bSensed False exec wire into that Branch.
- Only on True, write SensedLocation to
LastKnownLocationwith Set Value as Vector. - Then clear
TargetActorwith Clear Value. Do nothing if the comparison is false.

We use the position recorded in that sight notification as the investigation point. Perception updates at intervals, so it does not exactly match the last on-screen frame's position. Calling Get Actor Location on a hidden target would let the AI chase a current position it should not know, so we use the stored coordinate.
Writing the coordinate first and clearing the chase target after means the next investigation point is ready the moment the chase ends. We compare that it is the same target so another Actor's notification does not clear the chase target.
Hearing: investigate the sound position only when nothing is visible
Wire ApplyNoiseUpdate as follows.
- Connect the entry to a Branch with bSensed as Condition. Do nothing on the False side.
- On True, read
TargetActorwith Get Value as Object and check its Return Value with Is Valid . - If there is no valid target, write NoiseLocation to
LastKnownLocationwith Set Value as Vector. If there is a target, do nothing.

Is Valid checks whether a reference is valid. Use the version with exec pins here and continue to the store logic from the "Is Not Valid" output. Notifications where the sound information went stale do not rewrite the investigation point.
That way, footsteps while chasing a player right in front of you do not switch the enemy to investigating.
4. Make a Task that clears the note when the investigation ends
From BT_Enemy 's "New Task", choose BTTask_BlueprintBase and create BTT_ClearLastKnown . Add a variable TargetKey of type Blackboard Key Selector with "Instance Editable" on.
Key Selector is the type for specifying "which note to clear" from a Task placed in the tree. Connect these three nodes with white exec wires.
Event Receive Execute AI → Clear Blackboard Value → Finish Execute
Pass a Get of TargetKey to Clear Blackboard Value's Key and turn on Finish Execute's Success.

In the Controller we operated by Key Name, but inside a BTTask you use Blackboard nodes that take a Key Selector . The names are similar, so compare where you are and the Key pin's type.
5. Insert the investigate branch between chase and patrol
Add a Sequence to BT_Enemy 's Selector and name it "Investigate". Arrange it so the order is chase, investigate, patrol from left to right .

Keep the Behavior Tree article's "chase and attack" and "patrol". Confirm the chase side has TargetActor Is Set with Observer Aborts Both.
Add a Blackboard Decorator to the "Investigate" Sequence. A Decorator is the condition for using that branch.
| Setting | Value |
|---|---|
| Blackboard Key | LastKnownLocation |
| Key Query | Is Set |
| Notify Observer | On Value Change |
| Observer Aborts | Both |
Is Set means "is an investigation point present". Both lets a condition change abort the running branch or the patrol to its right. On Value Change watches not only presence but coordinate changes, so a noise at a new place can restart the investigation.
Under the Sequence, connect Move To, Wait, and BTT_ClearLastKnown left to right. All three are children of the Sequence. They are not chained parent to child.

| Node | Settings |
|---|---|
| Move To | Blackboard Key = LastKnownLocation, Acceptable Radius = 100 |
| Move To arrival | Turn off both Reach Test Includes Agent Radius and Goal Radius |
| Move To path | Allow Partial Path off, Observe Blackboard Value on |
| Wait | Wait Time = 5, Random Deviation = 0 |
| BTT_ClearLastKnown | Target Key = LastKnownLocation |
An acceptable radius of 100 cm makes it wait within about a meter of the spot. The 5 seconds start after the movement finishes.
Also right-click the investigate Move To and add Force Success from "Add Decorator". It makes the tree treat a failed move as success and continue. It is not a feature that forces movement to the destination.
If the point was unreachable, it waits 5 seconds there and ends the investigation. Without this, a movement failure stops the Sequence, the note never gets cleared, and the same investigation repeats.
Clearing the value with BTT_ClearLastKnown drops the investigate condition and returns it to patrol. Writing (0, 0, 0) means "investigate the origin", a different thing, so remember use Clear when you want it empty .
6. Compare the movement against the notes
Compile each Blueprint and save everything including the Behavior Tree. For the test area, use a wide floor at one height with a single wall you can hide behind entirely. Confirm the NavMesh connects the enemy and the player with P .
Open BT_Enemy during Play and select the placed enemy's Controller in "Debug Object" to see the running branch and Blackboard values.
| What to try | What you should see |
|---|---|
| Step within 12 m in front | The player enters TargetActor and it switches to chasing |
| Move behind the wall | The investigation coordinate is set and TargetActor empties |
| Stay hidden | The enemy goes to the point, waits 5 seconds, returns to patrol |
| Show yourself while it waits | The wait aborts and it switches to chasing |
| Press N where it cannot see you | Within 20 m the Hearing notification arrives and it investigates |
| Press N at another spot while it investigates | LastKnownLocation changes and it investigates the new place |
Making noise repeatedly while moving keeps updating the investigation point, so it rarely returns to patrol. On your first check, press N once and stay quiet until the enemy finishes waiting.
If something is off, check in the order does the notification arrive → do the notes change → does the tree switch → can it move .
| Symptom | Where to check |
|---|---|
| No Sight log | View angle, distance, occluders, Sight's Detect Neutrals, target registration and Player tag |
| No Hearing log | The N input, Report Noise Event running, Instigator, Hearing settings |
| Logs appear but notes do not change | Get Blackboard's Target, Key Name spelling, launch via Run Behavior Tree |
| Notes change but it keeps patrolling | Branch left-to-right order, the Decorator's Key and Observer Aborts |
| It never returns from investigating | The Task after Wait, Target Key, Finish Execute |
| It keeps chasing behind the wall | Sight's occlusion setting and Auto Success Range, whether TargetActor cleared |
| It spots you from directly behind | Whether the enemy itself turned, whether you mixed up sight and hearing, the actual view angle |
If movement itself stops, confirm the foundation with the N-key movement experiment in the NavMesh article.
Perception expiry and search time are separate
It is tempting to think "I set Max Age to 5, so it searches for 5 seconds", but these two have different responsibilities.

Max Age is the expiry of the perception information Perception holds. 0 means no expiry. To use the behavior that forgets stale Actor information, also enable "Forget Stale Actors" under "Project Settings" → "AI System".
Wait, on the other hand, is a wait time as enemy behavior. This article writes "move to the investigation point and wait 5 seconds" in the tree, then clears LastKnownLocation. Perception-side information expiring does not automatically clear a value you saved to the Blackboard yourself.
Actually visualize the field of view
The Gameplay Debugger during Play also shows perception state. It opens with ' (apostrophe) by default, and numpad 4 displays Perception information. If a different keyboard layout prevents it opening, check "Activation Key" in the project's Gameplay Debugger settings.
Narrowing or widening the view angle changes how far around you can get behind it. Changing Half Angle from 60 → 30 and trying to circle around to the enemy's front makes the difference clear.
The "keep seeing" exception changes the stealth feel
Auto Success Range from Last Seen Location sets how close to the last-seen position someone must be to keep counting as seen . It is not a distance from the AI itself.
Leave it at -1 while testing occlusion and adjust once the basic behavior works. Dominant Sense likewise selects which sense takes priority at a queried target position; it does not automatically decide our Blackboard write order or behavior priority.
Bonus: Good to Know Up Front
- Visible but unnoticed : Sight is affected by angle and occlusion , not just distance. Even directly in front, an intervening mesh's Collision settings can prevent detection
- Affiliation can wait : handling teams in Blueprint alone is where people get stuck. Use Detect Neutrals first and judge friend or foe with tags or interfaces
- Notifications arrive on change : it is not reported "visible" every frame. It arrives the moment it spots and the moment it loses. To keep chasing, remember the last seen position yourself
- Perception expiry and search time are separate : how long Perception remembers and how long the AI keeps searching are decided independently
Summary
Chase a visible target as an Actor, and use a coordinate as the clue when you lose sight or only hear a noise. With that difference, an enemy can distinguish "I know where they are now" from "they were around here".
Try hiding, making noise, and showing yourself again while watching the notification kinds and Blackboard values. Once you understand why chase and investigate switch, you can tune noise loudness and search time to fit your game.