Behavior Trees¶
Behavior trees determines how a entity make decisions. The current system uses what I call a LogicString.
LogicString is a string that creates a chain of boolean logic. Using &
for AND logic, |
for OR logic and !
for a NOT logic. Words in a LogicString are a function name which should return either true
, false
or nil
. A word with the [word]
brackets are [Action] functions, these stop the chain from proceeding regardless of return value. Since it's like boolean logic, curly brackets ()
will reorder the logic sequence.
Example:¶
In this LogicString, it proceeds with the logic from left to right. First look for function calledIsThirsty
and if function returns true
, the flow continues to HasWater
, otherwise, it jumps to the OR (|
) logic which continues with IdleTree
.
If the tree hits [DrinkWater]
which is consider an [Action], it ends the flow regardless of what the function returns, hence it will not reach IdleTree
.
When ShouldIdle
return false, it will just end the flow and perform no action.
The flow chart would look more like this below.
flowchart LR
A{Default}
A --> B{{IsThirsty}}
B --> |true|C{{HasWater}}
C --> |true|D>DrinkWater]
B --> |false|E{IdleTree}
C --> |false|E{IdleTree}
flowchart LR
A{IdleTree}
A --> B{{ShouldIdle}}
B --> |true|C>DoIdle]
More information on the Behavior Tree redesign: Behavior Tree Design
Old Behavior Trees 2.2¶
Behavior trees are logic trees that determine how a entity make decisions.
Tree Nodes¶
The 3 basic nodes are: - Leaf Node - Sequence Node - Select Node
Leaf Node¶
This is an action, it contains a function that do or check something. It returns a Success
or Failure
, aka true
or false
.
E.g.
HasEnemy
node: If EnemyClass
property exist, returns Success
else Failure
.
function treePackage.HasEnemy(logic, npcClass: NpcClass)
local targetHandler: NpcTargetHandler = npcClass:GetComponent("TargetHandler");
return targetHandler.EnemyClass ~= nil and logic.Success or logic.Failure;
end
FireGun
node: Point gun at target and fire primary.
Sequence Node¶
This is a logic node, it is usually a parent of a bunch of nodes which creates a logic flow chart.
Think of the Sequence Node as a "Until false/failure" flow.
For example:
HealSequence
node is a sequence node that sequentially process its child nodes until it fails. The IsLowHealth
, HasHealItem
and UseHealItem
are leaf nodes.
flowchart LR
A{{HealSequence}}
A --> B>IsLowHealth]
B --> C>HasHealItem]
C --> D>UseHealItem]
If a leaf node fails, the sequence stops, otherwise, it continues to the next leaf node.
Select Node¶
This is a logic node, it is usually a parent of a bunch of nodes, think of the Select Node as a "Until true/success" flow.
For example:
IdleSelect
node is a select node that process its child nodes until it returns true. Usually combined with SequenceNode
to make a comprehensive logic flow.
The HealSelfSequence
, HealNearbySequence
, IdleTasksSequence
, PatrolSequence
nodes are sequence nodes.
IdleSelect={"Or"; "HealSelfSequence"; "HealNearbySequence"; "IdleTasksSequence"; "PatrolSequence";};
flowchart LR
A[[IdleSelect]]
A --> B{{HealSelfSequence}}
B --> C{{HealNearbySequence}}
C --> D{{IdleTasksSequence}}
D --> E{{PatrolSequence}}
Here it goes one by one from HealSelfSequence
and process it to see if it succeeds, if not it continues to HealNearbySequence
, IdleTasksSequence
and then PatrolSequence
Bandit's HasEnemySequence¶
A useful example. This sequence node handles what happens when a Bandit has an enemy assigned.
flowchart LR
A{{HasEnemySequence}}
A-->B>HasEnemy]
B-->C[[FleeSelect]]
C-->D[[FightSelect]]
Which process as a sequence node (Until false):
1. HasEnemy
: If success (true) proceeds to FleeSelect
, otherwise sequence stops.
2. FleeSelect
: Does the FleeSelect
behavior tree until success true, otherwise proceeds to FightSelect
.
3. FightSelect
: Does the FightSelect
behavior tree.
![[BanditDefaultTree.luau]]