Composites

Composites are responsible for directing the path traced through the tree on a given tick (execution). They are the factories (Sequences and Parallels) and decision makers (Selectors) of a behaviour tree.

digraph selector {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Sequence [fontcolor=black, shape=box, fontsize=11, style=filled, fillcolor=orange];
Selector [fontcolor=black, shape=octagon, fontsize=11, style=filled, fillcolor=cyan];
Parallel [fontcolor=black, shape=parallelogram, fontsize=11, style=filled, fillcolor=gold];
}

PyTree Composites

Composite behaviours typically manage children and apply some logic to the way they execute and return a result, but generally don’t do anything themselves. Perform the checks or actions you need to do in the non-composite behaviours.

Most any desired functionality can be authored with a combination of these three composites. In fact, it is precisely this feature that makes behaviour trees attractive - it breaks down complex decision making logic to just three primitive elements. It is possible and often desirable to extend this set with custom composites of your own, but think carefully before you do - in almost every case, a combination of the existing composites will serve and as a result, you will merely compound the complexity inherent in your tree logic. This this makes it confoundingly difficult to design, introspect and debug. As an example, design sessions often revolve around a sketched graph on a whiteboard. When these graphs are composed of just five elements (Selectors, Sequences, Parallels, Decorators and Behaviours), it is very easy to understand the logic at a glance. Double the number of fundamental elements and you may as well be back at the terminal parsing code.

Tip

You should never need to subclass or create new composites.

The basic operational modes of the three composites in this library are as follows:

  • Selector: select a child to execute based on cascading priorities
  • Sequence: execute children sequentially
  • Parallel: execute children concurrently

This library does provide some flexibility in how each composite is implemented without breaking the fundamental nature of each (as described above). Selectors and Sequences can be configured with or without memory (resumes or resets if children are RUNNING) and the results of a parallel can be configured to wait upon all children completing, succeed on one, all or a subset thereof.

Tip

Follow the links in each composite’s documentation to the relevant demo programs.

Selector

class py_trees.composites.Selector(name='Selector', memory=False, children=None)[source]

Selectors are the decision makers.

digraph selector {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Selector [fontcolor=black, shape=octagon, fontsize=11, style=filled, fillcolor=cyan];
"High Priority" [fontcolor=black, shape=ellipse, fontsize=11, style=filled, fillcolor=gray];
Selector -> "High Priority";
"Med Priority" [fontcolor=black, shape=ellipse, fontsize=11, style=filled, fillcolor=gray];
Selector -> "Med Priority";
"Low Priority" [fontcolor=black, shape=ellipse, fontsize=11, style=filled, fillcolor=gray];
Selector -> "Low Priority";
}

A selector executes each of its child behaviours in turn until one of them succeeds (at which point it itself returns RUNNING or SUCCESS, or it runs out of children at which point it itself returns FAILURE. We usually refer to selecting children as a means of choosing between priorities. Each child and its subtree represent a decreasingly lower priority path.

Note

Switching from a low -> high priority branch causes a stop(INVALID) signal to be sent to the previously executing low priority branch. This signal will percolate down that child’s own subtree. Behaviours should make sure that they catch this and destruct appropriately.

See also

The py-trees-demo-selector program demos higher priority switching under a selector.

Parameters:
  • name (str) – the composite behaviour name
  • memory (bool) – if RUNNING on the previous tick, resume with the RUNNING child
  • children ([Behaviour]) – list of children to add

Sequence

class py_trees.composites.Sequence(name='Sequence', memory=True, children=None)[source]

Sequences are the factory lines of Behaviour Trees

digraph sequence {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Sequence [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
Guard [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> Guard;
"Action 1" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 1";
"Action 2" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 2";
"Action 3" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 3";
}

A sequence will progressively tick over each of its children so long as each child returns SUCCESS. If any child returns FAILURE or RUNNING the sequence will halt and the parent will adopt the result of this child. If it reaches the last child, it returns with that result regardless.

Note

The sequence halts once it sees a child is RUNNING and then returns the result. It does not get stuck in the running behaviour.

See also

The py-trees-demo-sequence program demos a simple sequence in action.

Parameters:

Parallel

class py_trees.composites.Parallel(name=<Name.AUTO_GENERATED: 'AUTO_GENERATED'>, policy=<py_trees.common.ParallelPolicy.SuccessOnAll object>, children=None)[source]

Parallels enable a kind of concurrency

digraph pastafarianism {
graph [fontname="times-roman", splines=curved];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Parallel [fillcolor=gold, fontcolor=black, fontsize=9, label="Parallel\n--SuccessOnSelected(⚡,[B1,B2])--", shape=parallelogram, style=filled];
B1 [fillcolor=gray, fontcolor=black, fontsize=9, label=B1, shape=ellipse, style=filled];
Parallel -> B1;
B2 [fillcolor=gray, fontcolor=black, fontsize=9, label=B2, shape=ellipse, style=filled];
Parallel -> B2;
B3 [fillcolor=gray, fontcolor=black, fontsize=9, label=B3, shape=ellipse, style=filled];
Parallel -> B3;
}

Ticks every child every time the parallel is run (a poor man’s form of parallelism).

Policies SuccessOnAll and SuccessOnSelected may be configured to be synchronised in which case children that tick with SUCCESS will be skipped on subsequent ticks until the policy criteria is met, or one of the children returns status FAILURE.

Parallels with policy SuccessOnSelected will check in both the setup() and tick() methods to to verify the selected set of children is actually a subset of the children of this parallel.