BehaviorTreeGraph is a Behavior Tree implementation in Unity that heavily focuses on Visual Scripting, Visual Scripting enables the creation of custom nodes without writing a single line of code, which is perfect if you are an artist and want to explore all the possibilities of Behavior Trees in Unity, also if you are a programmer like me sometimes creating a simple Visual Scripting node for custom nodes that won't be reuse and making changes on the fly comes handy from time to time.
BehaviorTreeGraph also uses ports that enable your node logic to be agnostic from where you are getting the values to operate with, it can be from the Blackboard, from a Visual Scripting graph, or a simple literal variable, so no more writing custom code to constantly update values in your blackboard, instead we can use a Visual Scripting node that outputs the value which you can fetch from anywhere in your game, at the end the logic to fetch values and the logic to operate over those values become two completely separate pieces which you can arrange as you like to create different results.
Graph Editor
Graph View: Here you can edit/delete/create nodes.
Node Inspector: Once a node has been selected you will see the inspector here.
Variables View: Using this window you can edit the behavior tree variables.
Putting it simple a Behavior Tree is just a visual representation of the decision making for NPC, if you want to deeper your knowledge about Behavior Trees please use the following resources.
Nodes in Behavior Trees fall into four categories Gameplay Nodes, Composites, Decorator, Conditional Execution.
A gameplay node is an action or task, that can be completed over several game update calls, the action can have several states, but the most important ones is Failure which indicates that the action can't be completed, Success which indicates the action has been completed and Running which indicates the action is in progress, by overwriting several methods in the GameplayNode class you can create your custom node logic.
Composite nodes group other nodes and execute them in a specific order.
Sequence: Executes child nodes in order from left to right. Execution ends when any child node returns FAILURE.
Selector: Executes child nodes in order from left to right. Execution ends when any child node returns SUCCESS.
Parallel Selector: Executes child nodes at the same time. Execution ends when any child node returns SUCCESS.
Parallel Sequence: Executes child nodes at the same time. Execution ends when any child node returns FAILURE.
Random Sequence: Executes child nodes in random order. Execution ends when any child node returns SUCCESS.
Random Selector: Executes child nodes in random order. Execution ends when any child node returns FAILURE.
Composites nodes decide the execution order of child nodes based on their ExecutionStatus, for example, if a child node returns Success inside a Sequence it will start executing the next node, but decorator nodes can override this behavior by changing the return child node ExecutionStatus and force certain behaviors.
Repeater: Forces child node to continue his execution by overwriting his ExecutionStatus to Running, use this if you want your node to continue his execution, for example, you can use this in a node that scans the environment looking for threats, so the NPC is constantly looking for enemies.
Return Failure: Overrides child node ExecutionStatus value to Failure, use this if you want to ensure that the child node execution fails.
Return Success: Overrides child node ExecutionStatus value to Success, use this if you want to ensure that the child node Succeeds.
Until Failure: Forces child execution until it returns Failure, use this if you want to keep running a child node in a loop until it returns failure.
Until Success: Forces child execution until it returns Success, use this if you want to keep running a child node in a loop until it returns Success.
Behavior Trees are stored in ScriptableObjects, to create a new Behavior Tree right-click in the project editor window and select Create/Visual Scripting/Behavior Tree
Behavior Trees are executed in the game using a Behavior Tree Machine component.
Graph: The reference graph is stored in a Behavior Tree Graph that can be used between multiple behavior tree machine instances.
Embed: The reference graph is stored in the Behavior Tree Graph Asset
Use the Convert button to transform a graph from Embed to Graph and vice versa.
If the source is a Graph we can set the Behavior Tree Graph Asset in the Graph field.
Use the Edit Graph button to open the editor window.
The editor window is where we can edit/create/delete nodes to create our desired AI behavior, to open the editor window use the Edit Graph button from the Behavior Tree Graph asset or Behavior Tree Machine component.
Zoom In/ Zoom Out → Ctrl + Scroll Wheel
Move → Press Scroll Wheel and Move Mouse
1 → Blackboard editor view: use this view to edit the different Variables component including the one attached in the Behavior Tree Machine GameObject
2 → Graph Inspector: With this view, you can inspect the different elements in the Behavior Tree, for example, if you don't want to use Ports you can expose all your variables in the inspector in a more traditional way.
3 → Zoom: Use this input field to edit the Zoom value
4 → Toolbar
5 → Behavior Tree Canvas
Create new nodes
Right-click in the Behavior Tree Canvas and select the type of node you want to create.
Delete nodes
Select the node or nodes you want to delete and select DEL
Duplicate nodes
Select the node or nodes you want to duplicate and use CTRL+D
Copy/Paste/Cut
Copy: CTRL+C
Paste: CTRL+V
Cut: CTRL+X
Note on Copy/Paste/Cut
If the node contains a Visual Scripting Graph said node can't be copied/duplicated/pasted since it holds a reference to the Script Graph, if needed you can create a new node and Copy/Paste the nodes in the Script Graph.
Connecting Nodes
To create the desired behavior you need to connect nodes in a specific order, to do so select the node you want as parent press and hold CTRL drag from the parent node to the child node release control and the connection will be created, use ESC to cancel the transition creation.
Connecting Ports
Drag and drop a port output pin to an input pin to connect the ports, and use ESC to cancel the port connection creation.
Although most nodes use ports, some nodes can be edited using the inspector, and also you can expose your custom values in the inspector instead of using ports, to access a node inspector select the node you want to edit and the node inspector window will be updated.
In order to achieve a separation from the node logic and the data, we can use ports, this way the node doesn't care form where we pull the data, it can be from the blackboard, a gameObject component, another system, a literal, etc. by doing this there is no need to create separate scripts or logic to update the information from the blackboard, using visual scripting you can pull the information from anywhere.
It is nice that we can create custom input and output ports inside the nodes, but the real power of ports comes when mixed with Visual Scripting, we can use a Visual Scripting Variable and connect the return value to any node we want, this way we can use Visual Scripting to fetch any value we want form anywhere in the game, to illustrate this let's create a small example.
Create four nodes an Add Force, Wait, Repeater, and a Sequence.
Arrange the nodes in the following way.
Create a Float literal using Unity/Literal/Float and connect the value node to the Time in the waiting node by dragging the port.
Select the literal node and set the value to 1.
Create the Script Graph Variable using Unity/Visual Scripting/Visual Scripting Variable, select the node and click Open Graph.
Use visual scripting to return a Rigidbody from the scene
Connect The Visual Scripting Variable to the Target input in the Add Force node
Use a literal Vector3 node to set a Force value.
Click play and see the result.
As you can see the Add Force node doesn't care from where the Rigidibody is coming and the Visual Scripting Variable can fetch values from anywhere in the game.
Over time as your Behavior Tree grows in complexity you may have several branches to handle different behaviors in your AI, for example, imagine we are making an AI for an RTS game a simple worker whose primary task is to collect wood, so we handle all wood collection related logic in a single branch, later we decided that we also want workers to run when there are enemies nearby, and from time to time we also want our worker to take a rest just to make him look more alive.
As you can see we have used a selector to arrange our tasks in order of importance from left to right, of course we run away from enemies is the most important task followed by Sleep and finally wood collection, if the Unit doesn't need to run from enemies or take rest it will keep on collecting wood, now lest say we are in the middle of our wood collection branch and suddenly some enemies appear in the forest due to how we had arranged our behavior tree the worker needs to finish his wood collection task until he can run away which is not desired at all, a common approach will be to use conditional nodes in the wood collection branch like this.
Now with this new approach we will return Failure as soon as an enemy approaches and end the Wood Collection branch to move to the appropriate branch, it “works” but suddenly the wood collection branch needs to be aware of the Run from enemies branch, now let's say we want our worker to prioritize Gold collection our Wood Collection branch needs to be aware of this too and end if there are Gold mines nearby, so as our Behavior Tree logic grows so does all other branches, also our branches arent just modularize pieces of code we can share with other Units because they depend on other branches, let's say for some reason I want an attack unit like a swordman to also collect wood I can't just modularize this branch and share the logic because it depends on other branches like Run away from enemies which our Sowrdman doesn't do.
Now that we understand the common approach and the problems it may generate, let's see how the ConditionalExecution tries to solve this problem. The conditional execution is a node that is attached to other nodes, which indicates if a node can execute or not.
The node Can we run this branch returns a boolean that the Boolean Conditional Execution node uses to determine if the branch can run, if it returns false at any moment before trying to run the branch it will return Failure, if it returns true it will continue his normal execution.
First, select the node in which you desire to attach the conditional execution node, right-click, and select Add Conditional Execution/Boolean Conditional, right now there is only Boolean Conditional Execution, but you can create your custom Conditional Execution nodes to abort executions based on your custom logic.
The recommended approach is to use a Script Graph Variable Node and return a simple boolean value, you can also use code to create your custom logic and return using a port.
To use a Script Graph Variable just create the node and insert your logic in the script graph.
Create the node
2. Select Script Graph Variable and Click Open Graph
3. Create your custom logic and return a boolean value
4. Connect the node ports
Now before running the Sequence node will ask to the Boolean Conditional Execution if it can run, which will request the boolean value and run your custom Script Graph to determine if it can run normally or should abort the execution by returning Failure.
Now let's say you don't want to use a script graph and you want to create your custom logic in Code.
Add your custom node using the conditional execution menu.
As you can see the new Custom Conditional Execution doesn't make use of ports, which may be desirable in some cases, but you can always use ports if you like.
Now by using a Boolean Conditional Execution, we can abort the Wood Collection branch in favor of running away for enemies.
Now we can cancel the wood collection task at any point if there are enemies around, which will execute the runaway branch, now notice how the lower parts of the branch don't need to be aware of the Run from enemies branch, or any other branch just focus on their given task, this will allow us to use a subtree and abstract this logic sharing the wood collection task with any Unit since now it doesn't depend on any other branches.
Now we can add a Conditional Execution Boolean to the entire subgraph, and now the entire branch doesn't care about other branches and just this single Unit behavior tree handles the logic to transition between its own branches, which comes extremely handy when trying to modularize and share as much logic as we can between behavior trees.
To create a custom node, create a new class extend from GameplayNode and override the methods you need to create the desired logic.
A small box can be added in the node inspector if you want to add a short description of your node.
If you look closer into your new node, you can see a small square at the top where it can be connected to other nodes just as a child node, but you can't use it as a parent node, you can't create a connection from it, but you can create a connection to it, this can be easily configurable.
After setting both variables to true you can notice now we have the indicators to connect this node as parent and child.
You may wonder how useful can be a node, that cant be use as children and/or parent, and the answer is ports, insert link yo ports section.
Behavior Trees use the same Variables component from Script Graphs as a blackboard for Behavior Trees, to learn more about the Variables component refer to the Unity documentation.
The best way to get variables values into nodes is to use Ports, to do so we can create a Get Variable node using Unity/Variables/Get Variable
In order to get a Variable you need to what kind of variable component you want to use.
Flow: current execution flow object.
Graph: The graph can also store variables, which is useful in some cases.
Object: The variables component attached to the BehaviorTreeMachine executing this graph.
Scene: Scene variables are useful to share variables with other graphs in the scene.
Application: Like scene variables but it doesn't get destroyed on scene load.
Saved: This variable are serialized and saved so they can be saved between game sessions.
The Get Variable node requires a Key and returns a Value, the key is just a string that represents the variable name to fill it we can create a literal string value using, Unity/Literal/String.
Use the node inspector to fill the value for the string literal.
If you want it is also possible to get a variable value using just code.
The best way to set variable values into nodes is to use Ports, to do so we can create a Get Variable node using Unity/Variables/Get Variable.
Which requires two values, the key (the name of the variable) and the new value, also to set a Variable you need to set what kind of variable component you want to use.
Flow: current execution flow object.
Graph: The graph can also store variables, which is useful in some cases.
Object: The variables component attached to the BehaviorTreeMachine executing this graph.
Scene: Scene variables useful to share variables with other graphs in the scene.
Application: Like scene variables but it doesnt get destroyed on scene load
Saved: This variables are serialized and save so they can be saved betwen game sessions.
To set the Key we can use Literal String node, create a Literal String using Unity/Literal/String
Use the String Literal node inspector to set the desired variable name.
For the value we can use any kind of variable we need.
If you want it is also possible to set a variable value using just code.
One of the strongest points of Behavior Tree is the integration with Visual Scripting and Ports, which we will explore in this section.
Entire nodes logic can be created using just Visual Scripting, to create this node use Unity/Visual Scripting/Script Graph.
In the inspector we have 4 different graphs to create the Script Graph logic, the graphs haven't been created yet and they will be created the first time Open Graph is clicked, by Clicking Open Graph on On Enter Graph, you can see the Script Graph being executed each time the node Calls OnEnter.
The Script Graph execution starts in the Enter trigger and ends in the Exit trigger.
In this case, we are increasing by 1 the Value of MyVariableName each time OnEnter is called.
It is important to note that while the Script Graph can only have one SciprtGraphInput it can have as many ScriptGraphOutput as needed and the execution finishes when the first Exit trigger is reached.
If we open the OnUpdate Graph we will notice something different from other graphs.
This time the ScriptGraphOutput has a return value called result, as you may remember the OnUpdate method needs to return the ExecutioStatus enum, which is the same value ScriptGraphOutput is expecting, an ExecutionStatus of Running will continue with the node execution and Success or Failure will end the node execution.
In this example if MyCustomVariable value is greater than 1 we will return Running otherwise we will return Success to end the node Execution.
Script Graphs can be used not only for creating node logic but also to fetch information from anywhere for your nodes to operate over, create this node by using Unity/Visual Scripting/Visual Scripting Variable.
Use the Open Graph button to enter the Script Graph, and it only has an output port to return the value.
The ScriptGraphOut returns a value that is used by the Output Port, the value type can be of any type by default it is an object, but you can set the value type by editing the Result Type in the node inspector.
Finally, use any operations you need to return the expected value.
Behavior Trees allow us to share certain parts of a behavior tree with other behavior tree instances, this way we can avoid duplication, let's say we are working on an FPS game we will have different enemies AI with different behaviors but they all share similar logic, like shooting at the player, we can encapsulate this logic into a single behavior tree and share it with all other enemies AI, to do so we can use Gameplay/Run Behavior Tree Graph.
At the start it will indicate that the graph is missing, we need to set it using the node inspector, once we have set the behavior tree graph we want to use, a simplified version of the behavior tree will be drawn on the canvas.
It is important to note that we can't edit this simplified version, if we want to edit we need to open the assets and edit it directly, changes made to the behavior tree graph assets will be reflected on the canvas once the game is in play mode.
In the end, we can treat a Sub Behavior Tree Graph as any other node with an execution status that ends at some point (unless we use a repeater).
Remember we said Graph can save Variables? We can use default values in the graph variables, for example, let's say we have a default value for shoot damage in the shoot behavior tree.
But what if we create an enemy AI holding a shotgun and want to increase the shot damage just for this enemy AI, at the start of the game each parent Behavior Tree Graph will override the values of the children's Behavior Tree graphs, which means if our Enemy AI also has a variable called ShootDamage our component will override the default value just for this enemy AI.
In this case, ShootDamage will be overridden form 10 to 50 only for this enemy AI.
Traditionally, when we create Behavior Tree Nodes fetching the information and game logic form part of the same node let's see an example.
This makes this node hard to reuse because there needs to be another piece of logic constantly updating the Blackboard before we can access the targetPositionKey, let's say we have a separate system to calculate navAgents escape routes, there needs to be another custom piece of code updating this in the blackboard it can be another node or a new component in the BehaviorTreeMachine doing it in the update, but this way is not scalable, hard to read in the behavior tree, and you take away power from the designers since they will always depend on a developer even for trivial task like moving an NavAgent.
Behavior Tree philosophy is to treat fetching the information and the game logic as two separate puzzle pieces that designers can arrange in the order they want to create new results.
Now we have 2 separate puzzle pieces one node setting the NavAgentPosition and another Node returning the EscapePosition, the NavAgentPosition node can be used with any node returning a Vector3, and the EscapePosition can be connected to any node that needs a Vector3, at the end designers can grab the individuals pieces and arrange them in unique ways to create different behaviors.
If you want to premature end an action because the AI has something more important to do, like attack an enemy while we are in the middle of the idle action, we can use Conditional nodes, but try first ConditionalExecutions which will mark an entire behavior tree branch as failed base on custom logic.
In the FPS sample, you can clearly see how each branch does one Job, each function or branch can't care for or access information only relevant to other branches, for example, the Patrol branch doesn't care if there are enemies around to shoot at, only ConditionalExecutions should care about other branches and prematurely end a method if needed.
Once you have to separate your branches into clear specific methods that don't care and don't depend on other branches, use subbehaviortrees to share those methods with other AI, by doing this your branches will become separate puzzle pieces that designers can arrange in specific ways to create different behaviors, so once a designer had been tasked to create a specific AI they only need to pick the existing behaviors and give each one different priorities, thus giving the illusion of a different behavior using existing pieces.