Implementing AI Hearing using C++
In this post we’re going to create an AI Character that hears our player footsteps and follows him.
If you are interested in creating a “Seeing” sense for your AI, check out this post. Before we start,
here is the end result:
http://www.youtube.com/embed/cy6qwaLfpVI?version=3&rel=1&fs=1&autohide=2&showsearch
=0&showinfo=1&iv_load_policy=1&wmode=transparent
To achieve the above functionality, we’re going to need a couple of things:
1. A playable character with a PawnNoiseEmitter component, which will be used to
“report” into the game that a sound has been played.
2. A Controller for our AI character
3. A custom AI Character which will contain a reference for our Behavior Tree and a
PawnSensing component
4. A Behavior Tree for our AI Logic
5. A Blackboard to store values for our Behavior Tree
6. A footstep sound (Optional)
While reading this list you will notice that the footsteps sound is optional, this is because in UE4
we explicitly tell the game that we’ve played a sound somewhere with a certain volume. The
engine itself doesn’t care if we have actually played a sound or not. In case you want to include
the same footstep sound I’ve used in the video above, here is the download link. In order to get
started, create a Third Person C++ Template project.
Before we even begin to write any code, right when your template project loads up, add a
nav mesh bounds volume to cover your whole level!
Setting up our playable Character
To set up our character, open up it’s header file, and include the following library right before
the .generated.h file:
#include "Perception/PawnSensingComponent.h"
Then, add the following declarations:
1 public:
2
3
/*The function that is going to play the sound and report it to our game*/
UFUNCTION(BlueprintCallable, Category = AI)
4
void ReportNoise(USoundBase* SoundToPlay,float Volume);
5
6
/*A Pawn Noise Emitter component which is used in order to emit the sounds to nearby AIs*/
7
UPROPERTY(VisibleAnywhere,BlueprintReadWrite)
8
UPawnNoiseEmitterComponent* PawnNoiseEmitterComp;
Then, switch to your character’s source file and inside your constructor, type in the following
initialization:
1
PawnNoiseEmitterComp =
CreateDefaultSubobject<UPawnNoiseEmitterComponent>(TEXT("PawnNoiseEmitterComp"));
Moreover, type in the following implementation of the ReportNoise function:
1
2
void ATPBlogProjectCharacter::ReportNoise(USoundBase* SoundToPlay,float Volume)
{
//If we have a valid sound to play, play the sound and
3
//report it to our game
4
if (SoundToPlay)
5
{
6
//Play the actual sound
7
8
9
UGameplayStatics::PlaySoundAtLocation(GetWorld(), SoundToPlay, GetActorLocation(),
Volume);
10
//Report that we've played a sound with a certain volume in a specific location
11
MakeNoise(Volume, this, GetActorLocation());
12
}
13
14
}
Note here we’re doing two things. We first play the given sound and then we’re using the builtin function MakeNoise to inform our game that we have played a sound.
In case you copy and pasted the code above don’t forget to edit my Character’s class name
to match yours.
Now that we’re done with our character’s C++ logic and what we need to tell our character to play
a sound when he touches the floor. To do that we’re going to use Animation Notifies. Think of
Animation Notifies like functions, which get called in a specific frame of an animation
that we decide. For more information about them, check out the official UE4 documentation.
Locate your character’s run animation and add a new Notify (I named my notify
GenerateFootstepSound) when your character’s feet touch the ground. I’ve placed my notifies in
frames 8 and 18 like the following image suggests:
Then, go to the Event Graph of your animation blueprint and add in the following logic for the
Notifies we’ve created above:
Notice that I imported the sound I’ve mentioned above and I have hardcoded its reference for the
given function. It would be more flexible to create a separate USoundBase* property inside the
Character’s header file and assign values through its Blueprint. However, in this case, this
is adequate.
Compile and save your Blueprint. If you play with your Character right now, you will hear the
generated sounds for each footstep! Let’s move on!
Creating our AI Character
Add a new C++ class which is based on the Character class and name it MyAICharacter. Inside
it’s header file, right before the .generated.h add the following includes:
1 #include "Perception/PawnSensingComponent.h"
2 #include "BehaviorTree/BehaviorTree.h"
Then, type in the following declarations:
1 public:
2
/*A Pawn Sensing Component, responsible for sensing other Pawns*/
3
UPROPERTY(VisibleAnywhere)
4
UPawnSensingComponent* PawnSensingComp;
5
6
/*Hearing function - will be executed when we hear a Pawn*/
7
UFUNCTION()
8
void OnHearNoise(APawn* PawnInstigator, const FVector& Location, float Volume);
9
10
/*A Behavior Tree reference*/
11
UPROPERTY(EditDefaultsOnly)
12
UBehaviorTree* BehaviorTree;
Then, switch to your source file, and type in the following code:
// Sets default values
1 AMyAICharacter::AMyAICharacter()
2 {
3
// Set this character to call Tick() every frame. You can turn this off to improve performance if you
4 don't need it.
5
PrimaryActorTick.bCanEverTick = true;
6
7
//Initializing our Pawn Sensing comp and our behavior tree reference
8
PawnSensingComp =
9 CreateDefaultSubobject<UPawnSensingComponent>(TEXT("PawnSensingComp"));
10
BehaviorTree = CreateDefaultSubobject<UBehaviorTree>(TEXT("BehaviorTreeReference"));
11 }
12
13 // Called when the game starts or when spawned
14 void AMyAICharacter::BeginPlay()
15 {
16
Super::BeginPlay();
17
18
if (PawnSensingComp)
19
{
20
//Registering the delegate which will fire when we hear something
21
PawnSensingComp->OnHearNoise.AddDynamic(this, &AMyAICharacter::OnHearNoise);
22
}
23
24 }
25
26 void AMyAICharacter::OnHearNoise(APawn* PawnInstigator, const FVector& Location, float Volume)
27 {
28
29
//AMyAIController* Con = Cast<AMyAIController>(GetController());
30
31
//We don't want to hear ourselves
32
if (Con && PawnInstigator != this)
33
{
34
//Updates our target based on what we've heard.
35
//Con->SetSensedTarget(PawnInstigator);
36
}
}
You will notice that inside the OnHearNoise function I’ve commented out the lines 28 and
34. This is because we have yet to add our AI Controller class. When we’re done with our
Controller, we’ll get back and uncomment that code. For now, just compile and save your
code.
Creating our AI Controller
Add a new C++ class which is based on the AI Controller class and name it MyAIController.
Inside the header file, before the .generated.h header file, type in the following includes:
1 #include "BehaviorTree/BehaviorTree.h"
2 #include "BehaviorTree/BehaviorTreeComponent.h"
3 #include "BehaviorTree/BlackboardComponent.h"
Then, type in the following declarations:
1 protected:
2
/*A Behavior tree component in order to be able to call specific functions like starting our BT*/
3
UBehaviorTreeComponent* BehaviorTreeComp;
4
5
/*A Blackboard component which will be used to initialize our Blackboard Values*/
6
UBlackboardComponent* BlackboardComp;
7
8
/*This property is used to find a certain key for our blackboard.
9
We will create the blackboard later in this tutorial*/
10
UPROPERTY(EditDefaultsOnly)
11
FName TargetKey = "SensedPawn";
12
13 public:
14
/*Default Constructor*/
15
AMyAIController();
16
17
/*Called when the AI Controller possesses a Pawn*/
18
virtual void Possess(APawn* InPawn) override;
19
20
/*Sets the new sensed target value inside our Blackboard values*/
21
void SetSensedTarget(APawn* NewTarget);
Switch to your source file and type in the following code:
1 AMyAIController::AMyAIController()
2 {
3
//Initialize our components
4
BlackboardComp = CreateDefaultSubobject<UBlackboardComponent>(TEXT("BlackboardComp"));
5
BehaviorTreeComp = CreateDefaultSubobject<UBehaviorTreeComponent>(TEXT("BehaviorComp"));
6
7 }
8
9 void AMyAIController::Possess(APawn* InPawn)
10 {
11
Super::Possess(InPawn);
12
13
//If our character is valid and has a valid Behavior Tree,
14
//Initialize the values of the Blackboard and start the tree
15
AMyAICharacter* Char = Cast<AMyAICharacter>(InPawn);
16
if (Char && Char->BehaviorTree->BlackboardAsset)
17
{
18
//Initialize the blackboard values
19
BlackboardComp->InitializeBlackboard(*Char->BehaviorTree->BlackboardAsset);
20
21
//Start the tree
22
BehaviorTreeComp->StartTree(*Char->BehaviorTree);
23
24
}
25
26 }
27
28 void AMyAIController::SetSensedTarget(APawn* NewTarget)
29 {
30
//Set a new target to follow
31
if (BlackboardComp) BlackboardComp->SetValueAsObject(TargetKey, NewTarget);
32 }
Moreover, inside your controller’s source file, don’t forget to add the following include:
#include “MyAICharacter.h”
When you’re done with all that, switch to your AI Character’s source file, uncomment lines
28 and 34 and add the following include:
#include “MyAIController.h”
Compile and save your code.
Setting up our Blueprints
Once you have completed all the steps above, create the following Blueprints:
1. A Blueprint which inherits our AICharacter class
2. A Blueprint which inherits our AIController class, and name it BP_MyAICon
3. A Behavior Tree, named MyBehaviorTree
4. A Blackboard named MyBlackboard
Once you’ve completed all that, go to our AI Character’s Blueprint and:
1. Assign the default mannequin skeletal mesh and make sure to rotate the mesh to
face the blue arrow inside the capsule collider
2. Inside the Anim Blueprint Generated Class select the same Anim Blueprint as your
main Character
3. Select the BP_MyAICon as the AI Controller Class
4. Select the MyBehaviorTree for the Behavior Tree
The above steps are summarised in the following image:
Since we don’t want a “Seeing” sense for our AI, locate it’s PawnSensingComponent and
disable the See Pawns option. This is crucial.
Setting up our Blackboard
Our blackboard setup is simple, add a new Object key named SensedPawn.
Settin up our Behavior Tree
The logic for our AI will be the following: Once we have heard a pawn, we move to its location.
When we’ve reached our target we wait. And so on… It’s not the smartest AI in the world
obviously, but it will do for this tutorial. The above logic is described in the following Behavior
Tree:
Moreover, click on the MoveActor node and set the acceptable radius to 100 and the
Blackboard Key to SensedPawn.
Save your project and place an AI Character inside your map. The AI should now follow you like
the video I’ve uploaded above.
© Copyright 2026 Paperzz