Relying on the kind of enemies you may have in your sport, you’ll create totally different AI habits. And there are a number of methods methods to create enemy AI in Unreal Engine, from the essential enemies that transfer between two factors all the best way to creating complicated AI habits utilizing Conduct Timber and Blackboards.
On this put up we’re going to study AI in Unreal Engine by creating primary, intermediate and superior enemy AI habits utilizing C++ and blueprints.
Obtain Property And Full Venture For This Tutorial
Unreal Engine Enemy AI C++ And Blueprints Tutorial
.single-product div.product .abstract kind.cart { show: none!necessary; }
Class: Uncategorized
Associated merchandise
To comply with together with this tutorial, please obtain the starter mission by clicking on the inexperienced Obtain belongings button above.
Within the downloaded folder you will discover the completed mission, and the starter mission which I ready so that you can comply with this tutorial.
Necessary Info Earlier than We Begin
One of many labels for this tutorial is newbie, nonetheless this isn’t a tutorial for full newbies.
I count on you to know methods to create primary video games in Unreal, however you’re a newbie in the case of AI programming in Unreal. So it’s necessary that you know the way to code in C++ and blueprints, and know methods to use Unreal and its interface.
One other factor to notice is that I’m utilizing Unreal Engine 4.27 for this tutorial, however the methods that I’m going to show you will be utilized to any Unreal Engine model.
Enemy AI Patrol
First we’re going to create a primary patrol habits for the enemy the place the enemy goes to roam within the degree.
Open the Enemy_AI_Starter_Project that you just downloaded. Open the EnemyAI_Map positioned in Content material -> Maps folder. Within the map you’ll discover the enemy and the participant actor are already ready.
Open the BP_Enemy blueprint positioned in Content material -> Blueprints folder. Within the Occasion Graph tab, Proper Click on and seek for customized occasion:
To make the enemy patrol within the degree, we’re going to use AI MoveTo perform, and we’re going to present it a random vacation spot inside the navigationable bounds quantity:
You possibly can copy the nodes from right here:
For AI MoveTo parameters we supplied self, which is a reference to the enemy blueprint, and for the vacation spot we used the GetRandomReachablePointInRadius which is a perform that can give us a reachable level within the radius we offer from the origin.
This perform will calculate all of the collisions which might be in the best way of the AI and that method the enemy will cleverly keep away from any obstacles in his path.
For the origin parameter of the GetRandomReachablePointInRadius perform we supplied the situation of the enemy, as a result of we’re going to patrol from the enemy’s location.
For the radius I’ve set 1500 as the worth, which suggests it should attempt to get a reachable level from the enemy’s location within the 1500 worth radius. In fact, we are able to change the radius worth to the next or a decrease quantity anytime we need to make the enemy patrol additional within the degree.
When the AI MoveTo perform finishes, we’re going to delay for 1 second utilizing the Delay perform, after which name the Random Patrol node we create in order that the enemy will randomly patrol the extent once more.
Earlier than we proceed to check this out, from the BeingPlay name the Random Patrol node we created:
Drag the NavMeshBoundsVolume within the degree and set the next values for its Location and Scale:
This may make the NavMeshBoundsVolume cowl the entire degree and this would be the navigationable space the place the enemy can transfer with the assistance of AI MoveTo perform:
Allow us to now run the sport and check it out:
We are able to additionally take away the delay that occurs after AI MoveTo perform finishes which can make the enemy patrol the extent with out stopping.
Detecting The Participant’s Presence And Transferring In direction of Him
We are able to use the identical perform to make the enemy run in the direction of the participant. However first, we’d like a option to detect the participant’s presence close to the enemy. For that we’re going to connect a Sphere Collision part to the enemy.
Within the blueprint editor for the BP_Enemy, within the Parts tab click on on Add Element and filter for sphere collision:
Within the My Blueprint tab, below variables create a brand new boolean variable and identify it Participant Detected. Then create one other variable and for the variable sort, click on on the variable icon:
Within the search bar filter for third individual and choose the Object Reference for the Third Individual Character:
With these two capabilities we’re going to detect when the Participant Collision Detection sphere collides with the participant actor in order that we are able to chase him, and when the participant actor exists the collision in order that we are able to cease chasing him.
However earlier than we try this, we’re going to create a customized occasion and identify it Transfer To Participant, which goes to make the enemy transfer in the direction of the participant actor:
You possibly can copy the nodes from right here:
As you may see, we’re utilizing the Participant REF variable, which is a reference to the participant actor within the sport, to get the situation of the participant and make the enemy transfer in the direction of him.
The Acceptance Radius parameter for the AI MoveTo perform is how removed from the goal will the AI cease transferring, on this case we set the worth to 150 models.
To get a reference to the participant actor, we have to detect collision in On Element Start Overlap that we created:
In OnComponentBeginOverlap we first carry out a solid to check if the participant actor has collided with the sphere, if that’s true, we are going to get a reference to the participant actor and we set the Participant Detected bool worth to true. We are going to use this variable to manage the enemy AI logic.
After which, we make the enemy go in the direction of the participant utilizing the Transfer To Participant customized occasion node we created.
In OnComponentEndOverlap, when the participant actor collides with the sphere, we’re going to set the Participant Detected worth to false, and make the enemy patrol randomly once more:
Compile and save the modifications and now let’s run the sport and check it out:
Whereas the enemy is detecting the participant’s presence and going in the direction of his location, now we have one massive subject, and that’s the enemy just isn’t following the participant when he strikes round, as a substitute it goes in the direction of the primary location the place the participant was after we detected the collision with the participant.
We are able to repair this subject by calling the Transfer To Participant within the Tick occasion, however this may make an issue after we need to assault the participant. As a substitute there’s a higher resolution to this downside that we’re going to implement now.
A Smarter Approach To Make The Enemy AI Chase The Participant
Because the AI MoveTo perform will transfer the AI to the primary place we go to it, we’d like a option to take a look at if the goal place has modified in order that the AI will transfer in the direction of the modified place.
For that, I’m going to create a brand new customized occasion, identify it Search participant and add the next nodes to it:
You possibly can copy the nodes from right here:
The Clear Timer By Perform identify will cease the timer from calling the perform with the required identify, in our case Search Participant which we supplied within the Perform Identify parameter.
We have to do that as a result of we specified that the Set Timer By Perform Identify ought to loop, which suggests it should run on a regular basis till we cease it through the use of Clear Timer By Perform Identify.
Now, we have to edit the Transfer To Participant perform:
You possibly can copy the nodes from right here:
Compile and save the brand new modifications to the BP_Enemy blueprint and let’s run the sport to check it out:
As you may see now, even when the participant modifications his location the enemy is consistently chasing him.
Enemy AI Assault
The thought is after we detect participant collision with the Participant Assault Collision Detection part, we are going to set the Can Assault Participant worth to true, and when the participant exists that collision we are going to set the worth to false:
We additionally must make modifications within the Transfer To Participant node in order that the enemy assaults the participant when it will get near him. First we’re going to create a brand new situation when the AI MoveTo finishes:
When the enemy will get to the participant’s location we’re going to test if the enemy can assault the participant, if that’s true we are going to name Cease Searching for Participant as a result of now we have to assault him, if the enemy can’t assault the participant, then we are going to name Search Participant once more.
To assault the participant, we’re going to use the montage animation I’ve ready which is the enemy’s assault animation:
You possibly can copy the nodes from right here:
The Play Montage perform takes a number of parameters that we have to present. The primary one is the Mesh which represents the Skeletal Mesh Element on which the animation might be performed.
For that, I supplied the Mesh part from the BP_Enemy:
For the Montage To Participant click on on the drop down record and choose the Mutant_Attack_Montage:
When the montage finishes taking part in, we take a look at if Participant Detected is true, if that’s the case we are going to make the enemy transfer in the direction of the participant by calling Search Participant and that can repeat this identical course of over once more.
Compile and save the modifications and let’s run the sport to check it out:
Attaching Collision Parts To Sockets
Now that we’re attacking the participant, allow us to additionally deal harm. To do that, we have to edit the skeleton of the mutant mannequin. Within the Content material -> Enemy_Model folder open Mutant_Skeleton within the editor:
Within the choices tab on the left aspect, find the RightHand within the skeleton hierarchy and Proper Click on ->Add Socket:
Rename the Field to Injury Collision, and within the Particulars tab below the Sockets settings for the Father or mother Socket discipline click on on the little loop icon and seek for the RightHandSocket which is the identify of the socket we created within the Mutant_Skeleton a number of moments in the past:
Because of this the Injury Collision will transfer together with the precise hand of the mutant mannequin. We do must reposition and resize the Injury Collision part, so set the next values for the situation:
And the next values for the Field Extent axis below the Form settings:
Now the Injury Collision part seems like this:
Animation Notification Occasions
The explanation why we went by means of all of this Field collision and socket arrange is as a result of we’re going to use the assault montage animation to set off the harm performance.
If we open the Mutant_Attack_Montage which is positioned in Content material ->Enemy Mannequin folder, and preview the animation we are going to see that the mutant is attacking with its proper hand:
Since we connected the Injury Collision part to the precise hand socket, when the assault animation is performed and the mutant strikes his proper hand, the Injury Collision part will transfer together with it and we are able to use that to detect the collision with the participant actor and deal harm.
To try this we have to add animation notifiers that can notify us when the animation is at a sure body.
We are able to try this by dragging the animation preview slider on the desired body within the animation timeline:
Or we are able to set the precise body we would like on the precise aspect of the Filter search bar within the animation timeline:
Once we are carried out with that, on the Notifies timeline, Proper Click on -> Add Notify -> New Notify and identify the brand new notify Assault Began:
We created a brand new animation notify, or notification, on body 11, which suggests after we play the assault animation and when the animation reaches body 11, the Assault Began notify might be known as, and we might be notified within the code when that occurs.
We additionally must create one other notification that can inform us that the animation has ended. Go on body 30, and create a brand new notify and identify it Assault Ended.
After you end, you will note two animation notifications within the assault animation timeline:
Earlier than we entry the notification occasions within the blueprint editor, we have to go contained in the BP_Enemy editor, and below variables create a brand new boolean variable and identify it Can Deal Injury:
Now open the BP_Enemy_Animation blueprint positioned in Content material -> Enemy_Model folder. Within the Occasion Graph tab, Proper Click on and seek for assault began:
In the identical method seek for the assault ended anim notify node. We have already got a reference to the BP_Enemy within the BP_Enemy_Animation blueprint, so we are able to use that variable to entry the Can Deal Injury bool to vary its worth when the assault has began and when the assault ends:
You possibly can copy the nodes from right here:
Going again to BP_Enemy blueprint, choose the Injury Collision part and within the Particulars tab below Occasions, click on on the inexperienced + button for the On Element Start Overlap:
Within the Occasion Graph tab, for the On Element Start Overlap of the Injury Collision part, add the next nodes:
You possibly can copy the nodes from right here:
When the Injury Collision part detects the collision with the participant actor, we’re going to test if we are able to deal harm to the participant, if that’s true, we are going to deal harm, or in our case print one thing to the console hehehe
Compile and save the modifications we made and let’s run the sport to check it out:
Each time the enemy attacked the participant and the collision was detected, we noticed Participant Broken printed within the prime left nook of the sport window.
In fact, in your sport, you’d add logic the place the participant can have a well being worth and that well being worth might be decreased when the enemy assaults him, however that is the essential logic that goes behind that.
Enemy AI C++ Model
Now we’re going to try the C++ model of the enemy AI that we created. First, in Content material -> C++ Lessons -> Enemy_AI folder, Proper Click on -> New C++ Class. Ensure that the category inherits from the Character then click on Subsequent:
public:
bool PlayerDetected;
bool CanAttackPlayer;
UPROPERTY(BlueprintReadWrite)
bool CanDealDamage;
class AEnemy_AICharacter* PlayerREF;
UPROPERTY(EditAnywhere)
class USphereComponent* PlayerCollisionDetection;
UPROPERTY(EditAnywhere)
class USphereComponent* PlayerAttackCollisionDetection;
UPROPERTY(EditAnywhere)
class UBoxComponent* DamageCollision;
These are all of the variables that we might want to create the enemy AI logic. And should you check out the variable names you’ll discover that these are the identical variables as those we used within the BP_Enemy blueprint.
For the CanDealDamage bool variable we added BlueprintReadWrite because the parameter within the UPROPERTY as a result of we have to entry that variable within the BP_Enemy_Animation and with the assistance of animation notifiers set it to true or false relying on the state of the assault animation.
The AEnemy_AICharacter declared on line 9 is definitely the category used to create the ThirdPersonCharacter blueprint that you could find in Content material -> ThirdPersonCPP -> Blueprints, which is the participant actor we use within the sport.
I’m addressing this simply to keep away from confusion, if there’s any, as a result of the category identify has enemy AI in it, and I didn’t need to create a brand new class simply to provide it one other identify and nonetheless use it for a similar goal with the identical code.
One factor that you’ll discover on strains 9, 12, 15, and 18, is that we’re utilizing ahead declaration to declare the variables that we’d like. In case you are not aware of the idea of ahead declaration, you may examine it by clicking right here.
Now open the MutantEnemy.cpp file, and first on the prime, above the category declaration add the imports we have to use the variables we declared within the .h file:
#embrace "Enemy_AICharacter.h"
#embrace "Parts/SphereComponent.h"
#embrace "Parts/BoxComponent.h"
Should you don’t know which incorporates to make use of for particular variables, you may check out my information on that subject by clicking right here.
Within the constructor of the mutant class we’re going to create these elements and fasten them to the basis part:
AMutantEnemy::AMutantEnemy()
{
// Set this character to name Tick() each body. You possibly can flip this off to enhance efficiency should you do not want it.
PrimaryActorTick.bCanEverTick = true;
PlayerCollisionDetection =
CreateDefaultSubobject(TEXT("Participant Collision Detection"));
PlayerCollisionDetection->SetupAttachment(RootComponent);
PlayerAttackCollisionDetection =
CreateDefaultSubobject(TEXT("Participant Assault Collision Detection"));
PlayerAttackCollisionDetection->SetupAttachment(RootComponent);
DamageCollision = CreateDefaultSubobject(TEXT("Injury Collision"));
DamageCollision->SetupAttachment(GetMesh(), TEXT("RightHandSocket"));
}
The code above is straight ahead as we’re simply creating the elements from code, however one particular factor that I’ll point out is on line 17 the place we’re attaching the DamageCollision variable to the mesh, we additionally must specify the socket identify the place we need to connect the DamageCollision, identical as what we did contained in the BP_Enemy blueprint.
Now we are able to create a blueprint out of the mutant enemy class, so go contained in the Content material -> Blueprints, Proper Click on -> Blueprint Class and for the All Lessons, click on on the drop down record and filter for mutantenemy:
Identify the brand new blueprint BP_Enemy_CPP and open it within the editor. First, choose the Mesh part within the Parts tab and within the Particulars tab for the Mesh settings choose the Mutant mannequin, and for the Animation settings choose the BP_Enemy_Animation_C blueprint:
As quickly as you choose the Mutant mannequin for the Mesh part, you’ll discover how the Injury Collision part is connected to the precise hand of the Mutant enemy:
We additionally must resize the Injury Collision identical method we did within the BP_Enemy blueprint:
And we have to change the Location X worth as nicely:
The final step is to resize the Radius for the Participant Collision Detection to 800:
And set the Radius for the Participant Assault Collision Detection to 200:
Small Fixes Earlier than We Code C++ AI Logic
Earlier than we proceed to code the AI habits with C++, we have to make some small modifications to the BP_Enemy_Animation blueprint.
As a result of now we’re utilizing BP_Enemy_CPP blueprint, we have to change the enemy reference variable to the kind of the blueprint we’re utilizing.
Open the BP_Enemy_Animation blueprint and within the My Blueprint tab create a brand new variable, identify it Enemy CPP REF and set the sort to BP_Enemy_CPP and selected Object Reference from the record:
From the Blueprint Initialize Animation occasion, as a substitute of getting the reference to the Enemy REF variable, we’re going to get a reference to Enemy CPP REF variable:
You possibly can copy the nodes from right here:
Now from the AnimGraph tab, open the Idle – Run State Machine after which open Idle – Run animation, and as a substitute of utilizing the Enemy REF variable, we’re going to use Enemy CPP REF variable to get the rate of the enemy actor for the aim of the idle / run animation:
You possibly can copy the nodes from right here:
We needed to make these modifications in any other case after we begin utilizing BP_Enemy_CPP we might see all sort of errors as a result of we are attempting to get the reference to the BP_Enemy contained in the BP_Enemy_Animation blueprint.
We additionally want to vary the code within the animation notifiers to make use of the Enemy CPP REF as a substitute of Enemy REF:
Creating And Setting Up The AI Controller Class
To create the AI logic we’re going to use the AIController class. Contained in the Content material -> C++ Lessons -> Enemy_AI folder, Proper Click on -> New C++ Class. Click on on the Present All Lessons checkbox and filter for ai controller, choose it and press Subsequent:
Within the subsequent window give the category a reputation MutantAIController and create the category. Earlier than write code within the new class we created, we have to specify that the BP_Enemy_CPP blueprint will use MutantAIController class.
Open BP_Enemy_CPP blueprint, choose the BP_Enemy_CPP(self) prime father or mother, and within the particulars tab below Pawn settings, for the AI Controller Class click on on the drop down record and choose the MutantAIController class:
Since we’re going to use the AI Controller class to manage the AI actor, we additionally must specify within the blueprint of the AI actor which AI Controller class goes to manage him.
AI Patrol Logic With C++
Now open Visible Studio and first we have to specify within the Construct.cs file that we need to use the navigation system. Within the Answer Explorer find the Enemy_AI.Construct.cs file and open it:
Within the PublicDependencyModuleNames add NavigationSystem on the finish:
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine",
"InputCore", "HeadMountedDisplay", "NavigationSystem" });
That is necessary if you wish to use the navigation system lessons, if we don’t add this line and attempt to use the navigation system, the code is not going to compile and we are going to see errors within the Output tab.
Now open the MutantAIController.h file, and add the next strains of code:
public:
void BeginPlay() override;
personal:
class UNavigationSystemV1* NavArea;
FVector RandomLocation;
public:
UFUNCTION()
void RandomPatrol();
On line 6 we declared UNavigationSystemV1 which is a variable that we’ll use to get random navigationable factors within the degree.
And the RandomPatrol perform goes to make the enemy actor randomly patrol the extent.
Now open the MutantAIController.cpp file, and on the prime, beneath the primary #embrace line, add the next line of code:
#embrace "NavigationSystem.h"
We have to embrace the navigation system with a purpose to use its performance. Inside BeginPlay we’re going to get a reference to the navigation system:
void AMutantAIController::BeginPlay()
{
Tremendous::BeginPlay();
NavArea = FNavigationSystem::GetCurrent(this);
RandomPatrol();
}
Tremendous::BeginPlay();
void AMutantAIController::RandomPatrol()
{
if (NavArea)
{
NavArea->K2_GetRandomReachablePointInRadius(GetWorld(), GetPawn()->GetActorLocation(),
RandomLocation, 15000.0f);
MoveToLocation(RandomLocation);
}
}
The second parameter is the origin location from which we’re going to seek for the reachable level, for that parameter we handed the situation of the actor that’s controller by the AI Controller which is our BP_Mutant_CPP.
#pragma as soon as
#embrace "CoreMinimal.h"
#embrace "AIController.h"
#embrace "MutantAIController.generated.h"
/**
*
*/
UCLASS()
class ENEMY_AI_API AMutantAIController : public AAIController
{
GENERATED_BODY()
public:
void BeginPlay() override;
personal:
class UNavigationSystemV1* NavArea;
FVector RandomLocation;
public:
UFUNCTION()
void RandomPatrol();
};
#embrace "MutantAIController.h"
#embrace "NavigationSystem.h"
void AMutantAIController::BeginPlay()
{
Tremendous::BeginPlay();
NavArea = FNavigationSystem::GetCurrent(this);
RandomPatrol();
}
void AMutantAIController::RandomPatrol()
{
if (NavArea)
{
NavArea->K2_GetRandomReachablePointInRadius(GetWorld(), GetPawn()->GetActorLocation(),
RandomLocation, 15000.0f);
MoveToLocation(RandomLocation);
}
}
Now open the MutantEnemy.h file and proper beneath the place we declared the DamageCollision variable, add the next strains of code:
class AMutantAIController* MutantAIController;
void OnAIMoveCompleted(struct FAIRequestID RequestID, const struct FPathFollowingResult& Consequence);
Since we specified that the MutantAIController is the controller of the MutantEnemy, we have to get a reference to it with a purpose to carry out AI actions, therefore the MutantAIController variable declaration.
The OnAIMoveCompleted perform will subscribe to the OnRequestFinished occasion to tell us when the AI motion has completed.
Now open the MutantEnemy.cpp file and beneath the final #embrace, add the next strains:
#embrace "MutantAIController.h"
#embrace "Navigation/PathFollowingComponent.h"
#embrace "AITypes.h"
We’d like the consists of with a purpose to use capabilities from the AIController and to have the ability to use FAIRequestID and FPathFollowingResult as parameters within the OnAIMoveCompleted perform.
In BeingPlay, we’re going to get a reference to the AIController and subscribe to the OnRequestFinished occasion:
void AMutantEnemy::BeginPlay()
{
Tremendous::BeginPlay();
MutantAIController = Forged(GetController());
MutantAIController->GetPathFollowingComponent()->OnRequestFinished.AddUObject
(this, &AMutantEnemy::OnAIMoveCompleted);
}
And contained in the OnAIMoveCompleted perform, we’re going to name the MutantAIController to make the AI patrol once more:
void AMutantEnemy::OnAIMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Consequence)
{
MutantAIController->RandomPatrol();
}
As you may see, the enemy is patrolling the extent cleverly avoiding all obstacles in its method. You possibly can change the radius parameter worth within the K2_GetRandomReachablePointInRadius perform to make the AI patrol farther from its location.
Detecting And Chasing The Participant
To make the AI chase the participant we have to detect the participant’s presence close to the enemy. For that we have to create capabilities that we’ll bind to on part being and finish overlap of the collision elements.
Within the MutantEnemy.h file beneath the road the place we declared the OnAIMoveCompleted perform, add the next strains of code:
UPROPERTY(EditAnywhere)
float StoppingDistance = 100.0f;
FTimerHandle SeekPlayerTimerHandle;
UFUNCTION()
void MoveToPlayer();
UFUNCTION()
void SeekPlayer();
UFUNCTION()
void StopSeekingPlayer();
UFUNCTION()
void OnPlayerDetectedOverlapBegin(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnPlayerDetectedOverlapEnd(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex);
UFUNCTION()
void OnPlayerAttackOverlapBegin(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnPlayerAttackOverlapEnd(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex);
UFUNCTION()
void OnDealDamageOverlapBegin(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
The StoppingDistance variable goes to find out the stopping distance between the enemy and the participant.
I’ve added the EditAnywhere parameter within the UPROPERTY which implies that we will edit this variable on the blueprint occasion, so you may selected to make the enemy cease at the next or decrease distance from the participant actor.
As for the SeekPlayerTimerHandler variable, we’re going to use it to create a timer that can name the SeekPlayer perform in a loop, the identical method we did within the BP_Enemy blueprint, and we’re additionally going to make use of that variable to cease the timer.
As for the capabilities that I’ve declared I gave them the identical names as those we created in BP_Enemy blueprint, and you’ll already assume that these capabilities will carry out the identical actions as those in BP_Enemy.
First we’re going to bind the capabilities to on part start and overlap occasions of our collision elements. Open MutantEnemy.cpp file and in BeginPlay beneath the road the place we bind the OnAIMoveCompleted add the next strains of code:
PlayerCollisionDetection->OnComponentBeginOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerDetectedOverlapBegin);
PlayerCollisionDetection->OnComponentEndOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerDetectedOverlapEnd);
PlayerAttackCollisionDetection->OnComponentBeginOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerAttackOverlapBegin);
PlayerAttackCollisionDetection->OnComponentEndOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerAttackOverlapEnd);
DamageCollision->OnComponentBeginOverlap.AddDynamic(this,
&AMutantEnemy::OnDealDamageOverlapBegin);
Now we’re going to create and code the capabilities one after the other beginning with MoveToPlayer perform. Under the OnAIMoveCompleted perform, add the next strains of code:
void AMutantEnemy::MoveToPlayer()
{
MutantAIController->MoveToLocation(PlayerREF->GetActorLocation(), StoppingDistance, true);
}
void AMutantEnemy::SeekPlayer()
{
MoveToPlayer();
GetWorld()->GetTimerManager().SetTimer(SeekPlayerTimerHandle, this,
&AMutantEnemy::SeekPlayer, 0.25f, true);
}
void AMutantEnemy::StopSeekingPlayer()
{
GetWorld()->GetTimerManager().ClearTimer(SeekPlayerTimerHandle);
}
void AMutantEnemy::OnPlayerDetectedOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
PlayerDetected = true;
SeekPlayer();
}
}
void AMutantEnemy::OnPlayerDetectedOverlapEnd(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
PlayerDetected = false;
StopSeekingPlayer();
MutantAIController->RandomPatrol();
}
}
void AMutantEnemy::OnPlayerAttackOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
CanAttackPlayer = true;
}
}
void AMutantEnemy::OnPlayerAttackOverlapEnd(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
CanAttackPlayer = false;
SeekPlayer();
}
}
void AMutantEnemy::OnDealDamageOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF && CanDealDamage)
{
// deal harm to participant
UE_LOG(LogTemp, Warning, TEXT("Participant Broken"));
}
}
void AMutantEnemy::OnAIMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Consequence)
{
if (!PlayerDetected)
{
MutantAIController->RandomPatrol();
}
else if (PlayerDetected && CanAttackPlayer)
{
StopSeekingPlayer();
// assault participant
UE_LOG(LogTemp, Warning, TEXT("Participant ATTACKED"));
}
}
As quickly because the enemy detects the participant it begins transferring in the direction of him, and when it reaches the participant, of CanAttackPlayer is true, we noticed that Participant ATTACKED was printed within the console.
Attacking The Participant And Dealing Injury
To make the enemy assault the participant we have to play the enemy assault montage animation. To do that, we’d like a number of variables.
Within the MutantEnemy.h file, on the backside of the category add the next strains of code:
UPROPERTY(EditAnywhere)
class UAnimMontage* EnemyAttackAnimation;
class UAnimInstance* AnimInstance;
UFUNCTION(BlueprintCallable)
void AttackAnimationEnded();
EnemyAttackAnimation is a reference to the enemy assault montage. AnimInstance is a reference to the anim occasion that can play the assault animation.
And the AttackAnimationEnded perform goes to behave like a delegate that can inform us when the animation has ended. Discover how I added BlueprintCallable within the UFUNCTION parameter, it is because we’re going to create one other notify occasion within the enemy assault montage, and we’re going to name AttackAnimationEnded from that occasion.
Earlier than we proceed just remember to declare the AttackAnimationEnded perform within the MutantEnemy.cpp file, you may click on right here to see a shortcut methods to declare perform.
Now save and compile the category and open BP_Enemy_CPP blueprint within the editor.
Within the Parts tab, choose the BP_Enemy_CPP(self) prime father or mother, and within the Particulars tab for the Enemy Assault Animation, click on on the drop down record and choose Mutant_Attack_Montage:
Compile and save the modifications made to the blueprint after which open the Mutant_Attack_Montage animation within the editor.
Close to the top of the assault animation below the Notifies timeline, Proper Click on -> Add Notify -> New Notify and provides the brand new notify identify Assault Animation Ended:
Save the brand new change and open the BP_Enemy_Animation blueprint within the editor. Within the Occasion Graph Proper Click on and within the search bar filter for assault animation ended notify:
Compile and save the brand new modifications and open MutantEnemy.cpp file. First we’re going to add the extra consists of that we have to play the assault animation. So beneath the final embrace add the next strains:
#embrace "Animation/AnimInstance.h"
#embrace "Animation/AnimMontage.h"
On the backside of BeginPlay perform get a reference to the anim occasion:
AnimInstance = GetMesh()->GetAnimInstance();
In OnAIMoveCompleted, make modifications in order that the enemy assaults the participant when it reaches his place and the CanAttackPlayer worth is about to true:
void AMutantEnemy::OnAIMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Consequence)
{
if (!PlayerDetected)
{
MutantAIController->RandomPatrol();
}
else if (PlayerDetected && CanAttackPlayer)
{
StopSeekingPlayer();
// assault participant
AnimInstance->Montage_Play(EnemyAttackAnimation);
}
}
If the enemy tries to assault the participant e.g. the assault animation begins taking part in, and the participant runs away from the enemy at that second, we’re going to cease the assault animation and begin chasing the participant once more.
For that, we have to make some modifications within the OnPlayerAttackOverlapEnd perform:
void AMutantEnemy::OnPlayerAttackOverlapEnd(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
CanAttackPlayer = false;
// cease the assault animation and chase the participant
AnimInstance->Montage_Stop(0.0f, EnemyAttackAnimation);
SeekPlayer();
}
}
Calling Montage_Stop perform from AnimInstance and passing 0.0f as the primary parameter will instantly cease taking part in the supplied animation which is the second parameter for that perform and in our case the EnemyAttackAnimation.
And the final step is to assault the participant once more when the animation ends, which we are going to do within the AttackAnimationEnded perform:
void AMutantEnemy::AttackAnimationEnded()
{
if (CanAttackPlayer)
{
AnimInstance->Montage_Play(EnemyAttackAnimation);
}
}
#embrace "CoreMinimal.h"
#embrace "GameFramework/Character.h"
#embrace "MutantEnemy.generated.h"
UCLASS()
class ENEMY_AI_API AMutantEnemy : public ACharacter
{
GENERATED_BODY()
public:
// Units default values for this character's properties
AMutantEnemy();
protected:
// Known as when the sport begins or when spawned
digital void BeginPlay() override;
public:
// Known as each body
digital void Tick(float DeltaTime) override;
// Known as to bind performance to enter
digital void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
public:
bool PlayerDetected;
bool CanAttackPlayer;
UPROPERTY(BlueprintReadWrite)
bool CanDealDamage;
class AEnemy_AICharacter* PlayerREF;
UPROPERTY(EditAnywhere)
class USphereComponent* PlayerCollisionDetection;
UPROPERTY(EditAnywhere)
class USphereComponent* PlayerAttackCollisionDetection;
UPROPERTY(EditAnywhere)
class UBoxComponent* DamageCollision;
class AMutantAIController* MutantAIController;
void OnAIMoveCompleted(struct FAIRequestID RequestID, const struct FPathFollowingResult& Consequence);
UPROPERTY(EditAnywhere)
float StoppingDistance = 100.0f;
FTimerHandle SeekPlayerTimerHandle;
UFUNCTION()
void MoveToPlayer();
UFUNCTION()
void SeekPlayer();
UFUNCTION()
void StopSeekingPlayer();
UFUNCTION()
void OnPlayerDetectedOverlapBegin(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnPlayerDetectedOverlapEnd(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex);
UFUNCTION()
void OnPlayerAttackOverlapBegin(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnPlayerAttackOverlapEnd(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex);
UFUNCTION()
void OnDealDamageOverlapBegin(class UPrimitiveComponent* OverlappedComp,
class AActor* OtherActor, class UPrimitiveComponent* OtherComp,
int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UPROPERTY(EditAnywhere)
class UAnimMontage* EnemyAttackAnimation;
class UAnimInstance* AnimInstance;
UFUNCTION(BlueprintCallable)
void AttackAnimationEnded();
};
#embrace "MutantEnemy.h"
#embrace "Enemy_AICharacter.h"
#embrace "Parts/SphereComponent.h"
#embrace "Parts/BoxComponent.h"
#embrace "MutantAIController.h"
#embrace "Navigation/PathFollowingComponent.h"
#embrace "AITypes.h"
#embrace "Animation/AnimInstance.h"
#embrace "Animation/AnimMontage.h"
// Units default values
AMutantEnemy::AMutantEnemy()
{
// Set this character to name Tick() each body. You possibly can flip this off to enhance efficiency should you do not want it.
PrimaryActorTick.bCanEverTick = true;
PlayerCollisionDetection =
CreateDefaultSubobject(TEXT("Participant Collision Detection"));
PlayerCollisionDetection->SetupAttachment(RootComponent);
PlayerAttackCollisionDetection =
CreateDefaultSubobject(TEXT("Participant Assault Collision Detection"));
PlayerAttackCollisionDetection->SetupAttachment(RootComponent);
DamageCollision = CreateDefaultSubobject(TEXT("Injury Collision"));
DamageCollision->SetupAttachment(GetMesh(), TEXT("RightHandSocket"));
}
// Known as when the sport begins or when spawned
void AMutantEnemy::BeginPlay()
{
Tremendous::BeginPlay();
MutantAIController = Forged(GetController());
MutantAIController->GetPathFollowingComponent()->OnRequestFinished.AddUObject
(this, &AMutantEnemy::OnAIMoveCompleted);
PlayerCollisionDetection->OnComponentBeginOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerDetectedOverlapBegin);
PlayerCollisionDetection->OnComponentEndOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerDetectedOverlapEnd);
PlayerAttackCollisionDetection->OnComponentBeginOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerAttackOverlapBegin);
PlayerAttackCollisionDetection->OnComponentEndOverlap.AddDynamic(this,
&AMutantEnemy::OnPlayerAttackOverlapEnd);
DamageCollision->OnComponentBeginOverlap.AddDynamic(this,
&AMutantEnemy::OnDealDamageOverlapBegin);
AnimInstance = GetMesh()->GetAnimInstance();
}
// Known as each body
void AMutantEnemy::Tick(float DeltaTime)
{
Tremendous::Tick(DeltaTime);
}
// Known as to bind performance to enter
void AMutantEnemy::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Tremendous::SetupPlayerInputComponent(PlayerInputComponent);
}
void AMutantEnemy::OnAIMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult& Consequence)
{
if (!PlayerDetected)
{
MutantAIController->RandomPatrol();
}
else if (PlayerDetected && CanAttackPlayer)
{
StopSeekingPlayer();
// assault participant
AnimInstance->Montage_Play(EnemyAttackAnimation);
}
}
void AMutantEnemy::MoveToPlayer()
{
MutantAIController->MoveToLocation(PlayerREF->GetActorLocation(), StoppingDistance, true);
}
void AMutantEnemy::SeekPlayer()
{
MoveToPlayer();
GetWorld()->GetTimerManager().SetTimer(SeekPlayerTimerHandle, this,
&AMutantEnemy::SeekPlayer, 0.25f, true);
}
void AMutantEnemy::StopSeekingPlayer()
{
GetWorld()->GetTimerManager().ClearTimer(SeekPlayerTimerHandle);
}
void AMutantEnemy::OnPlayerDetectedOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
PlayerDetected = true;
SeekPlayer();
}
}
void AMutantEnemy::OnPlayerDetectedOverlapEnd(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
PlayerDetected = false;
StopSeekingPlayer();
MutantAIController->RandomPatrol();
}
}
void AMutantEnemy::OnPlayerAttackOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
CanAttackPlayer = true;
}
}
void AMutantEnemy::OnPlayerAttackOverlapEnd(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF)
{
CanAttackPlayer = false;
// cease the assault animation and chase the participant
AnimInstance->Montage_Stop(0.0f, EnemyAttackAnimation);
SeekPlayer();
}
}
void AMutantEnemy::OnDealDamageOverlapBegin(UPrimitiveComponent* OverlappedComp,
AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex,
bool bFromSweep, const FHitResult& SweepResult)
{
PlayerREF = Forged(OtherActor);
if (PlayerREF && CanDealDamage)
{
// deal harm to participant
UE_LOG(LogTemp, Warning, TEXT("Participant Broken"));
}
}
void AMutantEnemy::AttackAnimationEnded()
{
if (CanAttackPlayer)
{
AnimInstance->Montage_Play(EnemyAttackAnimation);
}
}
If by any probability you see Participant Injury printed a number of instances within the Output Log if you assault, you may transfer the Assault Ended notify a little bit nearer to the Assault Began notify within the Mutant_Attack_Montage animation:
The place To Go From Right here
On this tutorial we noticed easy and intermediate methods how we are able to arrange enemy AI in Unreal Engine utilizing Blueprints and C++.
If you wish to study a extra superior method methods to arrange enemy AI utilizing Conduct Timber which provides you extra management and helps you create lifelike enemies, you are able to do that in my Enemy AI With Conduct Timber In Unreal Engine tutorial.