Behave is a simple, Unity node-based editor for creating and editing behavior trees, with an intuitive C# API.
To setup Behave in your project simply go to Window -> Package Manager, click the + icon on the top left and select Add package from git URL to add the Behave git URL.
You can now make a new behavior tree by going to Assets -> Create -> Behavior Tree Editor -> Behavior Tree.
After editing your behavior tree with the editor, you can reference the generated scriptable asset file from any of your scripts and use the Root
property to access the root of the generated behavior tree. You can then call the public bool Run(Blackboard blackboard)
method to run an iteration of your behavior tree.
public class BehaviorTreeLoader : MonoBehaviour {
[SerializeField]
public TreeGraph treeGraph;
BehaviorTree tree;
void Start() {
tree = treeGraph.Root;
Debug.Log(tree.ToString());
}
void Update() {
tree.Run(myBlackboard);
}
}
The Root
property is of type BehaviorTree
, which derives indirectly from the abstract Task
class, which provides a common interface into a behavior tree task. Functionality and extensibility is described in the sections below.
The behavior tree API is based on a Task
abstract class that can be derived to provide custom functionality. You can derive from Task
and provide a bool Run(Blackboard board)
override to create your own task, or use one of the provided tasks. The built-in tasks are:
- Selector
- Sequence
- NonDeterministicSelector
- NonDeterministicSequence
- Decorator
- Inverter
- UntilFail
- Limit
- SemaphoreGuard
- Wait
- Parallel
- Interrupter
- PerformInterruption
- BehaviorTree
In order to create a behavior tree programmatically, you can compose constructor calls for the desired tasks. Here's an example:
Task behaviorTree = new Sequence(
new NonDeterministicSelector(
new UntilFail(
new CustomDecorator1(),
new CustomDecorator2()
)
),
new Limit(
new CustomDecorator(),
100
)
);
The resulting behavior tree will automatically be generated every time you access the Root
property of your editor generated TreeGraph
scriptable object, so it is recommended that you cache the result of Root
somewhere in your script.
You can derive from any of the aforementioned Task
subclasses, or create your own Task
subclass and override the Run(Blackboard blackboard)
method in order to extend the behavior tree implementation to suit your needs. In order for your task to show up as an editor context menu option when using the behavior tree editor, you need to add a BTEditor
attribute to the top of your class definition. Here's an example:
[BTEditor("Behavior Tree/CustomTask1")]
public class CustomTask1 : Task {
public override bool Run(Blackboard blackboard) {
// Do some stuff
}
}
The argument to the BTEditor
attribute specifies what the context menu path and name of the node should be. It is recommended that you use the "Behavior Tree/..." context menu path so that all behavior tree nodes are grouped together, but you are free to follow your own conventions.
The BTEditor
attribute can also include an additional argument to specify a path to a texture that will be used as the editor node image, if that is something you'd like to include.
[BTEditor("Behavior Tree/CustomDecorator1", texturePath = "some/texture/path/here")]
public class CustomDecorator1 : Decorator {
public override bool Run(Blackboard blackboard) {
// Do some stuff
}
}
In order to have class fields show up as node fields in the behavior tree editor, you should mark them with the BTEditable
attribute
[BTEditor("Behavior Tree/CustomTaskWithProperties")]
public class CustomTaskWithProperties : Task {
[BTEditable]
public float maxSum;
[BTEditable]
public bool hasMaxSum;
[BTEditable]
float sumSoFar;
public override bool Run(Blackboard blackboard) {
// Do some stuff
}
}
Take a look at the files in Runtime/Data Structures/BehaviorTree to get a feel for the Task
interface and how all the subclasses are implemented.
Behave includes a simple blackboard data structure definition to be used in saving and sharing data within your behavior trees or as a general centralized data store across your application. Simply create a new Blackboard
, adding and accessing BlackboardDatum
instances using the Set
and Get
methods, respectively. Here's an example that saves a character position into the blackboard
Blackboard blackboard = new Blackboard();
BlackboardDatum positionDatum = new BlackboardDatum("PlayerPosition", playerGO.transform.position);
blackboard.Set(positionDatum);
and getting the position back is just as simple
Vector3 position = blackboard.Get<Vector3>("PlayerPosition");