Victor's Behaviour tree basic zombie(C++/Blueprints) [TUTORIALl]

In tutorial, im going to show, step by step, how to create a very simple zombie AI that is idle until it sees a player, and when it sees it it goes to attack it melee.

The behavior tree is a very interesting feature, still WIP, and really barebones, you will find that the default tasks are really few and more are needed most of the time.
The system works by selecting a task to run, and execute it. When the task is finished, it searches for the next task to do.

Tasks
The Tasks are derived from BTTask_Blackboard if you want to create them in c++, and BTTask_Blueprint if you want to create them in blueprints. For my needs, most of the time ive found that keeping them in blueprints helps a LOT with prototyping, if i need a task that does strange things, i code the difficult part on C++, most of the times in my base AI Controller class or Bot Pawn, and then i call that function from blueprint. If you are worried of performance, you can still make the task purely C++ if you want.

Services
A service is some kind of minitask, that is called all the time. Its what i use to check sight, i have my ZombieSearchPlayer Service wich just calls the SearchPlayer() function in my c++ code( as its a bit more complex logic, i made it as c++ function). That service is called on a interval of time, you can set it one time and some random offset. Of course, if you use lower intervals, calling the service many times, you can slow the game, so better have care to what you are calculating. One example is that my SearchPlayer function was not very well optimized, as it searches all the pawns in the map and checks if one is a player, and calling that every 0.1 seconds on 15 zombies isnt good to the performance, so i added a check and only search for a player if you dont have a target already)

Composites
A Composite selects wich task to run, by default, you will see a number in the composites and tasks, thats is the order the behavior tree will check each task. The Sequence composite is one of the most useful, it makes tasks run one after another. Selector only runs a task if the one before failed. And Simple Parallel is the one to use if you want several tasks executing at once ( im using it for the melee attack, one task performs the damage and the animation, while other makes the bot run to target)

Decorators
You can attach Decorators to both Composites and Tasks. The Decorator is to control the flow, for example a very useful one is the Blackboard Decorator. In that one you can make it so it only makes the task in that branch execute if some blackboard value is set or not. The cool thing about Decorators is that you can make them as some kind of “event”. The Blackboard Decorator can override a different task that is runnning, if you set “Observer aborts” property to Both, it overrides its branch and every other branch that has lower priority (its to the right), If you click the Decorator, you will see that some nodes get colored, the ones that can be overriden. In the zombie logic, when the Enemy blackboard variable is set, the decorator is fired and cancels the default “wait” task.

Blackboard
The blackboard is VERY important, its a data asset, that you need to create in the editor. It holds the values your behavior tree uses. In the zombie example, it has Enemy variable only, as its the only needed. But in more complex AI i have, i have more than 10 values in the blackboard to drive different Decorators and add variables to tasks.
Tasks get their data from the Blackboard, yes, you could access the controller or pawn easily and use the values there. But if the data is in the blackboard, you can use it to drive Decorators, and see the variables at runtime easily when debugging.

Example
Enough theory, lets go to the actual example. The plan is a very basic zombie/creature/whatever AI that stands still until it sees a player, and if it sees the player it goes to hit him in the face.
You should do with ShooterGame example, editing it, or with a project that uses similar code. Becouse the shootergame already provides bot class that can die and take damage, wich is useful for the AI.

To start, make sure you have Behavior Tree Editor enabled. To enable it, go to Edit->Editor Preferences-> Experimental. Check the Behavior Tree Editor property.

When the BTEditor is enabled, create 2 new assets in your content browser. One of type Behavior Tree , and other of type Data Asset, use class Blackboard when it asks wich data asset class to use. Name them something logical, like SimpleZombieBT, and SimpleZombieBlackboard

Now, lets go to the Blackboard Data Asset. You will get a property window. Ignore the “Parent” tab, and in the “Keys” array, add one value, set the KeyType to type Object, and the name to Enemy, also, in the BaseClass under Key Type once you set that as object, i recomend you to set it as Character or your base player class, for safety.

Once that is set, go to the Behavior tree asset, and open it. You will be welcomed with a blueprint like grid with only one ROOT node.
Click that node, and set the Blackboard Asset value to the blackboard you just created. After that click in the dark grey bar on the lower part of the ROOT node, drag it( an arrow should appear), and like in blueprints, it prompts a menu. Select a “Sequence”, will be our basic sequence for the character.
Drag from the lower dark grey bar of the Sequence node, and create another sequence node. In that node, right click it, and select Add Decorator. Add a Blackboard Decorator. Click that decorator(blue), and see the properties.
Decoratr.PNG

That decorator will fire its branch whenever the “Enemy” key in the blackboard is set.
Now, add some pathfinding logic to the zombie, so it runs to the Enemy
Add a MoveTo Task node, and connect it to the Attack logic. In the task properties, put Acceptable Radius as 100, its a good value to end the pathfinding, then the hit logic will trigger.
Makse sure the Blackboard Key of the Move To node is set as Enemy. Becouse we obviously want the AI to move to the Enemy position.
Add a SimpleParallel composite attached to the sequence that has the blackboard.
That node makes a base task to run (left bottom grey bar), and while it runs it also runs the tasks attached to the right side. When the task finishes, the secondary tasks are stopped.
Add a MoveDirectlyToward task at the right side, set Acceptable Radius to 0, and make sure the blackboard key is Enemy.

Why 2 different “move” tasks?
Easy, becouse Move To uses actual pathfinding, while MoveDirectlyTowards doesnt. Thats why we are using Move To if its far, and Movedirectlytowards when its close.
At moment, the behavior tree looks like this:


If you try to run it, it will do nothing. Yet.

C++ Code
The important logic for the Melee attack and the Search Enemy functions are written in my own ACreatureAI and ACreatureBot classes. ACreatureAI inherits from AAIController and ACreatureBot from AShooterCharacter.

is the CreatureAI.h




UCLASS()
class ACreatureAI :  AAIController
{
	GENERATED_UCLASS_BODY()

	

	UPROPERTY(transient)
	TSubobjectPtr<class UBlackboardComponent> BlackboardComp;

	UPROPERTY(transient)
	TSubobjectPtr<class UBehaviorTreeComponent> BehaviorComp;

	virtual void Possess(class APawn* InPawn) OVERRIDE;

	virtual void BeginInactiveState() OVERRIDE;

	void Respawn();

	
	UFUNCTION(BlueprintCallable, Category = Behavior)
	void SetEnemy(class APawn* InPawn);
	UFUNCTION(BlueprintCallable, Category = Behavior)
	class AShooterCharacter* GetEnemy() const;


	


	UFUNCTION(BlueprintCallable, Category = Behaviour)
	bool PawnCanBeSeen(APawn * target);

       /* Checks sight to all pawns in map, sets enemy if it finds a thing */

	UFUNCTION(BlueprintCallable, Category = Behaviour)
	void SearchEnemyInView();

	
protected:
	int32 EnemyKeyID;		
};


The Cpp file is like this





#include "ShooterGame.h"



ACreatureAI::ACreatureAI(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
// create blackboard and behaviour components in the constructor
	BlackboardComp = PCIP.CreateDefaultSubobject<UBlackboardComponent>(this, TEXT("BlackBoardComp"));

	BehaviorComp = PCIP.CreateDefaultSubobject<UBehaviorTreeComponent>(this, TEXT("BehaviorComp"));

	bWantsPlayerState = true;
}



void ACreatureAI::Possess(APawn* InPawn)
{
	Super::Possess(InPawn);

	ACreatureBot* Bot = Cast<ACreatureBot>(InPawn);

	// start behavior
	if (Bot && Bot->BotBehavior)
	{		

		BlackboardComp->InitializeBlackboard(Bot->BotBehavior->BlackboardAsset);

                // Get the enemy blackboard ID, and store it to access that blackboard key later.
		EnemyKeyID = BlackboardComp->GetKeyID("Enemy");
		

		BehaviorComp->StartTree(Bot->BotBehavior);
	}
}

void ACreatureAI::BeginInactiveState()
{
	Super::BeginInactiveState();

	AGameState* GameState = GetWorld()->GameState;

	const float MinRespawnDelay = (GameState && GameState->GameModeClass) ? GetDefault<AGameMode>(GameState->GameModeClass)->MinRespawnDelay : 1.0f;

	GetWorldTimerManager().SetTimer(this, &ACreatureAI::Respawn, MinRespawnDelay);
}

void ACreatureAI::Respawn()
{
//	GetWorld()->GetAuthGameMode()->RestartPlayer(this);
}




void ACreatureAI::SetEnemy(class APawn* InPawn)
{
	if (BlackboardComp)
	{
		BlackboardComp->SetValueAsObject(EnemyKeyID, InPawn);
		SetFocus(InPawn);
	}
}

class AShooterCharacter* ACreatureAI::GetEnemy() const
{
	if (BlackboardComp)
	{
		return Cast<AShooterCharacter>(BlackboardComp->GetValueAsObject(EnemyKeyID));
	}

	return NULL;
}


void ACreatureAI::UpdateControlRotation(float DeltaTime, bool bUpdatePawn)
{
	// Look toward focus
	FVector FocalPoint = GetFocalPoint();
	if (!FocalPoint.IsZero() && GetPawn())
	{
		FVector Direction = FocalPoint - GetPawn()->GetActorLocation();
		FRotator NewControlRotation = Direction.Rotation();

		NewControlRotation.Yaw = FRotator::ClampAxis(NewControlRotation.Yaw);

		SetControlRotation(NewControlRotation);

		APawn* const P = GetPawn();
		if (P && bUpdatePawn)
		{
			P->FaceRotation(NewControlRotation, DeltaTime);
		}

	}
}


bool ACreatureAI::PawnCanBeSeen(APawn * target)
{
	if (target == NULL || GetPawn() == NULL)
	{
		return false;
	}
	FVector difference = target->GetActorLocation() - GetPawn()->GetActorLocation();
	float angle = FVector::DotProduct(difference, GetPawn()->GetActorRotation().Vector());

	if (LineOfSightTo(target, GetPawn()->GetActorLocation()) && angle >0)
	{
		return true;
	}
	else
	{
		return false;
	}
}


void ACreatureAI::SearchEnemyInView()
{
	APawn* MyBot = GetPawn();
	if (MyBot == NULL)
	{
		return;
	}

	const FVector MyLoc = MyBot->GetActorLocation();
	float BestDistSq = MAX_FLT;
	AShooterCharacter* BestPawn = NULL;

	//foreach all pawns in world
	for (FConstPawnIterator It = GetWorld()->GetPawnIterator(); It; ++It)
	{
		UE_LOG(LogShooterWeapon, Log, TEXT(" ENEMY SEEN %s "), *GetNameSafe(*It));
		if (PawnCanBeSeen(*It))
		{
			AShooterCharacter* TestPawn = Cast<AShooterCharacter>(*It);

			if (TestPawn && TestPawn->IsAlive() && Cast<ACreatureBot>(TestPawn) == NULL)
			{
				const float DistSq = (TestPawn->GetActorLocation() - MyLoc).SizeSquared();
 				if (DistSq < BestDistSq)
				{
					 BestDistSq = DistSq;
					BestPawn = TestPawn;
				 }
 			}
		}
	}

	if (BestPawn)
	{
		// We saw someone, so set him as target.
		SetEnemy(BestPawn);
	
	}
	
}


The important thing is the Search Enemy in View function, wich checks EVERY pawn in the map, and checks if it can be seen (PawnCanBeSeen function). If the pawn is visible, it casts it to CreatureBot, wich is the AI class, and if it isnt a AI class, then it can be a target, so store it. Get the closest pawn, and target that one. The SetEnemy function writes the enemy pawn to the blackboard, so it fires the decorators in the trees and can be used for the MoveTo and MoveToward classes.

The CreatureBot class is actually much simpler, as its just a character inheriting from ShooterCharacter, wich has the PerformMelee attack function and its several variables.

CreatureBot.h




UCLASS()
class ACreatureBot :  AShooterCharacter
{
	GENERATED_UCLASS_BODY()


	UPROPERTY(EditDefaultsOnly, Category = Behaviour)
	float AttackRange;
	UPROPERTY(EditDefaultsOnly, Category = Behaviour)
	float AttackDamage;
	
	UPROPERTY(EditAnywhere, Category = Behavior)
	class UBehaviorTree* BotBehavior;

	UFUNCTION(BlueprintCallable, Category = Behavior)
	void PerformMeleeAttack();

	float AccumulatedFiretime;

	virtual bool IsFirstPerson() const OVERRIDE;

	virtual void FaceRotation(FRotator NewRotation, float DeltaTime = 0.f) OVERRIDE;
	bool Attacking;
};


CreatureBot.cpp


// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.

#include "ShooterGame.h"


ACreatureBot::ACreatureBot(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	AIControllerClass = ACreatureAI::StaticClass();

	UpdatePawnMeshes();

	AttackRange = 100;
	AttackDamage = 10;	
	bUseControllerRotationYaw = true;
}


bool ACreatureBot::IsFirstPerson() const
{
	return false;
}
void ACreatureBot::FaceRotation(FRotator NewRotation, float DeltaTime)
{
	FRotator CurrentRotation = FMath::RInterpTo(GetActorRotation(), NewRotation, DeltaTime, 8.0f);

	Super::FaceRotation(CurrentRotation, DeltaTime);
}


void ACreatureBot::PerformMeleeAttack()
{
	const FVector StartTrace = GetActorLocation();
	const FVector ShootDir = GetActorRotation().Vector();
	const FVector EndTrace = StartTrace + ShootDir * AttackRange;



       //  We perform a sphere sweep, checking if there is something in the cylinder that trace creates, and if it finds something, damage it.
	static FName WeaponFireTag = FName(TEXT("WeaponTrace"));
	FCollisionQueryParams TraceParams(WeaponFireTag, true);
	TraceParams.AddIgnoredActor(this);
	TraceParams.bTraceAsyncScene = true;
	TraceParams.bReturnPhysicalMaterial = true;

	
	FHitResult Hit(ForceInit);
	GetWorld()->SweepSingle(Hit, StartTrace, EndTrace, FQuat::Identity, ECollisionChannel::ECC_Pawn, FCollisionShape::MakeSphere(25), TraceParams);


	if (Hit.Actor != NULL)
	{
		ACharacter *character = Cast<ACharacter>(Hit.GetActor());
		if (character != NULL)
		{
			FPointDamageEvent PointDmg;
			PointDmg.DamageTypeClass = UDamageType::StaticClass();
			PointDmg.HitInfo = Hit;
			PointDmg.ShotDirection = ShootDir;
			PointDmg.Damage = AttackDamage;
			character->TakeDamage(AttackDamage, PointDmg, Controller, this);			
		}
	}
}




The C++ part is now completed, time to actually make the tasks in blueprint, and add them to the behavior tree

Blueprint tasks
Now its time to add the actual “damage melee” task, and the “search player” service.

Create 2 new blueprints, one inheriting from BTTask_blueprintBase, and other from BTService_BlueprintBase.
Call the Task “Melee Attack”, and the Service “Search Enemy”.

Open the Search Enemy blueprint, and make it like this.

The Receive Tick function is not like a normal actor tick that its ticked every frame, tick is ticked by the interval you put in the service properties.

Now, Open the MeleeAttack blueprint, one is QUITE a bit more complicated, becouse i made it work with animation, and not attack instantly, instead, it starts the animation and it performs the damage check a bit later, driven by the AttackTime variable.

First part, the Receive Execute event.
event is called when the task is started, only once per task execution. Here, we use it to play the attack animation


The “Attack Delta” variable is a private float variable to control the time, as they “delay” node doesnt really work properly here, so we add the time logic in tick. The AttackAnimation variable is a editable animation montage.

The second part is the Tick event.


In "timer logic"we increase AttackDelta by DeltaSeconds, and when its more thatn Attack Time (editable float variable) we fire the actual attack logic.
The atack logic just casts the controller to get the pawn, and the pawn to get the PerformAttack function, to call it. then just end the task, calling “Finish Execute” with Success set to true.

With this, our 2 blueprints are done. yes, they can be 100 c++ perfectly fine, but with this, if i think i want to make the attacker spawn a particle effect when it attacks, or attach a light when the zombie searches, i can prototype it very fast.

Completing the Behavior Tree

Now that we have our 2 blueprints to search player and hit him in the face, we can complete the behavior tree at last. Open it.
Add the MeleeAttack task to the SimpleParallel node, under the left bar, wich should be of a different color
also right click on the topmost “Sequence” node, the one that doesnt have the decorator, and add a service, “Search Enemy”. then set a normal rate for it to Tick, 0.2 seconds is fine

The final look of our behavior tree is like this

Thats it, its done. To use it, create a new blueprint for CreatureBot, and set the Blackboard property to SimpleZombieBT. The engine should do the rest for you. Cool factor is even better if you have the behavior tree opened when the game is running, as you will see wich tasks are being executed and more data.

Yes, is insanely overkill by a simple AI such as zombie/creature . But system shines when you create a complex thing, as it simplifies quite a bit it, giving editor support for everything without having to add a shitload of properties to the controller, and lots of c++ code to deal with the behavior yourself.

As allways, if you have any doubt, problem, or similar, feel free to comment it here, and ill answer it.

awesome tutorial. I will be giving a go weekend. thanks!

not sure if works now im 4.1, im updating it just in case.

is awesome, thanks for the tutorial.

For people new to Behavior Trees. looks like an overwhelming amount of setup just to get that basic behavior working with all these different parts. But, once it is setup adding additional behaviors becomes quite simple, and moving things around and changing the design is also quite easy.

[=mikepurvis;23277]
is awesome, thanks for the tutorial.

For people new to Behavior Trees. looks like an overwhelming amount of setup just to get that basic behavior working with all these different parts. But, once it is setup adding additional behaviors becomes quite simple, and moving things around and changing the design is also quite easy.
[/]

For individual behaviours, its indeed a overhead. zombie can be done without BT just fine and work a be simpler. BT Shine when its complex AI with lots of things to do, and also for reusable tasks beetween different AIs (i use the see player service in all the monsters im creating). In those cases, it allows for a flexibility wich cant be matched by using blueprint alone or your own AI coded into c++.

Cool stuff , I am a huge fan of the Behavior Tree myself. It would be a great addition to our Wiki’s tutorials if you ever felt like adding it. Also, would you be willing to make a version of with only Blueprints?

, ill add them to the wiki, no problems about that. And yes, ill also explain how to do a blueprint only version.

Hello! Thank you for sharing!

I tried to do tutorial in 4.1. I had two problems:

  1. UpdateControlRotation: I can’t seem to find anything about function. It isn’t declared in the header file.

  2. I can’t seem to get the event tick function to fire off inside melee attack. It skips right over the task as if there was nothing in it. It does not hit any of the breakpoints. If I add a different event it detects it and goes through the code as I would expect.

[=Voident;28090]
Hello! Thank you for sharing!

I tried to do tutorial in 4.1. I had two problems:

  1. UpdateControlRotation: I can’t seem to find anything about function. It isn’t declared in the header file.

  2. I can’t seem to get the event tick function to fire off inside melee attack. It skips right over the task as if there was nothing in it. It does not hit any of the breakpoints. If I add a different event it detects it and goes through the code as I would expect.


[/]

add also the other part of the event graph, maiby thats the reason.
Update control rotation is on 4.1 as now ive checked, anyway, see if its Update Controller Rotation or something like that.

I got to work. Thank you very much! I’m using a reload animation because I don’t have a melee one right now.

any a blueprint only version was ever released ? thanks greatly for at least is a starting point for us.

[=;28453]
any a blueprint only version was ever released ? thanks greatly for at least is a starting point for us.
[/]

To launch the Behavior tree, you can just do it on the pawn blueprint. And here i show mixed tasks, that are declared in blueprint, but then call a c++ function. You can just do your whole blueprint logic inside those Task blueprints.

I’ve tried and all I get on compile are errors about "Expected an include at the top of the header ‘#include “CreatureBot.generated.h”’

if I add it complains that it has to be the last include… blah blah…

Any thoughts?

Thank you for the tutorial

[=;37965]
I’ve tried and all I get on compile are errors about "Expected an include at the top of the header ‘#include “CreatureBot.generated.h”’

if I add it complains that it has to be the last include… blah blah…

Any thoughts?
[/]

can you post the code maybe you just have a typo?

Thanks for the tutorial! However, when I try to compile the code, I recive error:

CreatureAI.h(42) : Expected an include at the top of the header: ‘#include “CreatureAI.generated.h”’

I have copy/pasted the code, so there should be no random typo if not the name or location of the scripts is incorrect? But im almost sure its not.
any help would be great, thx :slight_smile:

[=MADHOUSE;42597]
Thanks for the tutorial! However, when I try to compile the code, I recive error:

CreatureAI.h(42) : Expected an include at the top of the header: ‘#include “CreatureAI.generated.h”’

I have copy/pasted the code, so there should be no random typo if not the name or location of the scripts is incorrect? But im almost sure its not.
any help would be great, thx :slight_smile:
[/]

The code given is not meant to use as direct copypaste, its meant to be an example, and thats why i havent added the #include “creatureAI.generated.h” at the top.

[=;42630]
The code given is not meant to use as direct copypaste, its meant to be an example, and thats why i havent added the #include “creatureAI.generated.h” at the top.
[/]

“In tutorial, im going to show, step by step”

I assumed it was a completed tutorial sry. At point the tutorial has me confused as how to even compile the new code into the project :S
I am searching for an answer but find no explenation so far. If I try to add the code from UE4 editor menu “add new code to project”, there is no way of telling what class type is correct D:

[=MADHOUSE;42706]
“In tutorial, im going to show, step by step”

I assumed it was a completed tutorial sry. At point the tutorial has me confused as how to even compile the new code into the project :S
I am searching for an answer but find no explenation so far. If I try to add the code from UE4 editor menu “add new code to project”, there is no way of telling what class type is correct D:
[/]

tutorial is aimed for intermediate to advanced programmers, to extend over the basic concepts that are assumed as known. I really dont reccomend you to try to follow tutorial if you arent sure of the basics of coding for unreal engine. shows a mixed blueprint-c++ workflow for people that already know how to code properly, But the system can be used purely on blueprints.

[=;25056]
, ill add them to the wiki, no problems about that. And yes, ill also explain how to do a blueprint only version.
[/]

Hey, just wanted to follow-up and thank you for this. I have been directing people to the tutorial for getting started with BTs.