Start Page » Game Development with the Drag[en]gine » Behavior Tree File Format
Behavior trees are structured graphics describing the AI logic using a tree of rules. The behavior tree is at all times located at one of the rules in the tree. During each simulation step (typically during frame update) the behavior tree moves along the tree until an rule returns either BTResult.running
. The behavior tree keeps on running the active rule each simulation step until the rule either returns BTResult.success
or BTResult.failure
. The AI logic is formed by the logic behind each rule.
Each can also have one or more conditions assigned. Before each run of the rule the conditions are evaluated. If any of the conditions evaluates to false the rule fails. Only the conditions of the active rule are evaluated. The conditions of the parent rule are only evaluated if the active node returns anything else but BTResult.running
. In this case the behavior tree wants to move to the next action. To do this the parent rule is run which then automatically evaluates the parent rule conditions.
Conditions can be paramtrized by adding one or more parameters to the rule. The conditions define themselves which parameters they understand and how they react to them. The name of parameters is free but it is recommended to use a naming scheme like this: <source>.<parameter>
. This groups parameters and reduces the chance of name conflicts. This is especially important since parameters are also reused for action rules. And while actions already define the source
part rather well for conditions this is unknown.
The following rules can be used:
This is the main working rule connecting game logic to the behavior tree. BTAction subclasses are created by the game developer and added to the behavior tree. If the rule is run the action is looked up and run. The return value of the action becomes the return value of the rule.
For performance reasons the action is looked up once then stored in the rule. This is correct behavior as long as the rules are not changed at run-time. If you need changing actions at run-time implement a BTAction forwarding to the desired BTAction. This way the cached action stays valid while the actual BTAction called inside can change. This is though an elaborate situation. Usually actions are added to the behavior tree once and then never change.
If the named action is not found the rule fails with BTResult.failure
.
There exists convenience implementations of the BTAction interface linking to action sources present in the game engine:
Script Class | Description |
---|---|
BTActionParameterTable | Updates the value of a ParameterTree entry. |
BTActionTrigger | Fire or reset a trigger target. |
BTActionSendGlobalEvent | Send a global even using GlobalEvents class, typically BaseGameApp.getApp().getGlobalEvents(). |
BTBlockAction | Action running a script block like this: new BTBlockAction(block BTContext context, Dictionary parameters // do something return BTResult.running // or .success or .failure end) This allows to quickly add actions without needing to write a new script class. |
Actions can be paramtrized by adding one or more parameters to the rule. The actions define themselves which parameters they understand and how they react to them. The name of parameters is free but it is recommended to use a naming scheme like this: <source>.<parameter>
. This groups parameters and reduces the chance of name conflicts. This is especially important since parameters are also reused for rule conditions. And while actions already define the source
part rather well for conditions this is unknown.
Runs all actions in sequence. Returns BTResult.success
if all actions returned BTResult.success
. If an action return BTResult.failure
processing of the rule stops and BTResult.failure
is returned. If no actions are present BTResult.failure
is returned.
If looping is set to true the sequence restarts at the beginning if all actions returns BTResult.success
. This allows to keep looping a sequence until the first time any action returns BTResult.failure
.
Runs all actions in sequence. Returns BTResult.failure
if all actions returned BTResult.failure
. If an action returns BTResult.success
processing of the rule stops and BTResult.success
is returned. If no actions are present BTResult.failure
is returned.
If looping is set to true the choice restarts at the beginning if the first action returns BTResult.success
. This allows to keep looping a choice until the first time all actions return BTResult.failure
.
Returns always BTResult.success
unless a rule condition evaluates to false.
The main use for this rule is to test rule conditions without needing to attach them to some other rule.
Returns always BTResult.failure
.
The main use for this rule is to fail a sequence rule by placing this last.
Returns always BTResult.running
unless a rule condition evaluates to false.
The main use for this rule is to force waiting until a rule conditions fails.
The behavior tree file format is recognized by the LoadBehaviorTree script class. The file is an XML file with a simple structure to define a behavior tree using XML.
Tag | Description | Occurance | Default |
---|---|---|---|
action | Add action rule | 0..N | - |
sequence | Add sequence rule | 0..N | - |
choice | Add choice rule | 0..N | - |
success | Add success rule | 0..N | - |
failure | Add failure rule | 0..N | - |
running | Add running rule | 0..N | - |
subtree | Load behavior tree using path from tag text content. Subtree is added to the behavior tree as a sequence rule containing the loaded behavior tree | 0..N | - |
Adds an action rule.
Attribute | Description | Occurance | Default |
---|---|---|---|
name | Name of action to run. | Required | - |
Tag | Description | Occurance | Default |
---|---|---|---|
parameter | Add parameter. Parameter value is text content of node which can be empty. Attributes:
| 0..N | - |
condition | Add condition. Condition name is text content of node | 0..N | - |
conditionMode | How conditions are evaluated. Valid values:
| 0..1 | allTrue |
Adds a sequence rule.
Attribute | Description | Occurance | Default |
---|---|---|---|
loop | Loop sequence. | Optional | false |
doNotFail | If sequence fails return BTResult.success . Useful to optional sequences running as many rules in a sequence as possible without failing the parent rule. | Optional | false |
Tag | Description | Occurance | Default |
---|---|---|---|
parameter | Add parameter. Parameter value is text content of node which can be empty. Attributes:
| 0..N | - |
condition | Add condition. Condition name is text content of node | 0..N | - |
conditionMode | How conditions are evaluated. Valid values:
| 0..1 | allTrue |
action | Add action rule | 0..N | - |
sequence | Add sequence rule | 0..N | - |
choice | Add choice rule | 0..N | - |
success | Add success rule | 0..N | - |
failure | Add failure rule | 0..N | - |
running | Add running rule | 0..N | - |
subtree | Load behavior tree using path from tag text content. Subtree is added to the behavior tree as a sequence rule containing the loaded behavior tree | 0..N | - |
Adds a choice rule.
Attribute | Description | Occurance | Default |
---|---|---|---|
loop | Loop choice. | Optional | false |
doNotFail | If choice fails return BTResult.success . Useful for optional choices there failing the choice should not fail the parent rule. | Optional | false |
Tag | Description | Occurance | Default |
---|---|---|---|
parameter | Add parameter. Parameter value is text content of node which can be empty. Attributes:
| 0..N | - |
condition | Add condition. Condition name is text content of node | 0..N | - |
conditionMode | How conditions are evaluated. Valid values:
| 0..1 | allTrue |
action | Add action rule | 0..N | - |
sequence | Add sequence rule | 0..N | - |
choice | Add choice rule | 0..N | - |
success | Add success rule | 0..N | - |
failure | Add failure rule | 0..N | - |
running | Add running rule | 0..N | - |
subtree | Load behavior tree using path from tag text content. Subtree is added to the behavior tree as a sequence rule containing the loaded behavior tree | 0..N | - |
Adds a success rule.
Tag | Description | Occurance | Default |
---|---|---|---|
parameter | Add parameter. Parameter value is text content of node which can be empty. Attributes:
| 0..N | - |
condition | Add condition. Condition name is text content of node | 0..N | - |
conditionMode | How conditions are evaluated. Valid values:
| 0..1 | allTrue |
Adds a failure rule.
Tag | Description | Occurance | Default |
---|---|---|---|
parameter | Add parameter. Parameter value is text content of node which can be empty. Attributes:
| 0..N | - |
condition | Add condition. Condition name is text content of node | 0..N | - |
conditionMode | How conditions are evaluated. Valid values:
| 0..1 | allTrue |
Adds a running rule.
Tag | Description | Occurance | Default |
---|---|---|---|
parameter | Add parameter. Parameter value is text content of node which can be empty. Attributes:
| 0..N | - |
condition | Add condition. Condition name is text content of node | 0..N | - |
conditionMode | How conditions are evaluated. Valid values:
| 0..1 | allTrue |
Example from the ExampleApp. A simple behavior tree making the actor wander around and if the player gets close tries to stay away from him. If the player is moving fast the actor gets jumpy.
This uses the basic behavior tree system from the game engine with some ready made actions.
<?xml version='1.0' encoding='ISO-8859-1'?> <behaviorTree> <!-- The basic AI loop. For this example we use a choice with all supported behaviors. This could be also done differently but avoids the need to use "success" rules to make a choice always succeed if no of the supported behaviors applies. It is possible to add the loop="true" attribute. This would cause the root rule to be looping. The difference is subtle but potentially problematic. If the rule ends with a success state and looping is set to true then the rule will start again from the beginning. As you can imagine this can lead to a run-away situation if the rule continues to return success state. If the rule is not looping with a success state the processing ends for this stepping of the behavior tree. It is recommended to not use looping on the root rule uness you have a specific need to do so. Looping rules can be used anywhere in the behavior tree for both sequence and choice rules. Looping can be useful in some situations. This choice is structed in a way all regular actions come first. One of them is going to succeed so the interruption rule is never used. This rule is only jumped to directly hence the name. It would be also possible to embed the interruption behavior into the regular processing of the behavior tree. In this case interruption actions would only be possible after another action finished running. Rules are not required to have identifiers under certain circumstances. Identifiers are used by these actions: - Mandatory: Switch current rule (jumping) from script code - Mandatory: Saving behavior tree context state to save states - Optional: Debug logging to identify what rule is processed For mandatory usage an exception will be thrown if rule identifiers are null. If a rule only returns success or failure state and never returns running state the identifier can be skipped. This works since only running rules can be the current rule and thus only the identifier or such rules are saved to file writers. If you never intend to save state nor jump to rules you can skip them entirly. --> <choice id='root'> <!-- Choice of behaviors reacting to player getting too close. This rule is jumped to directly if the player gets close. This makes this rule an "interruption" rule. Such rules can be jumped to directly but also work if run inside regular AI looping. Jumping to simply makes the actor react immediatly. Without jumping the actor would react after finishing his current sequence of actions. To make writing identifiers simpler a trick can be used. If the identifier begins with a period the parent rule identifier is used as prefix. For example the ID ".child" inside a parent with ID "parent" becomes "parent.child". --> <!-- Wander to a random position. This rule keeps running until the actor arrives at the target. The action succeeds once the actor arrived at the desired position. This action fails as long as player is too close. This action has a 25% chance. The attribute "name" defines the action to run and has to match a BTAction present in the game scripts. --> <action id='wander' name='wander'> <!-- Parameters have string key and value. The action-tag and custom BTAction subclasses can use these parameters to modify the way the selected action runs. Certain conditions and custom BTCondition subclasses can also use the same parameters to modify the run checks. Since parameters are shared across all these elements care has to be taken how the parameters are named. If for example two conditions use the same parameter name they will receive the same value. If you need unique values for specific types of conditions you have to choose individual parameter names in your game scripts when adding the conditions to the context to allow you to define them separately in your behavior trees. The condition and parameter tags can appear in any order. --> <parameter name='chance'>0.25</parameter> <!-- All actions use this condition to be interrupted. This could be also achieved by jumping to a specific rule manually. In this example the actions contain the interrupt condition. Each rule can have any number of conditions. The conditionMode states how the rules are checked. Supported are these modes: - allTrue: no condition returns false - anyTrue: at least one condition returns true - anyFalse: at least one condition returns false - allFalse: no condition returns true If complex conditions are required it is best to model them in the game scripts as a separate named condition. This increases the flexibility and avoids complicated XML for non-trivial situations --> <conditionMode>allFalse</conditionMode> <condition>playerNearby</condition> <!-- The above condition is an example. To keep conditions more compact you can create a negated version of the condition. This has been done in the two actions below. --> </action> <!-- Randomly rotate. This action has a 35% chance. --> <action id='turning' name='turning'> <parameter name='chance'>0.35</parameter> <condition>playerNotNearby</condition> </action> <!-- Wait a random amount of time. This action has a 100% chance. --> <action id='wait' name='wait'> <parameter name='chance'>1</parameter> <condition>playerNotNearby</condition> </action> <!-- If the player is close enough and moving fast the actor tries to flee by running and does a little jump for show. If any condition is false this action is not triggered. Condition checks are best done inside the action itself instead of using a construct like this: <sequence> <condition>isConditionTrue</condition> <action>doAction</action> </sequence> Using this construct is not wrong but prevents actions from being interrupted if a condition changes. The condition in the example above would be evaluated when the sequence is first entered and whenever an inner action fails and the next one is tried. To make writing the identifiers simpler a trick can be used. If the identifier begins with a period the parent rule identifier is used as prefix. In this example the ID ".flee" becomes "interrupt.flee". --> <action id='flee' name='flee'> <condition>playerNearby</condition> </action> <!-- If the player is close enough and not moving fast the actor tries to increase his distance to the player by walking. If any condition is false this action is not triggered. --> <action id='backOff' name='backOff'> <condition>playerNearby</condition> </action> <!-- If we end up here no event condition is true. We want to loop again so we need to use the success rule. Using failure instead (same as doing nothing here) would fail the behavior tree and throw an exception. This is a good example where rule identifier is not required since the rule never returns running and thus never will be the current rule after the behavior tree finished stepping. Another such rule is <failure/> which always fails. --> <success/> </choice> </behaviorTree>