This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
dragengine:modules:dragonscript:behaviortrees [2025/03/13 18:30] – removed - external edit (Unknown date) 127.0.0.1 | dragengine:modules:dragonscript:behaviortrees [2025/03/13 18:31] (current) – dragonlord | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | {{tag> | ||
+ | <WRAP youarehere> | ||
+ | [[: | ||
+ | </ | ||
+ | |||
+ | ====== Behavior Trees ====== | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | 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: ''< | ||
+ | |||
+ | The following rules can be used: | ||
+ | |||
+ | ===== Action ===== | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | 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|< | ||
+ | < | ||
+ | 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: ''< | ||
+ | |||
+ | ===== Sequence ===== | ||
+ | |||
+ | Runs all actions in sequence. Returns '' | ||
+ | |||
+ | If looping is set to true the sequence restarts at the beginning if all actions returns '' | ||
+ | |||
+ | ===== Choice ===== | ||
+ | |||
+ | Runs all actions in sequence. Returns '' | ||
+ | |||
+ | If looping is set to true the choice restarts at the beginning if the first action returns '' | ||
+ | |||
+ | ===== Success ===== | ||
+ | |||
+ | Returns always '' | ||
+ | |||
+ | The main use for this rule is to test rule conditions without needing to attach them to some other rule. | ||
+ | |||
+ | ===== Failure ===== | ||
+ | |||
+ | Returns always '' | ||
+ | |||
+ | The main use for this rule is to fail a sequence rule by placing this last. | ||
+ | |||
+ | ===== Running ===== | ||
+ | |||
+ | Returns always '' | ||
+ | |||
+ | The main use for this rule is to force waiting until a rule conditions fails. | ||
+ | |||
+ | |||
+ | ====== File Format (*.debtree) ====== | ||
+ | |||
+ | 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. | ||
+ | |||
+ | <uml> | ||
+ | @startuml | ||
+ | |||
+ | object " | ||
+ | <# | ||
+ | |sequence|| | ||
+ | |choice|| | ||
+ | |success|| | ||
+ | |failure|| | ||
+ | |running|| | ||
+ | |subtree| | ||
+ | } | ||
+ | |||
+ | object " | ||
+ | <# | ||
+ | |condition| | ||
+ | |conditionMode| | ||
+ | } | ||
+ | behaviorTree --> action | ||
+ | |||
+ | object " | ||
+ | <# | ||
+ | |condition| | ||
+ | |conditionMode| | ||
+ | |action|| | ||
+ | |sequence|| | ||
+ | |choice|| | ||
+ | |success|| | ||
+ | |failure|| | ||
+ | |running|| | ||
+ | |subtree| | ||
+ | } | ||
+ | behaviorTree --> sequence | ||
+ | |||
+ | object " | ||
+ | <# | ||
+ | |condition| | ||
+ | |conditionMode| | ||
+ | |action|| | ||
+ | |sequence|| | ||
+ | |choice|| | ||
+ | |success|| | ||
+ | |failure|| | ||
+ | |running|| | ||
+ | |subtree| | ||
+ | } | ||
+ | behaviorTree --> choice | ||
+ | |||
+ | object " | ||
+ | <# | ||
+ | |condition| | ||
+ | |conditionMode| | ||
+ | } | ||
+ | behaviorTree --> success | ||
+ | |||
+ | object " | ||
+ | <# | ||
+ | |condition| | ||
+ | |conditionMode| | ||
+ | } | ||
+ | behaviorTree --> failure | ||
+ | |||
+ | object " | ||
+ | <# | ||
+ | |condition| | ||
+ | |conditionMode| | ||
+ | } | ||
+ | behaviorTree --> running | ||
+ | |||
+ | @enduml | ||
+ | </ | ||
+ | |||
+ | ====== Tags ====== | ||
+ | |||
+ | ===== behaviorTree ===== | ||
+ | |||
+ | ^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|-| | ||
+ | |||
+ | ===== action ===== | ||
+ | |||
+ | Adds an action rule. | ||
+ | |||
+ | ^Attribute^Description^Occurance^Default^ | ||
+ | |name|Name of action to run.|Required|-| | ||
+ | |||
+ | ^Tag^Description^Occurance^Default^ | ||
+ | |parameter|< | ||
+ | * '' | ||
+ | </ | ||
+ | |condition|Add condition. Condition name is text content of node|0..N|-| | ||
+ | |conditionMode|< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | </ | ||
+ | |||
+ | ===== sequence ===== | ||
+ | |||
+ | Adds a sequence rule. | ||
+ | |||
+ | ^Attribute^Description^Occurance^Default^ | ||
+ | |loop|Loop sequence.|Optional|'' | ||
+ | |doNotFail|If sequence fails return '' | ||
+ | |||
+ | ^Tag^Description^Occurance^Default^ | ||
+ | |parameter|< | ||
+ | * '' | ||
+ | </ | ||
+ | |condition|Add condition. Condition name is text content of node|0..N|-| | ||
+ | |conditionMode|< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | </ | ||
+ | |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|-| | ||
+ | |||
+ | ===== choice ===== | ||
+ | |||
+ | Adds a choice rule. | ||
+ | |||
+ | ^Attribute^Description^Occurance^Default^ | ||
+ | |loop|Loop choice.|Optional|'' | ||
+ | |doNotFail|If choice fails return '' | ||
+ | |||
+ | ^Tag^Description^Occurance^Default^ | ||
+ | |parameter|< | ||
+ | * '' | ||
+ | </ | ||
+ | |condition|Add condition. Condition name is text content of node|0..N|-| | ||
+ | |conditionMode|< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | </ | ||
+ | |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|-| | ||
+ | |||
+ | ===== success ===== | ||
+ | |||
+ | Adds a success rule. | ||
+ | |||
+ | ^Tag^Description^Occurance^Default^ | ||
+ | |parameter|< | ||
+ | * '' | ||
+ | </ | ||
+ | |condition|Add condition. Condition name is text content of node|0..N|-| | ||
+ | |conditionMode|< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | </ | ||
+ | |||
+ | ===== failure ===== | ||
+ | |||
+ | Adds a failure rule. | ||
+ | |||
+ | ^Tag^Description^Occurance^Default^ | ||
+ | |parameter|< | ||
+ | * '' | ||
+ | </ | ||
+ | |condition|Add condition. Condition name is text content of node|0..N|-| | ||
+ | |conditionMode|< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | </ | ||
+ | |||
+ | ===== running ===== | ||
+ | |||
+ | Adds a running rule. | ||
+ | |||
+ | ^Tag^Description^Occurance^Default^ | ||
+ | |parameter|< | ||
+ | * '' | ||
+ | </ | ||
+ | |condition|Add condition. Condition name is text content of node|0..N|-| | ||
+ | |conditionMode|< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | </ | ||
+ | |||
+ | ====== Examples ====== | ||
+ | |||
+ | 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. | ||
+ | |||
+ | <code xml><? | ||
+ | < | ||
+ | <!-- | ||
+ | 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 " | ||
+ | behaviors applies. | ||
+ | |||
+ | It is possible to add the loop=" | ||
+ | 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=' | ||
+ | <!-- | ||
+ | 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 " | ||
+ | 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 " | ||
+ | becomes " | ||
+ | --> | ||
+ | |||
+ | <!-- | ||
+ | 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 " | ||
+ | BTAction present in the game scripts. | ||
+ | --> | ||
+ | < | ||
+ | <!-- | ||
+ | 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. | ||
+ | --> | ||
+ | < | ||
+ | |||
+ | <!-- | ||
+ | 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 | ||
+ | --> | ||
+ | < | ||
+ | < | ||
+ | |||
+ | <!-- | ||
+ | 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. | ||
+ | --> | ||
+ | </ | ||
+ | |||
+ | <!-- | ||
+ | Randomly rotate. This action has a 35% chance. | ||
+ | --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | <!-- | ||
+ | Wait a random amount of time. This action has a 100% chance. | ||
+ | --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | <!-- | ||
+ | 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: | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | 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 " | ||
+ | --> | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | <!-- | ||
+ | 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. | ||
+ | --> | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | <!-- | ||
+ | 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 | ||
+ | < | ||
+ | --> | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||