Download

Behavior Tree Only Works for One AI Character Spawned

Hello,

I have created an AI character that has a behavior tree which makes the AI character move to the main character as soon as its TargetToFollow blackboard key is not null. This TargetToFollow blackboard key is set as soon as the main character overlaps with the AI character.

My code uses SpawnActor() to spawn multiple AI Characters in my level, and one of them works just fine.

All of the AI chracter’s OnActorBeginOverlap delegates are firing just fine, but only one of them actually executes its behavior tree service.

It’s a simple BT, pic attached.

Any explanations would be appreciated.

Thank you.

Each AI character is being spawned, and a new AI Controller is being spawned. The Posssess() function is being called just fine, and it appears that the Behavior Tree actually runs just fine for each character as well, after some debugging. However, the service SpiderBTService on seems to be ticking for one of the AI characters. I made this service in C++. Other Behavior Tree nodes seem to work (I used play sound and then wait 5000 seconds for each BT and each time an AI character is spawned I hear the sound). However, with a UE_LOG() in the TickNode() function of the service, I print a message to the screen with GetName() for the particular character, and only one of them prints this message. So the problem seems to be the custom C++ Service I created.

Any help would be appreciated.

Thanks.

Well, without being able to see the code i can just guess.
But if it only works for one Ai and all others are doing nothing or seem to just hang, my first guess would be that you accidentally used a static variable (which is shared among all instances) somewhere instead of a normal member variable (each instance of that class having its own).

For example, you might have a normal member variable “bool BusyDoingSomething” which gets set to true when the AI is busy doing a task (like walking or following a target) and false when the Ai should decide what to do next.

If it is a non-static variable (aka no static keyword : “bool BusyDoingSomething”) then it should work fine, since every Ai instance will have its own.BusyDoingSomething-check.

If however you have it as a static variable (aka “static bool BusyDoingSomething”), as soon as one AI sets it to true, all other Ais will also see it as true, so only the first one being able to set it to true will be able to do anything. Once it gets set to false only the first Ai setting it to true will then be able to do the next thing.

This is my code. The Blueprints version seems to work just fine.

#include “SpiderBTService.h”
#include “SpiderController.h”
#include “BehaviorTree/BlackboardComponent.h”
#include “BehaviorTree/Blackboard/BlackboardKeyAllTypes.h”
#include “BehaviorTree/BehaviorTree.h”
#include “Spider.h”

void USpiderBTService::TickNode(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, float DeltaSeconds)
{
Super::TickNode(OwnerComp, NodeMemory, DeltaSeconds);

if (BlackboardComp == nullptr || ThisController == nullptr || ThisAICharacter == nullptr)
{

ThisController = Cast<ASpiderController>(OwnerComp.GetAIOwner());

BlackboardComp = ThisController->GetBlackBoard();

ThisAICharacter = Cast<ASpider>(ThisController->GetPawn());

if (BlackboardComp == nullptr || ThisController == nullptr || ThisAICharacter == nullptr)
{
UE_LOG(LogTemp, Warning, TEXT(“Warning Agro Service performed on invalid AI”));

return;
}
}

if (ThisAICharacter)
{
UE_LOG(LogTemp, Warning, TEXT(“Spider %s service is ticking”), (ThisAICharacter->GetName()));
AActor
Target = ThisAICharacter->GetTargetActor();
if (Target)
{
UE_LOG(LogTemp, Warning, TEXT(“Target to follow: %s”), *(Target->GetName()));
ThisController->GetBlackBoard()->SetValueAsObject(TEXT(“TargetToFollow”), Target);

ThisController->GetBlackBoard()->SetValueAsVector(TEXT(“HomeLocation”), ThisAICharacter->GetActorLocation());

ThisController->GetBlackBoard()->SetValueAsVector(TEXT(“TargetLocation”), Target->GetActorLocation());
}
else
{
ThisController->GetBlackBoard()->SetValueAsObject(TEXT(“TargetToFollow”), nullptr);
}
}

}

Thank you so much. Helped me with a problem I had :slight_smile:

Did you ever manage to solve this?

It was a while ago… I think I ended up using a Service Blueprint instead.

Well without seeing all the related classes and based on my experience solving a similar problem I can only guess that you probably performed initialization for just one of the AI’s instances.

I could be wrong but this is what I understood -
Only a single instance of a behavior tree with its nodes is ever created.
Don’t expect to have a unique instance of the tree for every individual instance of the AI.

That said, you would need to initialize your references (BlackboardComp, ThisController, ThisAICharacter) every single time the task is executed, so probably it would be a good idea to omit the null check in these lines. (also, take care of performance, if you have to do a long operation to initialize a reference, e.g. a component, consider caching it on the pawn or the controller and fetching that instead. )

Now, if we have say AI_0 and AI_1, Unreal is going to execute the BTree tasks once for AI_0, and then for AI_1, meaning, if you didn’t re-initialize your references for the AI_1 you will end up using the references of AI_0.

This is what fixed my problem but my idea about behavior trees still isn’t concrete.
For anyone having a similar problem, this could be worth a try. Good luck! :slightly_smiling_face: