Hey guys,
I’m a starter at UE and trying to make a little game with some Ai controlled Minions that attack each other depending on the team they are on. I did a behavior tree and an AiController and everything seems to be working fine, until a minion wants to attack another one. First, here is a picture of my behavior tree:
It seems like only one minion is actually attacking while the other only receives damage. Actually, if I send only a minion at a time from each side(team), the behavior seems normal,
Find the enemy base → go to it → attack any minion on your way → once you killed it, continue to the enemy base → attack it
But it seems like when a spawn one every 5 seconds, they start to act weird, not dying or taking way too long to do so, not attacking or just completely ignoring the other minions… Even destroying the enemy base while being clearly not in range!
I think my problem might be around the “EBTNodeResult::Succeeded” that I might not use properly… Or around the BTTask_Cooldown.cpp… i juste wanted a timer that i could control but it turned out to be more complicated than I thought…
So here is some code:
BTTask_Attack.cpp
#include "ProjetDEC.h"
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BlackBoard/BlackboardKeyAllTypes.h"
#include "Minion.h"
#include "Building.h"
#include "MinionAI.h"
#include "BTTask_Attack.h"
EBTNodeResult::Type UBTTask_Attack::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) {
//getting the controller
AMinionAI *MinionPC = Cast<AMinionAI>(OwnerComp.GetAIOwner());
MinionPC->StopMovement();
//getting the minion controlled by the MinionAI controller
AMinion *ThisMinion = Cast<AMinion>(MinionPC->GetPawn());
int temp=0;
//get enemy actor (minion or building)
AActor *Enemy = Cast<AActor>(OwnerComp.GetBlackboardComponent()->GetValue<UBlackboardKeyType_Object>(MinionPC->EnemyKeyID));
if (dynamic_cast<AMinion*>(Enemy)){
AMinion *EnemyMinion = Cast<AMinion>(Enemy);
if (EnemyMinion->GetHealth() <= ThisMinion->GetPhysicalDamage()) {
EnemyMinion->ReceiveDamage(ThisMinion->GetPhysicalDamage());
OwnerComp.GetBlackboardComponent()->SetValue<UBlackboardKeyType_Object>(MinionPC->EnemyKeyID, nullptr);
OwnerComp.GetBlackboardComponent()->SetValueAsBool("EnemyClose", false);
OwnerComp.GetBlackboardComponent()->SetValueAsBool("InRange", false);
return EBTNodeResult::Succeeded;
}
else {
EnemyMinion->ReceiveDamage(ThisMinion->GetPhysicalDamage());
return EBTNodeResult::Succeeded;
}
}
else {
ABuilding *EnemyBuilding = Cast<ABuilding>(Enemy);
if (EnemyBuilding->GetHealth() <= ThisMinion->GetPhysicalDamage()) {
EnemyBuilding->ReceiveDamage(ThisMinion->GetPhysicalDamage());
OwnerComp.GetBlackboardComponent()->SetValue<UBlackboardKeyType_Object>("Target", nullptr);
OwnerComp.GetBlackboardComponent()->SetValueAsBool("EnemyClose", false);
OwnerComp.GetBlackboardComponent()->SetValueAsBool("InRange", false);
return EBTNodeResult::Succeeded;
}
else {
EnemyBuilding->ReceiveDamage(ThisMinion->GetPhysicalDamage());
return EBTNodeResult::Succeeded;
}
}
return EBTNodeResult::Failed;
}
BTTask_Cooldown
#include "ProjetDEC.h"
#include "BehaviorTree/BehaviorTree.h"
#include "BehaviorTree/BehaviorTreeComponent.h"
#include "BehaviorTree/BlackboardComponent.h"
#include "BehaviorTree/BlackBoard/BlackboardKeyAllTypes.h"
#include "Minion.h"
#include "MinionAI.h"
#include "BTTask_Cooldown.h"
EBTNodeResult::Type UBTTask_Cooldown::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) {
//getting the controller
TaskOwnerComp = &OwnerComp;
AMinionAI *MinionPC = Cast<AMinionAI>(OwnerComp.GetAIOwner());
//getting the minion controlled by the MinionAI controller
AMinion *ThisMinion = Cast<AMinion>(MinionPC->GetPawn());
OwnerComp.GetBlackboardComponent()->SetValueAsBool("OnCooldown", true);
GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &UBTTask_Cooldown::OnTimerEnd, ThisMinion->GetAttackDelay(),false);
return EBTNodeResult::Succeeded;
}
void UBTTask_Cooldown::OnTimerEnd() {
TaskOwnerComp->GetBlackboardComponent()->SetValueAsBool("OnCooldown", false);
}
Minion.cpp
#include "ProjetDEC.h"
#include "Minion.h"
AMinion::AMinion()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
}
// Called when the game starts or when spawned
void AMinion::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AMinion::Tick( float DeltaTime )
{
Super::Tick( DeltaTime );
if (Health <= 0) {
Die();
}
}
void AMinion::Die() {
Destroy();
}
// Called to bind functionality to input
void AMinion::SetupPlayerInputComponent(class UInputComponent* inputComponent)
{
Super::SetupPlayerInputComponent(InputComponent);
}
Minion.h
#pragma once
#include "GameFramework/Character.h"
#include "Minion.generated.h"
UCLASS()
class PROJETDEC_API AMinion : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AMinion();
UPROPERTY(EditAnywhere, Category = Behavior)
class UBehaviorTree *BotBehavior;
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Called every frame
virtual void Tick( float DeltaSeconds ) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* inputComponent) override;
//custom functions
virtual void Die();
void ReceiveDamage(int DamageDealt) { Health -= DamageDealt; };
//getters and setters
float GetMouvementSpeed() { return MouvementSpeed; };
float GetAttackRange() { return AttackRange; };
float GetVisionRange() { return VisionRange; };
int GetAttackDelay() { return AttackDelay; };
int GetPhysicalDamage() { return PhysicalDamage; };
int GetPhysicalResistance() { return PhysicalResistance; };
bool GetTeam() { return teamA; };
int GetHealth() { return Health; };
void SetTeam(bool team) { teamA = team; };
private:
float RunningTime;
float MouvementSpeed = 0.5;
float AttackRange = 500.0f;
float VisionRange = 2000.0f;
int AttackDelay = 1; //bigger is slower
int Health = 200;
int PhysicalDamage = 40;
int PhysicalResistance = 10;
bool teamA;
};
I realize this might be a tough question. Any help will be appreciated. Thanks!