Behavior Tree Tutorial

Disclaimer right up front. This isn’t complete, I’ll edit it to fill it out. Also, I’m on a slightly older build of UE4 and I’m not sure what’s changed with today’s version.

Edit: In the tread that follows, Epic devs share many things about the UE4 Behavior Tree that point out problems with how I show to do them here. I was intending to update my post, but I switched to using Blueprints only for my Behavior Trees. Go to post #5 in this thread for step by step insructions to set up Simple Wandering AI. https://forums.unrealengine.com/showthread.php?1270-UE4-Pathfinding-basic-AI&highlight=pathing

UE4 brings a new Editor, the Behavior Tree Editor. As far as I last knew it’s experimental so using it comes with the understanding it might be completely changed and your work will have to be at least partially redone that involved it. It’s also un-documented and incomplete.

Then, why would you want to use it? Well, because it’s awesome that’s why. Here’s a screenshot.

Being able to change the AI behaviors in a visual Editor is really cool. Also, debugging in it works. You can see the execution paths through the Tree.

Edit: You can now right click and create these assets from the content browser, BlackBoard is DataAsset and BehaviorTree is Miscellaneous.
ShooterGame is the example to use to get yourself up and running with a BehviorTree, but you’ll also have to copy paste two uassets in from the project. At the time of the last build you could not create a new Behavior Tree or Blackboard component in the Editor. So, copy those two items from the ShooterGame into your own project. You then can rename them and clear everything out from their editor.

There are some concepts that are pretty much considered common terminology in Behavior Trees in game programming, but each implementation will vary slightly.

For a really great introduction to Behavior Tree’s in general refer to AiGameDev’s website: http://aigamedev.com/insider/tutorial/second-generation-bt/

I’ll just cover the basics before I show which classes you need at a minimum to support using UE4’s built in Behavior Tree system.

There are two main types of nodes. Composite Nodes and Leaf Nodes. Composites have children, leaf’s do not. There is a 3rd type which is a decorator, you can see on in the screenshot labled CoolDown, but I’ll just focus on the two main types. All the nodes return a value, if if succeeded, failed, or is running are 3 main types. There are two main types of composite nodes, Sequences and Selectors. These are the building blocks of all your decision making logic.

Both of them evaluate their children from left to right, but a Sequence Node will only go on to the next child if the previous one returned a success. The sequence node itself will only return a success if all of it’s children return a success.

The Selector node is similiar in that it evaluates it’s chlidren from left to right, but it will only try the next one if the previous failed. It will try each chlid in turn seeing if it returns a success. On the first success it finds, it stops evaluating the children and returns a success.

The leaf nodes are like the leaves on a tree, where the composite nodes are like the branches. The leaf nodes do not have children. There are two main types of leaf nodes. A condition and an Action. You use the Conditions to find out something, like the state of a variable ( Is health low? ) and and Action, you guessed it, performs an action.

You need to add some things to your Bot’s Character and Controller Classes in C++, and then hook them up in the Blueprint Editor to the uassets. The Tree itself you make in the Editor and it comes with several nodes, to make custom conditions and actions you extend UBTTask_BlackboardBase. It’s simpler than it sounds.

I have some extra things in here that you may want to do a different way for handling animations. This is not the only way to do it. I’ll leave it in here for now.

Here is what you need to add to the Character’s header:



#pragma once

#include "Bot.generated.h"

/**
 * Base class for all Bots
 */
UCLASS()
class ABot :  ACharacter
{
	GENERATED_UCLASS_BODY()

	UPROPERTY(EditAnywhere, Category=Behavior)
	class UBehaviorTree* BotBehavior;

	//-- Animations
	
	//-- play anim montage 
	virtual float PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate = 1.f, FName StartSectionName = NAME_None) OVERRIDE;

	//-- play anim montage 
	virtual float PlayAnimMontage(const FString AnimMontageName, float InPlayRate = 1.f, FName StartSectionName = NAME_None);

	//-- stop playing montage 
	virtual void StopAnimMontage(class UAnimMontage* AnimMontage) OVERRIDE;

	//-- stop playing all montages 
	void StopAllAnimMontages();

	//-- Reading data

	//-- get mesh component 
	USkeletalMeshComponent* GetPawnMesh() const;

protected:
	//-- search anim 
	UPROPERTY(EditDefaultsOnly, Category=Pawn)
	UAnimMontage* SearchAnim;

	//-- clean anim 
	UPROPERTY(EditDefaultsOnly, Category=Pawn)
	UAnimMontage* CleanAnim;

	//-- pounce anim 
	UPROPERTY(EditDefaultsOnly, Category=Pawn)
	UAnimMontage* PounceAnim;

	//-- turn left anim 
	UPROPERTY(EditDefaultsOnly, Category=Pawn)
	UAnimMontage* TurnLeftAnim;

	//-- turn right anim 
	UPROPERTY(EditDefaultsOnly, Category=Pawn)
	UAnimMontage* TurnRightAnim;

	//-- turn right anim 
	UPROPERTY(EditDefaultsOnly, Category=Pawn)
	UAnimMontage* TrotAnim;

	//-- time when we will finish playing animation 
	float AnimationEndTime;

	//-- if pawn is playing animation 
	bool bIsPlayingAnimation;
};

FORCEINLINE USkeletalMeshComponent* ABot::GetPawnMesh() const
{
	return Mesh;
}


The .cpp to go with it.




#include "YourGame.h"

ABot::ABot(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
	,AnimationEndTime(0)
	,bIsPlayingAnimation(false)
{
}

//////////////////////////////////////////////////////////////////////////
// Animations

float ABot::PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate, FName StartSectionName) 
{
	USkeletalMeshComponent* UseMesh = GetPawnMesh();
	if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance)
	{
		if(UseMesh->AnimScriptInstance->Montage_IsPlaying(AnimMontage))
		{
			return true;
		}

		return (UseMesh->AnimScriptInstance->Montage_Play(AnimMontage, InPlayRate));
	}

	return false;
}

float ABot::PlayAnimMontage(FString AnimMontageName, float InPlayRate, FName StartSectionName)
{
	UAnimMontage* AnimMontage = NULL;

	if(AnimMontageName == "CleanSelf")
	{
		AnimMontage = this->CleanAnim;
	}
	else if(AnimMontageName == "SearchLaser")
	{
		AnimMontage = this->SearchAnim;

	}
	else if(AnimMontageName == "Pounce")
	{
		AnimMontage = this->PounceAnim;

	}

	return PlayAnimMontage(AnimMontage, InPlayRate, StartSectionName); 
}

void ABot::StopAnimMontage(class UAnimMontage* AnimMontage)
{
	USkeletalMeshComponent* UseMesh = GetPawnMesh();
	if (AnimMontage && UseMesh && UseMesh->AnimScriptInstance &&
		UseMesh->AnimScriptInstance->Montage_IsPlaying(AnimMontage))
	{
		UseMesh->AnimScriptInstance->Montage_Stop(AnimMontage->BlendOutTime);
	}
}

void ABot::StopAllAnimMontages()
{
	USkeletalMeshComponent* UseMesh = GetPawnMesh();
	if (UseMesh && UseMesh->AnimScriptInstance)
	{
		UseMesh->AnimScriptInstance->Montage_Stop(0.0f);
	}
}


The controller class header:



#pragma once

#include "YourController.generated.h"




/**
 * 
 */
UCLASS()
class ABotController :  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;

        /** This sets a Blackboard component*/
	void SetLaser(class ALaserAttractor* InLaser);

	/* If there is line of sight to current enemy, start firing at them */
	UFUNCTION(BlueprintCallable, Category=Behavior)
	void ChaseTarget();

	/* This returns the Blackboard component, I'll update the tutorial to make it more generic */
	UFUNCTION(BlueprintCallable, Category=Behavior)
	class ALaserAttractor* GetLazor() const;

protected:
	int32 LaserKeyID;
};



and here is the Controller’s .cpp functions.



#include "YourGame.h"
//#include "ABotController.h"


ABotController::ABotController(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{
	BlackboardComp = PCIP.CreateDefaultSubobject<UBlackboardComponent>(this, TEXT("BlackBoardComp"));
 	Components.Add(BlackboardComp);
 	
 	BehaviorComp = PCIP.CreateDefaultSubobject<UBehaviorTreeComponent>(this, TEXT("BehaviorComp"));
 	Components.Add(BehaviorComp);

	bWantsPlayerState = true;
	PrimaryActorTick.bCanEverTick = true;
}



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

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

	// start behavior
	if (Bot && Bot->BotBehavior)
	{
		BlackboardComp->InitializeBlackboard(Bot->BotBehavior->BlackboardAsset);

		LaserKeyID = BlackboardComp->GetKeyID("Laser");
		

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

void ABotController::BeginInactiveState()
{
	Super::BeginInactiveState();
}

void ABotController::SetLaser(class ALaserAttractor* InLaser)
{
	if (BlackboardComp)
	{
		BlackboardComp->SetValueAsObject(LaserKeyID, InLaser);
		SetFocus(InLaser);
	}
}


class ALaserAttractor* ACatAIController::GetLazor() const
{
	if (BlackboardComp)
	{
		return Cast<ALaserAttractor>(BlackboardComp->GetValueAsObject(LaserKeyID));
	}

	return NULL;
}


Hit text limit, to be continued…

1 Like

Here is part two:

Then you need to make some Conditions and Actions. I have just been using the same pattern for them, extending from UBTTask_BlackboardBase in both cases. These become available in the Behavior Tree Editor after you compile them.

Here is the header.



#pragma once

#include "BTTask_YourAction.generated.h"

/**
 * 
 */
UCLASS()
class BTTask_YourAction:  UBTTask_BlackboardBase
{
	GENERATED_UCLASS_BODY()

	virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory) const OVERRIDE;
};


and then the .cpp file. You can check a variable in the case of a condition node, or attempt an action. Just return the status code you want.



#include "YourGame.h"
//#include "BTTask_YourAction.h"


UBTTask_YourAction::UBTTask_YourAction(const class FPostConstructInitializeProperties& PCIP)
	: Super(PCIP)
{

}

EBTNodeResult::Type UBTTask_YourAction::ExecuteTask(UBehaviorTreeCompo  nent* OwnerComp, uint8* NodeMemory) const
{
	UBehaviorTreeComponent* MyComp = OwnerComp;
	ABotController* MyController = MyComp ? Cast<ABotController>(MyComp->GetOwner()) : NULL;
	if (MyController == NULL)
	{
		return EBTNodeResult::Failed;
	}
	else
	{

                // GetSomeLocation is just made up here, it's just an example
		FVector Destination = MyController->GetSomeLocation();

                // Make a move
		MyController->MoveToLocation(Destination,60.0f);
		return EBTNodeResult::Succeeded;

	}

	return EBTNodeResult::Failed;
}



Here is a video showing what what a cat can do with a Behavior Tree for a brain. There are more functions at work in the video, there are 2 conditions and 4 actions that I did not show in this tutorial, as well as the code that deals with the laser pointer. But, that stuff is unique to a cat chasing a laser. It is pretty easy to make Tasks that do one small thing, such as retrieve a variable, or move to a location. The Behavior Tree Editor can then be used to put those tasks together in a way that is easy to experiment with.

Thanks a lot for taking your time and creating this basic BT tutorial, . Especially in current absence of documentation. Much appreciated!

In general our BT implementation is compatible with vanilla BT theory, however we do have a number of improvements and I’d hate to see people miss-use features or adopt wrong design patterns.

This is not fully true in UE4. We call leaf nodes Tasks and as such by definition are expected to actually do something. Of course you can implement tasks returning value based on some condition, but that’s not a pattern we encourage. The best way to implement conditions is to use Decorators, which have a lot of additional functionality in regards to condition checking. We’re currently working on BT documentation which will describe this in detail.

Deriving from UBTTaskNode would be enough here since you don’t use blackboard in your code.

The biggest issue with this implementation is that, even though it makes AI to perform a time consuming action, it finishes instantly. That’s why you can see constant behavior switching in your BT in the video, meaning constantly traversing the tree. The main advantage of Behavior Trees over Decision Trees is that BT can execute sequences of actions with little effort, simply by waiting for current action to finish and carry on from there, where decision trees tick on a regular interval to pick the best action for current world context.

In general you can divide tasks into two groups: instant and latent. You can always have instant tasks that simply run some code and instantly return control over to BT, but it’s latent tasks that make BT shine. When a BT triggers a latent task, the task makes AI do something that takes a certain amount of time (like moving somewhere) and returns EBTNodeResult::InProgress result to let BT know it can go to sleep and will be notified when task finishes.

The high level idea is that BT’s goal is to pick an action to be performed by AI, but the action implementation details are of no interest to BT. It’s like a conscious mind and the body - the mind tell the body what to do, but knows nothing about how to do it. It’s body’s responsibility to know (yeah, ok, in reality it’s primitive brain’s part, but you get the picture).

Thank you both for touching on this topic, this looks very interesting. Anyway to access this without delving into the code, I’m looking to keep things mainly in blueprints.

Oh but you can do all that in BP! BT tasks, conditions and services (a special kind of node that tick as long as relevant subtree is active) can all be implemented in BP. And to run an AIController run specified BT you just call “RunBehaviorTree” on it. Simple as that! :smiley:

Thanks . I did see the progress status and just wasn’t using it yet.

Thank you for adding the information, I just did what I could to make it work, without documentation I was just guessing at it.

I didn’t show the example of the Blackboard use, but the laser dot is actually a Blackboard item that is being used. I’ll change my conditions to be decorators and start returning the in progress response in my project. I’ll update the initial tutorial thread to reflect that use.

@Bobbyremone
I choose to use the C++ because it’s what I’m most comfortable with. The BT Nodes can be implemented in BluePrints as well. It was choice on my part, not necessity.

Thanks !

Yeah that makes a lot of sense, thank you for clarifying that.

So should we follow that tutorial or wait for something more official? My project will be heavily dependent on AI, the way i see it i want my AIController have few sub-BT that gather information depending on their role and then send their requests to the Main-BT that analyze and prioritize all that and then send orders to every pawn.

A big job for someone who isnt an AI Dev but at least BT would make the job easier

Why wait?

I know the tutorial I put up here isn’t perfect, but it can get you started, especially with incorporating the comments of the Epic Dev that posted. I have not finished porting my game over from the Beta build yet. Once I update it to take into consideration 's points such as doing conditions as decorators I’ll update the tutorial here. I plan to move it to the wiki, but that’s going to be a week or two.

thast’s awesome, I’ll give it a look tomorrow

We’re working on behavior tree documentation right now; as soon as it’s ready I expect we’ll make it available and probably move Behavior Trees out of the “Experimental” section of the Editor UI at the same time.

By the way, you can create behavior trees directly in the editor; all of our “experimental” code support can be toggled on (as it’s off by default) through “Edit->Editor Preferences->General - Experimental” tab. Just check the “Behavior Tree Editor” option if you want to try out the option and you don’t have to copy and paste from ShooterGame. (To create a new asset, right-click in the Content Browser, choose Miscellaneous at the bottom, and you’ll see a Behavior Tree option.) It’s still a good idea to look at the ShooterGame ones as an example though. :slight_smile:

As Mieszko noted, you can do everything in our behavior trees using blueprints if you’re hesitant to write code yourself. put up an excellent video about Behavior Tree coding, but for anyone who doesn’t care about coding or doesn’t even know anything about behavior trees, you may want to watch this other one in the meantime: http://aigamedev.com/insider/presentation/behavior-trees/

I’ll point out again (as Mieszko did) that we have some differences in our implementation in terms of how conditionals work (and other items), but it still gives a good general idea of why you may want to use a behavior tree and how they work.

When we have our documentation complete, I’m sure we’ll also post some tutorials and videos of our own.

For now, here’s a little more information about how to make Conditional Decorators (using ShooterGame’s BotBehavior behavior tree asset as an example where possible for now). I’ve broken it up into a few separate posts for clarity.

Using Blackboards with Behavior Trees

First of all, in the Root node of that behavior tree, you’ll notice that a “Blackboard Asset” is specified (BotBlackboard). Blackboards are basically places where you can jot down information for quick and easy use by the behavior tree. That particular asset has four entries, an Enemy (ShooterCharacter class of object), NeedAmmo (boolean), Destination (Vector), and SelfActor (Actor class of object). You can make your own blackboard(s) and add as many different named entries as you need.

To create a blackboard asset, in ContentBrowser, right-click, choose “Miscellaneous->Data Asset”, and then select “BlackboardData”. You can add the keys you need to the blackboard section of its details view. (We’re working on real documentation now, so I don’t want to go into too much detail about these assets right now. However, the “Parent” is basically just an optional blackboard you can “inherit” data from. For example, if you have two types of AI, and both share a number of entries in the blacboards but need a few specific additional entries, you can have a “BasicBlackBoard” asset and then use that as the parent for “SpecialAI-A” and “SpecialAI-B” so they can share the common part as a base.)

Blackboards are optional, but they’re both convenient and efficient. They’re used in the ShooterGame example in its conditional nodes, which are just “Blackboard” conditional checks. You can add these decorators by right clicking on a composite node (Sequence/Selector/Simple Parallel) or a Task node and choosing Blackboard under the Decorators section. They allow you to check whether the property in question is set or not (for the boolean Needs Ammo, set == true; for the Actor “Enemy”, “Is Set” means it’s not “None” (NULL).

You can also compare Blackboard entries using “Compare BBEntries” decorator. Plus, you can retrieve values yourself for checking in blueprint conditionals, and you can set them in blueprint tasks.

Note that you can set the Node Name for every node in the graph; I recommend naming your decorators in a simple way as to what conditional they require, like “Needs Ammo” and “Has Enemy”.

Conditional Decorators: “Observer Aborts” Property

One critical thing you’ll notice if you select either the “Needs Ammo” or “Has Enemy” nodes in the BotBehavior tree is that they are both set to “Observer Aborts” “Lower Priority”. Our conditional decorators support event-driven changes to make the behavior tree more efficient. Rather than having to iterate over the entire tree constantly as in the most basic behavior trees, we can register for an event if a conditional changes that should cause the tree’s execution flow to change.

If “Observer aborts” is “None”, then only during traditional BT execution will the conditional check of a decorator matter. So for example, if you have a simple check such as “is Blackboard entry Enemy set?” (i.e. not NULL), it will only check as the execution moves through the node into the subtree. If later it becomes NULL in that subtree, execution won’t instantly leave the subtree (though it might exit due to failure of some task that requires that value). Similarly, if execution is in a lower priority subtree, it won’t instantly know that the condition for a higher priority task has been satisfied, so the lower priority task will continue until it succeeds or fails normally. Then, execution through the higher priority branch may discover that that task can be executed.

However, if you set “Observer Aborts” to “Self”, then while execution remains in the subtree, if the status of that decorator changes, it will cause the tree to abort immediately. For example, if the Enemy becomes “Is Not Set” (NULL)), it will abort (basically failing up the tree).

If you set “Observer Aborts” to “Lower Priority”, then while the execution is in a lower priority subtree, if the condition is satisfied such that execution can move into the higher priority subtree, then the lower subtree will abort immediately. So, if the behavior tree is doing the “Idle” behavior and the Enemy becomes set it will immediately abort the lower priority tree (“Idle”) and begin the higher priority tree that was prevented by the “Has Enemy” node being false previously.

“Observer Aborts” “Both” means that it will abort both “Self” AND “Lower Priority” trees.

NOTE: If there are multiple nodes that are all set to abort “Lower Priority” (or “Both”), they will all have to be satisfied before the lower priority tree will be aborted. That way, you won’t have to worry about separate value changes repeatedly aborting your lower priority tasks when the higher priority task can’t actually execute.

To help visualize the “Self” and “Lower Priority” trees in the editor, if you select a specific decorator, it will highlight “Self” and/or “Lower Priority” nodes in the tree (depending on the “Observer Aborts” property). In the details view you can see the key which shows which color is Lower Priority (a light blue at the moment) and which is “Self” (currently a teal-green).

2 Likes

Creating Blueprint Nodes for Behavior Trees

To create blueprint-based nodes for the behavior tree, right-click in the Content Browser and choose “Blueprint”. Then you can choose “BTDecorator_BlueprintBase” (for decorator nodes), BTService_BlueprintBase (for Services), or BTTask_BlueprintBase (for tasks)! These blueprints work just like regular blueprints, and once you make them, they’ll automatically show up in the Behavior Tree as options under the appropriate right-click context menu.

For simple conditional decorators, your blueprint just needs the “Event Receive Condition Check”, and then it can call “Finish Condition Check” to determine true/false for the conditional. Here’s a completely random example, presuming you have your Behavior Tree in your AIController class as we do (it can be in any class of course).

NOTE: This isn’t a “real” example; it would be rather odd to decide whether to do something based on whether your own pawn is moving downward at all. I chose the example partly to make it clear that it’s ridiculous; use your own class if you need it and your own conditions! We’ll have better examples as soon as the official documentation is ready. :slight_smile: Also, make sure you avoid “side effects” in your Conditionals; i.e. don’t perform tasks there or change values of actors in the world, as you will not get the results you want!

Wow
Now we have more informations :slight_smile:
Thanks a lot for taking the time to write all that, this is really helpfull.

No problem! I’m excited to see everyone trying out our behavior trees and look forward to feedback. :slight_smile:

This is awesome .

I really wasn’t scratching the surface of what can be done with them.

Wow. I’m currently working on B.R.A.I.N.S: Behavior Tree AI Plugin powered by Kismet. Nice to know I’m in sync with EPIC’s Vision for UE4.

, I’m glad that helped. Now your tutorial can be even better. :slight_smile:

One more note on the ShooterGame Behavior Tree. One of my coworkers pointed out that it still has a “Return true hack” node in it. While that does work, the current “best practice” implementation is to use a decorator called “Force Success” on the “Pick up ammo” node without an extra selector.

However, the “Force Success” decorator is quite new; in fact, it’s so new that I believe you’ll only have it if you compile the editor yourself from our latest code. The released build still has a (now deprecated) node called “Optional” which serves roughly the same purpose. (There are some functional differences between Optional and Force Success when used as a child of a Selector, but not under a Sequence as in this case. We decided Optional was rather odd and unintuitive, which is why we deprecated it in favor of using Force Success.)

If anyone needs similar functionality and isn’t compiling code themselves (or just doesn’t want to sync to latest in between builds), I’d recommend using Optional for now until our next build release. Then the node will turn red and remind you to replace it with Force Success (at your leisure).

As you can see, rapid development is still ongoing, especially since behavior trees are still “experimental”! :slight_smile:

Here’s a screenshot where I used the released build to add an “Optional” node… if you have built latest code yourself, use “Force Success” instead! :slight_smile:

Cool, thanks.

I’ll update my intial thread to use the practices that have been provided to follow, but probably can’t until this weekend.