Unreal Engine Tutorial. Part 9: Artificial Intelligence
- Transfer
In the video game industry, Artificial Intelligence (AI) is usually called decision-making by non-player characters. It can be simple: the enemy sees the player and attacks. Or more complex, for example, an AI-controlled adversary in real-time strategy.
In Unreal Engine, you can create AI using behavior trees . The behavior tree is a system for determining the behavior used by the AI. For example, he may have fighting or running behavior. You can create a behavior tree in which the AI will fight with the player if his health is higher. If it is below 50%, then it will run away.
In this tutorial you will learn the following:
- Create an AI entity that can control the Pawn element
- Create and use behavior trees and blackboard
- Use AI Perception to give Pawn Sight
- Build behaviors so Pawn can walk and attack enemies
Note: This article is one of the nine parts of the Unreal Engine tutorial series:
- Part 1: Introducing the Engine
- Part 2: Blueprints
- Part 3: Materials
- Part 4: UI
- Part 5: How to create a simple game
- Part 6: Animation
- Part 7: Sound
- Part 8: Particle Systems
- Part 9: Artificial Intelligence
- Part 10: How to create a simple FPS
Getting to work
Download the project blank and unzip it. Go to the project folder and open MuffinWar.uproject .
Click on Play to start the game. Left-click inside the enclosed area to create a muffin.
GIF
In this part of the tutorial we will create an AI that will roam the screen. When an enemy muffin enters the field of view of an AI, it approaches the enemy and attacks it.
To create an AI character, we need three things:
- Body: the physical form of the character. In our case, the body is a muffin.
- Soul: an entity that controls a character. It can be a player or AI.
- Brain: how AI makes decisions. The brain can be created in various ways, for example, using C ++ code, Blueprints or behavior trees.
We already have a body, so we need a soul and a brain. First, we will create a controller that will be the "soul".
What is a controller?
A controller is a non-physical actor that can inhabit Pawn. Introducing allows the controller (as you might guess) to control Pawn. But what does “control” mean in this context?
For the player, this means that pressing the Pawn key will do something. The controller receives the player’s input and sends the input to Pawn. Also, the controller can handle the input itself and order Pawn to perform the action.
In the case of AI, Pawn can receive information from the controller or the brain (depending on how you implement it).
To control muffins using AI, you need to create a special type of controller called an AI controller .
Creating an AI Controller
Go to Characters \ Muffin \ AI and create a new Blueprint Class . Select AIController as the parent class and name it AIC_Muffin .
Then you need to tell the muffin to use the new AI controller. Go to Characters \ Muffin \ Blueprints and open BP_Muffin .
By default, the Details panel should display the default Blueprint settings. If not, then click on Class Defaults in the Toolbar.
Go to the Details panel and find the Pawn section . Set AI Controller Class to AIC_Muffin . This will create an instance of the controller when creating the muffin.
Since we create muffins dynamically, we also need to set the value of Auto Possess AI to Spawned . So AIC_Muffin will automatically populate BP_Muffin upon creation.
Click Compile and close BP_Muffin .
Now we will create the logic that will control the behavior of the muffin. You can use behavior trees for this .
Creating a Behavior Tree
Go to Characters \ Muffin \ AI and select Add New \ Artificial Intelligence \ Behavior Tree . Name it BT_Muffin and open.
Behavior tree editor
There are two new panels in the behavior tree editor:
- Behavior Tree: this is a graph in which we will create nodes for the behavior tree
- Details: the properties of the selected nodes are displayed here.
- Blackboard: this panel displays the Blackboard keys (more about them later) and their meanings. Displayed only when the game is running.
Like Blueprint, the behavior tree consists of nodes. There are four types of nodes in behavior trees. The first two are task and composite .
What are Task and Composite?
As the name implies, a task is a node that “does” something. It can be something complicated, such as a combo chain, or something simple, such as waiting.
To perform tasks you need to use composites (composite). A behavior tree consists of many branches (behaviors). At the root of each branch is a composite. Different types of composites have different ways of executing their child nodes.
Suppose we have the following sequence of actions:
For sequential execution of each action, we need to use the Sequence composite , because Sequence runs its child nodes from left to right. Here's what it would look like:
Note: everything that starts with a composite can be called a subtree . Usually these are behaviors. In our example, Sequence , Move To Enemy , Rotate Towards Enemy, and Attack can be considered "attack the enemy."
If any of the Sequence child nodes fails to execute , then Sequence ceases to execute.
For example, if Pawn cannot move towards the enemy, then Move To Enemy will fail. This means that Rotate Towards Enemy and Attack will not be completed. However, they will be fulfilled if Pawn manages to move towards the enemy.
Later we will also learn about the composite.Selector . For now, we'll use Sequence so that Pawn moves to a random point and then waits.
Random Point Movement
Create a Sequence and connect it to Root .
GIF
Now we need to move Pawn. Create a MoveTo and connect it to Sequence . This node will move Pawn to the specified point or actor.
Then create Wait and connect it to Sequence . You must place this node to the right of MoveTo . The order here is important because child nodes execute from left to right.
Note: you can check the execution order by looking at the numbers in the upper right corner of each node. The lower the value, the higher the priority of the nodes.
Congratulations, you just created your first behavior! It will move Pawn to the selected point, and then wait five seconds.
To move Pawn you need to specify a point. However, MoveTo can only receive values from blackboard , so let's create it.
Blackboard Creation
Blackboard is a resource whose sole purpose is to store variables (called keys ). You can consider it the memory of AI.
Although it’s not necessary to use them, blackboard provides a convenient way to read and save data. It is convenient because many nodes in behavior trees can only receive blackboard keys.
To create a blackboard, go back to the Content Browser and select Add New \ Artificial Intelligence \ Blackboard . Name it BB_Muffin and open.
Blackboard editor
The blackboard editor consists of two panels:
- Blackboard: this panel displays a list of keys
- Blackboard Details: this panel displays the properties of the selected key
Now we need to create a key that will contain the target point.
Create Target Point Key
Since we store points in 3D space, we need to store them as vectors. Click on New Key and select Vector . Name it TargetLocation .
Now we need a way to generate a random point and save it on the blackboard. To do this, we will use the third type of nodes of the behavior tree: service .
What is a Service?
Services are similar to tasks with which we do something. However, instead of forcing Pawn to take action, we use services to perform checks or update blackboards.
Services are not separate nodes; they join tasks or composites. This creates a more ordered tree, because we have to work with fewer nodes. Here's what the use of the task looks like:
And here is how the use of the service looks:
Now we can create a service that generates a random point.
Service Creation
Return to BT_Muffin and click on New Service .
This will create and automatically open a new service. Name it BTService_SetRandomLocation . To rename it, you will need to return to the Content Browser.
Service should only be performed when Pawn needs to move. To do this, attach it to MoveTo .
Open BT_Muffin and right click on MoveTo . Select Add Service \ BTService Set Random Location .
Now BTService_SetRandomLocation will be activated when activating MoveTo .
Next we need to generate a random target point.
Random Point Generation
Open Open BTService_SetRandomLocation .
To find out when the service is activated, create an Event Receive Activation AI node . It will be executed when the parent is activated (the node to which it is attached).
Note: There is also an Event Receive Activation event that does the same. The difference between the two events is that Event Receive Activation AI also provides Controlled Pawn .
To generate a random point, add selected nodes. Parameter Radius assign the value 500 .
This will give us a random point within 500 units of Pawn that you can reach.
Note: GetRandomPointInNavigableRadius uses navigation data (called NavMesh ) to determine if you can reach a point . In this tutorial, I created NavMesh in advance. It can be visualized by going to Viewport and selecting Show \ Navigation .
If you want to create your own NavMesh, then create a Nav Mesh Bounds Volume . Change its scale so that it limits the area that should be accessible for movement.
Now we need to save the point in the blackboard. There are two ways to select which key to use:
- You can specify a key by using its name in the Make Literal Name node.
- You can make the variable visible to the behavior tree. This will allow you to select a key from the drop-down list.
We will use the second method. Create a variable of type Blackboard Key Selector . Name it BlackboardKey and enable Instance Editable . This will make the variable visible when choosing a service in the behavior tree.
Next, create the following selected nodes:
Summarize:
- Event Receive Activation AI is executed when its parent is activated (in our case, it is MoveTo )
- GetRandomPointInNavigableRadius returns a random navigable point within a radius of 500 units from a managed muffin
- Set Blackboard Value as Vector sets the blackboard key (transmitted through the BlackboardKey ) to a random point value
Click on Compile and close BTService_SetRandomLocation .
Now we want to tell the behavior tree that we need to use our blackboard.
Blackboard Selection
Open BT_Muffin and make sure nothing is selected. Go to the Details panel. In the Behavior Tree, set the Blackboard Asset to BB_Muffin .
After that, MoveTo and BTService_SetRandomLocation will automatically use the first blackboard key. In our case, this is TargetLocation .
Finally, we need to instruct the AI controller to start the behavior tree.
Execution tree behavior
Open AIC_Muffin and connect the Run Behavior Tree to the Event BeginPlay . Select to BTAsset value BT_Muffin .
So BT_Muffin will be launched when creating AIC_Controller .
Click on Compile and return to the main editor. Click on Play , create some muffins and watch them wander around the screen.
GIF
We had to work hard, but we did it! Now we need to configure the AI controller so that it recognizes enemies in its field of vision. You can use AI Perception for this .
Configure AI Perception
AI Perception is a component that can be added to actors. Using it, you can give AI feelings (such as vision and hearing).
Open AIC_Muffin and add the AIPerception component .
Now we need to add a feeling. We want the muffin to recognize another muffin that falls into view, so we need to add vision .
Select AIPerception and go to the Details panel. In the AI Perception section, add a new element to Senses Config .
Set element 0 for AI Sight config and expand it.
Vision has three main parameters:
- Sight Radius: The maximum distance that the muffin can see. Let's leave the value 3000 here .
- Lose Sight Radius: если маффин увидел врага, это значение, на которое враг должен отдалиться, чтобы маффин потерял его из виду. Оставим здесь значение 3500.
- Peripheral Vision Half Angle Degrees: угол обзора маффина. Задайте значение 45. Это даст маффину угол обзора в 90 градусов.
By default, AI Perception only recognizes enemies (actors assigned to another command ). However, by default, actors do not have a command. If an actor has no command, then AI Perception considers it neutral .
At the time of writing, there was no way to assign commands using Blueprints. Instead, you can simply tell AI Perpcetion to recognize neutral actors. To do this, expand Detection by Affiliation and turn on Detect Neutrals .
Click on Compile and return to the main editor. Click on Play and create some muffins. Press the ' key to display the AI debugging screen. Press 4 on the numeric keypad to visualize AI Perception. When the muffin comes into view, a green sphere appears.
GIF
Now we need to move the muffin towards the enemy. To do this, the behavior tree must know about the enemy. This can be done by keeping the link to the enemy on the blackboard.
Creating an enemy key
Open BB_Muffin and add a key of type Object . Rename it to Enemy .
So far, we cannot use Enemy in MoveTo , because the key is of type Object , but MoveTo can only receive keys of type Vector or Actor .
To fix this, select Enemy and expand Key Type . Set Base Class to Actor . This will allow the behavior tree to recognize Enemy as an Actor .
Close BB_Muffin . Now we need to create behavior for moving towards the enemy.
Movement towards the enemy
Open BT_Muffin and separate Sequence from Root . This can be done by holding Alt and left-clicking on the wire connecting them. Let us leave the subtree of random motion alone for now.
Now create the selected nodes and set their Blackboard Key to Enemy :
At the same time, Pawn will move to Enemy . In some cases, Pawn doesn’t fully turn to the target, so you need to use Rotate to face BB entry as well .
Now you need to set Enemy when AI Perception recognizes another muffin.
Enemy Key Setting
Open AIC_Muffin and select the AIPerception component . Add the On Perception Updated event .
This event is executed when the feeling is updated. In our case, it is executed when the AI notices someone or loses sight of it. This event also conveys a list of actors who are currently detected by feelings.
Add selected nodes. Make sure Make Literal Name is set to Enemy .
This will allow you to check if the AI already has an enemy. If not, then we must give him the enemy. To do this, add the selected nodes:
Summarize:
- IsValid checks if Enemy key is set
- If it is not set, then all actors discovered at the moment are looped around
- Cast To BP_Muffin checks if an actor is a muffin
- If he is a muffin, then check if he is dead
- If IsDead returns false , then set the muffin as a new Enemy and end the loop
Click on Compile and close AIC_Muffin . Click on Play and create two muffins so that one is in front of the other. The muffin, located at the back, will automatically begin to move to another.
GIF
Now we will create our own task for the muffin to carry out the attack.
Creating an Attack Task
We can create a task in the Content Browser instead of the behavior tree editor. Create a new Blueprint Class and select BTTask_BlueprintBase as the parent class .
Name it BTTask_Attack and open. Add an Event Receive Execute AI node . This node will execute when the behavior tree executes BTTask_Attack .
First we need to get the muffin to attack. BP_Muffin contains the variable IsAttacking . If it is set, the muffin will carry out the attack. To do this, add the selected nodes:
If you use the task in its current state, then execution will stop on it, because the behavior tree does not know whether the task has completed. To fix this, add Finish Execute to the end of the chain .
Then enable Success . We use Sequence , so this will allow the nodes to execute after BTTask_Attack .
Here's what the graph should look like:
Summarize:
- Event Receive Execute AI is executed when the behavior tree launches BTTask_Attack
- Cast To BP_Muffin checks if Controlled Pawn is of type BP_Muffin
- If yes, then its IsAttacking variable is set.
- Finish Execute allows the tree to understand the behavior of the task is successfully executed
Click Compile and close BTTask_Attack .
Now we need to add BTTask_Attack to the behavior tree .
Adding an Attack to the Behavior Tree
Open BT_Muffin . Then add the BTTask_Attack node to the end of the Sequence .
Next, add a Wait node to the end of the Sequence . Change the value of its Wait Time variable to 2 . Thanks to this, the muffin will not attack constantly.
Return to the main editor and click on Play . Like last time, create two muffins. The muffin will begin to move and turn toward the enemy. Then he attacks and waits two seconds. If he sees another enemy, he will repeat the same sequence again.
In the last part, we combine the subtrees of attack and random movement.
Union of subtrees
To combine subtrees, you can use the Selector composite . Like Sequence, it also runs from left to right. However, Selector stops when the child node succeeds rather than fail. Using this behavior, we can make the behavior tree execute only one subtree.
Open BT_Muffin and create a Selector after the Root node . Then connect the subtrees as follows:
In such a scheme, only one subtree will be executed at a time. Here's how each subtree starts:
- Attack: Selector will launch the first subtree of the attack. If all tasks are completed successfully, then Sequence will also succeed. Selector will detect this and stop execution. Thanks to this, the subtree of random movement will not be executed.
- Roam: the selector will try to first execute the subtree of the attack. If Enemy is not set, then MoveTo will fail. Therefore, Sequence will also fail. Since the attack subtree failed, Selector will execute the next child, i.e. the subtree of random movement.
Return to the main editor and click on Play . Create some muffins to test your work.
Wait, but why doesn't the muffin immediately attack another muffin?
In traditional behavior trees, execution begins with each update from the root. This means that with each update, the tree first tries to execute the subtree of attack, and then the subtree of random movement. This means that the behavior tree can instantly change subtrees when the Enemy value changes .
However, Unreal's behavior trees work differently. In Unreal, execution continues from the last node executed. Since AI Perception does not immediately sense other actors, a subtree of random movement begins to execute. Now the behavior tree should wait for the completion of the random movement tree, and only then check the possibility of performing the subtree of the attack.
To fix this, we can use the last type of node: decorators .
Create Decorator
Like services, decorators join tasks or composites. Typically, decorators are used to perform checks. If the result is true, then the decorator also returns true, and vice versa. Thanks to decorators, we can control the execution of their parent elements.
In addition, decorators have the ability to terminate a subtree. This means that you can complete the execution of the random movement subtree if Enemy is specified . Thus, the muffin will be able to attack the enemy immediately after its discovery.
To use termination, we can use the Blackboard decorator . It just checks if the blackboard key is set. Open BT_Muffin and right clickon a sequencing subtree attack. Select Add Decorator \ Blackboard . At the same time, the Blackboard decorator will be added to Sequence.
Now select the Blackboard decorator and go to the Details panel. Set Blackboard Key to Enemy .
This will check if Enemy is set . If it is not set, then the decorator will fail and Sequence will fail . This will start the subtree of random movement.
To stop the execution of the random movement subtree, we will need to use the Observer Aborts parameter .
Using Observer Aborts
Observer aborts stops the subtree when the selected blackboard key changes. There are two types of termination:
- Self: this option allows the attack subtree to stop executing when Enemy becomes invalid. This can happen when Enemy dies before completing the subtree of the attack.
- Lower Priority: this option allows you to stop execution of trees with lower priority when setting Enemy . Since the random subtree is located after the attack, it has lower priority.
Set Observer Aborts to Both to enable both types of termination.
Now the attack subtree will immediately move to random movement in the absence of the enemy. And the subtree of random movement will immediately switch to attack mode when an enemy is detected.
Here's what the finished behavior tree looks like:
To summarize the subtree of the attack:
- Selector launches attack subtree if Enemy is specified
- If it is set, then Pawn will move and turn towards the enemy
- Then he will carry out the attack
- Finally, Pawn will wait two seconds.
To summarize the subtree of random movement:
- Selector starts the subtree of random movement if the subtree of the attack fails. In our case, it fails if Enemy is not specified .
- BTService_SetRandomLocation generates a random point
- Pawn moves to the generated point
- Then he waits five seconds
Close BT_Muffin and click on Play . Create some muffins and get ready for the greatest Battle Royale!
GIF
Where to go next?
The finished project can be downloaded from here .
As you can see, creating a simple AI character is very easy. If you want to create a more complex AI, then check out the Environment Query System . This system allows the AI to collect data about the environment and respond to it.