A Behavior Tree (BT) is a hierarchical structure that defines the decision-making process of an AI agent. It consists of nodes that represent tasks or behaviors, which are executed based on certain conditions. The tree is traversed from the root node down to the leaf nodes, with each node returning Success, Failure, or Running status. Behavior Trees help you structure the decision-making logic of your agent, making complex behaviors manageable and understandable via visual representation.
Action nodes are the leaf nodes of the Behavior Tree. They represent the actual tasks or behaviors that the AI agent can perform. When an action node is executed, it attempts to carry out a specific action, such as moving to a target, attacking an enemy, or playing an animation.
Organizational nodes that control the execution flow of their child nodes. There are several types of composite nodes, including:
Sequence: Executes its child nodes in order until one fails. If all child nodes succeed, the sequence node succeeds.
Selector: Attempts to execute its children one by one until one succeeds. It's used for selecting between different behaviors based on their success.
Parallel: Executes all its child nodes simultaneously and succeeds if a specified number of child nodes succeed.
For example you can have a "FindAndAttackEnemy" sequence node with two child nodes: "SearchForEnemy" and "AttackEnemy". The sequence node would first execute the "SearchForEnemy" node to locate an enemy. If an enemy is found, it would then execute the "AttackEnemy" node. If both child nodes succeed, the sequence node succeeds.
Modifiers that alter the behavior or outcome of their child nodes. They can be used to repeat actions, invert success/failure, or conditionally execute based on certain criteria. Common types of decorator nodes include:
Inverter: Inverts the success/failure status of its child node.
Repeater: Repeats the execution of its child node a specified number of times or until a condition is met.
Conditional: Executes its child node only if a specified condition is true.
A repeater node can execute the "AttackEnemy" node multiple times, allowing the AI agent to perform consecutive attacks.
The Blackboard is a global data store that allows nodes to share information and state across the Behavior Tree. It acts as a key-value store, where nodes can read and write data. The Blackboard is useful for storing variables such as target positions, health values, or any other relevant information that nodes might need to access.
The Behavior Tree Editor can be accessed by either double-clicking an existing tree asset or through the tab located in AnythingWorld -> Behavior Tree Editor.
Behaviour Tree Editor can be opened via double clicking on existing tree asset or opening the tab in AnythingWorld -> Behaviour Tree Editor.
Displays behavior tree nodes and the edges connecting them. This area allows users to interact with the nodes, connect them to each other and visualize the tree structure.
Shows data associated with the currently selected node. Some fields are presented as dropdowns linked to blackboard keys, they have a special type of NodeProperty which allows them to interact with blackboard keys. Other fields are standard Unity serialized fields.
To add a node, right-click on anywhere in the node view and select the desired node from the context menu. You can also start typing the name of the node to filter the list.
Double-clicking on a node opens the code associated with it, allowing direct modifications to its behavior.
Pressing "A" will auto-scale the node view to occupy the available window space fully, enhancing visibility.
Clicking on the Asset menu in the upper left corner reveals a list of all behavior trees in the current project and provides an option to create new ones.
New nodes and trees are saved to default locations specified in the global BT settings, found at Assets/AnythingWorld/AnythingBehaviour/BehaviourTreeProjectSettings.asset
.
Enable Node Values Copying setting determines whether node's field values will be deep copied when copy pasting nodes in node view.
Deep copy works for most of the Unity and basic types. However, more complex ones like Lists of custom types may fail. In this case a warning will be shown and the value skipped.
To use a behavior tree with a GameObject, add the BehaviourTreeRunner
component and select the appropriate tree. Once you add a tree it is automatically instantiated so you can edit it without changing the original asset.
BehaviourTreeRunner
also allows adding blackboard overrides for any blackboard keys BT has. This is useful if you want to change parameters like speed without opening and editing a BT.
Overrides are applied when entering playmode so they need to be setup beforehand.
Note that override will not work if any of the nodes write a value to that key effectively overwriting the value that you set in BehaviourTreeRunner.
Animated models in AnythingWorld Model Browser are imported with preset movement BTs depending on the model type. These default BTs can be changed via Make Options. You can also enable NavMesh usage which will make ground animals move using existing NavMesh or a generated one in case there is none in the scene.
When a scene is played, a runtime copy of the tree is created. If you've opened a Behaviour Tree Editor window before running the scene,d clicking on a GameObject with an active tree will visualize the execution flow, with colors indicating the state of each node. Yellow denotes nodes that returned Running state, green - nodes that returned Success state and red - nodes that returned Failure.
Behavior trees should be created in the lowest Unity version they will be used with, as there is no downward compatibility between versions. You can see the lowest Unity version AnythingWorld currently supports here.
New node types can be added either through the context menu in the node view or by creating a new C# class that inherits from Action, Decorator, or Composite types. These classes must implement OnStart()
, OnStop()
, and OnUpdate()
functions, with OnUpdate()
returning one of the node states.
Each node class has access to a shared context
field which is an instance of Context class that stores commonly used components and subsystems. It is created from the game object that you attached BehaviourTreeRunner component to. You can extend it by adding components that you want all nodes to have access to like Colliders, RigidBodies, etc.
To enable any field of a node to read or write to a blackboard key you must declare it as a NodeProperty<T>
where T
is the type of the property you want to declare, e.g. NodeProperty<float> speed
. You can set a default value via constructor like so: NodeProperty<float> speed = new NodeProperty<float>(4)
. If you have a blackboard key of the same type it's possible now to associate speed
NodeProperty with that key by choosing it from a dropdown in Node Inspector.
Converting a field into a NodeProperty and connecting it to a blackboard key will also allow you to override its value in the BehaviourTreeRunner component as discussed earlier.
Opening Type dropdown when creating a new key will show most of the common C# and Unity types. However if you find some types lacking you need to add them manually by opening BlackboardKeyTypes.cs
file and adding your type following the existing pattern.
Since the blackboard view extends the default Unity inspector it should be able to show all the types Unity inspector supports. However, adding more complex custom types can lead to rendering as well as reading \ writing problems. They can be fixed in some cases but will require in-depth editing of Behaviour Tree Editor code.
You can find example random movement BTs for all types of AnythingWorld agents underAssets/AnythingWorld/Examples/BTMovement
. To see them in action open and run RandomMovement scene from the same folder. There is also MoveAlongSpline BT which is explained in Path Creator documentation section.
All the random movement BTs follow the same logic:
Generate a random position via random position node.
Write it into a blackboard key.
Use that position as movement destination in a MoveToDestination node.
Wait before generating a new position.
However these nodes and their settings vary depending on the model type. The only variables common to all movement nodes and goal generation nodes are the following:
Min / Max Goal Spawn Radius - min / max random goal distance from agent's position.
Speed - initial speed of the agent.
Scale Speed With Model Speed - should we multiply the Speed by speedScalar? speedScalar is calculated from speed data specific to imported model.
Stopping Distance - the remaining distance to the goal when the agent stops moving and considers the goal reached.
Both ground animals and vehicles have settings for movement and turning speed. However ground animals are also able to jump hence GroundAnimalMoveToGoal node in GroundAnimalRandomMovement tree has additional jump settings explained below:
Jump Detector Size - the distance at which an agent will try to detect an obstacle and either avoid it or jump on it. Consequently determines the distance between jump starting point and obstacle.
Scale Jump With Model Height - will decrease or increase both Max Jump Height and Jump Detector Size proportional to model's height.
MaxSlope - maximum slope of the surface an animal able to traverse. Any surface with slope above max is processed as an obstacle that will be avoided or jumped on.
You will notice that the movement node of GroundAnimalNavMesh tree has almost no jump settings. That's because NavMesh is used to calculate optimal routes and there cannot be arbitrary changes to movement trajectory. Jump areas must be explicitly defined via NavMeshLinks explained here.
While movement speed and rotation settings resemble previously discussed agents, goal generation has an added height constraint. Flying agent's AerialRandomGoal node has:
Ground Y Coordinate which defines the ground level with global Y coordinate.
Min / Max Flying Height - min / max height relative to ground. So if you have ground level set at 5 and min height set at 2, the agent's min flying height will be 7.
Swimming agents are similar but always stay below water surface. Hence FishRandomGoal node has only Surface Y Coordinate and Swimming Depth which is also relative to the surface.