Enemy AI With C++ And Blueprints In Unreal Engine
October 7, 2021
Depending on the type of enemies you have in your game, you will create different AI behavior. And there are multiple ways how to create enemy AI in Unreal Engine, from the basic enemies that move between two points all the way to creating complex AI behavior using Behavior Trees and Blackboards.
In this post we are going to learn about AI in Unreal Engine by creating basic, intermediate and advanced enemy AI behavior using C++ and blueprints.
Download Assets And Complete Project For This Tutorial
Unreal Engine Enemy AI C++ And Blueprints Tutorial
To follow along with this tutorial, please download the starter project by clicking on the green Download assets button above.
In the downloaded folder you will find the finished project, and the starter project which I prepared for you to follow this tutorial.
Important Information Before We Start
One of the labels for this tutorial is beginner, however this is not a tutorial for complete beginners.
I expect you to know how to create basic games in Unreal, but you are a beginner when it comes to AI programming in Unreal. So it is mandatory that you know how to code in C++ and blueprints, and know how to use Unreal and its interface.
Another thing to note is that I am using Unreal Engine 4.27 for this tutorial, but the techniques that I am going to teach you can be applied to any Unreal Engine version.
Enemy AI Patrol
First we are going to create a basic patrol behavior for the enemy where the enemy is going to roam in the level.
Open the Enemy_AI_Starter_Project that you downloaded. Open the EnemyAI_Map located in Content -> Maps folder. In the map you will notice the enemy and the player actor are already prepared.
Open the BP_Enemy blueprint located in Content -> Blueprints folder. In the Event Graph tab, Right Click and search for custom event:
To make the enemy patrol in the level, we are going to use AI MoveTo function, and we are going to provide it a random destination within the navigationable bounds volume:
You can copy the nodes from here:
For AI MoveTo parameters we provided self, which is a reference to the enemy blueprint, and for the destination we used the GetRandomReachablePointInRadius which is a function that will give us a reachable point in the radius we provide from the origin.
This function will calculate all the collisions that are in the way of the AI and that way the enemy will cleverly avoid any obstacles in his path.
For the origin parameter of the GetRandomReachablePointInRadius function we provided the location of the enemy, because we are going to patrol from the enemy’s location.
For the radius I’ve set 1500 as the value, which means it will try to get a reachable point from the enemy’s location in the 1500 value radius. Of course, we can change the radius value to a higher or a lower number anytime we want to make the enemy patrol further in the level.
When the AI MoveTo function finishes, we are going to delay for 1 second using the Delay function, and then call the Random Patrol node we create so that the enemy will randomly patrol the level again.
Before we proceed to test this out, from the BeingPlay call the Random Patrol node we created:
Drag the NavMeshBoundsVolume in the level and set the following values for its Location and Scale:
This will make the NavMeshBoundsVolume cover the whole level and this will be the navigationable area where the enemy can move with the help of AI MoveTo function:
Let us now run the game and test it out:
We can also remove the delay that happens after AI MoveTo function finishes which will make the enemy patrol the level without stopping.
Detecting The Player’s Presence And Moving Towards Him
We can use the same function to make the enemy run towards the player. But first, we need a way to detect the player’s presence near the enemy. For that we are going to attach a Sphere Collision component to the enemy.
In the blueprint editor for the BP_Enemy, in the Components tab click on Add Component and filter for sphere collision:
In the My Blueprint tab, under variables create a new boolean variable and name it Player Detected. Then create another variable and for the variable type, click on the variable icon:
In the search bar filter for third person and select the Object Reference for the Third Person Character:
With these two functions we are going to detect when the Player Collision Detection sphere collides with the player actor so that we can chase him, and when the player actor exists the collision so that we can stop chasing him.
But before we do that, we are going to create a custom event and name it Move To Player, which is going to make the enemy move towards the player actor:
You can copy the nodes from here:
As you can see, we are using the Player REF variable, which is a reference to the player actor in the game, to get the location of the player and make the enemy move towards him.
The Acceptance Radius parameter for the AI MoveTo function is how far from the target will the AI stop moving, in this case we set the value to 150 units.
To get a reference to the player actor, we need to detect collision in On Component Begin Overlap that we created:
In OnComponentBeginOverlap we first perform a cast to test if the player actor has collided with the sphere, if that is true, we will get a reference to the player actor and we set the Player Detected bool value to true. We will use this variable to control the enemy AI logic.
And then, we make the enemy go towards the player using the Move To Player custom event node we created.
In OnComponentEndOverlap, when the player actor collides with the sphere, we are going to set the Player Detected value to false, and make the enemy patrol randomly again:
Compile and save the changes and now let’s run the game and test it out:
While the enemy is detecting the player’s presence and going towards his location, we have one big issue, and that is the enemy is not following the player when he moves around, instead it goes towards the first location where the player was when we detected the collision with the player.
We can fix this issue by calling the Move To Player in the Tick event, but this will make a problem when we want to attack the player. Instead there is a better solution to this problem that we are going to implement now.
A Smarter Way To Make The Enemy AI Chase The Player
Since the AI MoveTo function will move the AI to the first position we pass to it, we need a way to test if the target position has changed so that the AI will move towards the changed position.
For that, I am going to create a new custom event, name it Seek player and add the following nodes to it:
You can copy the nodes from here:
The Clear Timer By Function name will stop the timer from calling the function with the specified name, in our case Seek Player which we provided in the Function Name parameter.
We need to do this because we specified that the Set Timer By Function Name should loop, which means it will run all the time until we stop it by using Clear Timer By Function Name.
Now, we need to edit the Move To Player function:
You can copy the nodes from here:
Compile and save the new changes to the BP_Enemy blueprint and let’s run the game to test it out:
As you can see now, even when the player changes his location the enemy is constantly chasing him.
Enemy AI Attack
The idea is when we detect player collision with the Player Attack Collision Detection component, we will set the Can Attack Player value to true, and when the player exists that collision we will set the value to false:
We also need to make changes in the Move To Player node so that the enemy attacks the player when it gets close to him. First we are going to create a new condition when the AI MoveTo finishes:
When the enemy gets to the player’s location we are going to check if the enemy can attack the player, if that is true we will call Stop Seeking Player because now we need to attack him, if the enemy can’t attack the player, then we will call Seek Player again.
To attack the player, we are going to use the montage animation I’ve prepared which is the enemy’s attack animation:
You can copy the nodes from here:
The Play Montage function takes a few parameters that we need to provide. The first one is the Mesh which represents the Skeletal Mesh Component on which the animation will be played.
For that, I provided the Mesh component from the BP_Enemy:
For the Montage To Player click on the drop down list and select the Mutant_Attack_Montage:
When the montage finishes playing, we test if Player Detected is true, if that is the case we will make the enemy move towards the player by calling Seek Player and that will repeat this same process over again.
Compile and save the changes and let’s run the game to test it out:
Attaching Collision Components To Sockets
Now that we are attacking the player, let us also deal damage. To do this, we need to edit the skeleton of the mutant model. In the Content -> Enemy_Model folder open Mutant_Skeleton in the editor:
In the options tab on the left side, locate the RightHand in the skeleton hierarchy and Right Click ->Add Socket:
Rename the Box to Damage Collision, and in the Details tab under the Sockets settings for the Parent Socket field click on the little loop icon and search for the RightHandSocket which is the name of the socket we created in the Mutant_Skeleton a few moments ago:
This means that the Damage Collision will move along with the right hand of the mutant model. We do need to reposition and resize the Damage Collision component, so set the following values for the location:
And the following values for the Box Extent axis under the Shape settings:
Now the Damage Collision component looks like this:
Animation Notification Events
The reason why we went through all of this Box collision and socket set up is because we are going to use the attack montage animation to trigger the damage functionality.
If we open the Mutant_Attack_Montage which is located in Content ->Enemy Model folder, and preview the animation we will see that the mutant is attacking with its right hand:
Since we attached the Damage Collision component to the right hand socket, when the attack animation is played and the mutant moves his right hand, the Damage Collision component will move along with it and we can use that to detect the collision with the player actor and deal damage.
To do that we need to add animation notifiers that will notify us when the animation is at a certain frame.
We can do that by dragging the animation preview slider at the desired frame in the animation timeline:
Or we can set the exact frame we want on the right side of the Filter search bar in the animation timeline:
When we are done with that, on the Notifies timeline, Right Click -> Add Notify -> New Notify and name the new notify Attack Started:
We created a new animation notify, or notification, on frame 11, which means when we play the attack animation and when the animation reaches frame 11, the Attack Started notify will be called, and we will be notified in the code when that happens.
We also need to create another notification that will inform us that the animation has ended. Go on frame 30, and create a new notify and name it Attack Ended.
After you finish, you will see two animation notifications in the attack animation timeline:
Before we access the notification events in the blueprint editor, we need to go inside the BP_Enemy editor, and under variables create a new boolean variable and name it Can Deal Damage:
Now open the BP_Enemy_Animation blueprint located in Content -> Enemy_Model folder. In the Event Graph tab, Right Click and search for attack started:
In the same way search for the attack ended anim notify node. We already have a reference to the BP_Enemy in the BP_Enemy_Animation blueprint, so we can use that variable to access the Can Deal Damage bool to change its value when the attack has started and when the attack ends:
You can copy the nodes from here:
Going back to BP_Enemy blueprint, select the Damage Collision component and in the Details tab under Events, click on the green + button for the On Component Begin Overlap:
In the Event Graph tab, for the On Component Begin Overlap of the Damage Collision component, add the following nodes:
You can copy the nodes from here:
When the Damage Collision component detects the collision with the player actor, we are going to check if we can deal damage to the player, if that is true, we will deal damage, or in our case print something to the console hehehe