I've studied several articles and listen some talks about behavior trees (mostly the resources available on AIGameDev by Alex J. Champandard).
I'm particularly interested on event driven behavior trees, but I have still some doubts on how to implement them correctly using a scheduler.
Just a quick recap:
Standard Behavior Tree
- Each execution tick the tree is traversed from the root in depth-first order
- The execution order is implicitly expressed by the tree structure. So in the case of behaviors parented to a
parallel
node, even if both children are executed during the same traversing, the first leaf is always evaluated first.
Event Driven BT
- During the first traversal the nodes (tasks) are enqueued using a scheduler which is responsible for updating only running ones every update
- The first traversal implicitly produce a depth-first ordered queue in the scheduler
- Non leaf nodes stays suspended mostly of the time. When a leaf node terminate(either with success or fail status) the parent (observer) is waked up allowing the tree traversing to continue and new tasks will be enqueued in the scheduler
- Without parallel nodes in the tree there will be up to 1 task running in the scheduler
- Without parallel nodes, the tasks in the queue(excluding dynamic priority implementation) will be always ordered in a depth-first order (is this right?)
Now, from what is my understanding of a possible implementation, there are 2 requirements I think must be respected(I'm not sure though):
Now, some requirements I think needs to be guaranteed by a correct implementation are:
- The result of the traversing should be independent from which implementation strategy is used.
- The traversing result must be deterministic.
I'm struggling trying to guarantee both in the case of parallel nodes. Here's an example:
Parallel_1
-->Sequence_1
---->leaf_A
---->leaf_B
-->leaf_C
Considering a FIFO
policy of the scheduler, before leaf_A
node terminates the tasks in the scheduler are:
P1(suspended),S1(suspended),leaf_A(running),leaf_C(running)
When leaf_A
terminate leaf_B
will be scheduled (at the end of the queue), so the queue will become:
P1(suspended),S1(suspended),leaf_C(running),leaf_B(running)
In this case leaf_B
will be executed after leaf_C
at every update, meanwhile with a non event-driven traversing from the root node, the leaf_B
will always be evaluated before leaf_A
.
So I have a couple of question:
- do I have understand correctly how event driven BT work?
- How can I guarantee the depth first order is respected with such an implementation?
- is this a common issue or am I missing something?