Skip to main content
Version: 4.8

Decorators

A decorator is a node that must have a single child.

It is up to the Decorator to decide if, when and how many times the child should be ticked.

For the complete list of built-in nodes, see the other pages in this section and the source code on Github.

Inverter

Tick the child once and return SUCCESS if the child failed or FAILURE if the child succeeded.

If the child returns RUNNING, this node returns RUNNING too.

ForceSuccess

If the child returns RUNNING, this node returns RUNNING too.

Otherwise, it returns always SUCCESS.

ForceFailure

If the child returns RUNNING, this node returns RUNNING too.

Otherwise, it returns always FAILURE.

Repeat

Tick the child up to N times, as long as the child returns SUCCESS.

PortTypeDefaultDescription
num_cyclesInputPort<int>(required)Number of repetitions. Use -1 for infinite loop.
  • Returns SUCCESS after all N repetitions complete successfully.
  • Returns FAILURE immediately if the child returns FAILURE (loop is interrupted).
  • Returns RUNNING if the child returns RUNNING; the counter is not incremented and the same iteration resumes on the next tick.
  • If the child returns SKIPPED, the child is reset but the counter is not incremented.
<Repeat num_cycles="3">
<ClapYourHandsOnce/>
</Repeat>

RetryUntilSuccessful

Tick the child up to N times, as long as the child returns FAILURE.

PortTypeDefaultDescription
num_attemptsInputPort<int>(required)Number of attempts. Use -1 for infinite retries.
  • Returns SUCCESS immediately if the child returns SUCCESS (loop is interrupted).
  • Returns FAILURE after all N attempts are exhausted.
  • Returns RUNNING if the child returns RUNNING; the attempt counter is not incremented and the same iteration resumes on the next tick.
  • If the child returns SKIPPED, the child is reset and SKIPPED is returned.
<RetryUntilSuccessful num_attempts="3">
<OpenDoor/>
</RetryUntilSuccessful>
note

The deprecated name RetryUntilSuccesful (single 's') is still supported for backward compatibility but should not be used in new trees.

KeepRunningUntilFailure

The KeepRunningUntilFailure node returns always FAILURE (FAILURE in child) or RUNNING (SUCCESS or RUNNING in child).

Delay

Tick the child after a specified time has passed. The delay is specified as Input Port delay_msec. If the child returns RUNNING, this node returns RUNNING too and will tick the child on next tick of the Delay node. Otherwise, return the status of the child node.

Timeout

Halt a running child if it has been RUNNING longer than a given duration. This is the opposite of Delay: while Delay waits before ticking the child, Timeout interrupts a child that takes too long.

PortTypeDefaultDescription
msecInputPort<unsigned>(required)Timeout duration in milliseconds.
  • If the child completes (SUCCESS or FAILURE) before the timeout, its status is returned.
  • If the child is still RUNNING when the timeout expires, it is halted and FAILURE is returned.
<Timeout msec="5000">
<KeepYourBreath/>
</Timeout>
tip

Combine Timeout with RetryUntilSuccessful for a robust retry-with-timeout pattern:

<RetryUntilSuccessful num_attempts="3">
<Timeout msec="5000">
<LongRunningAction/>
</Timeout>
</RetryUntilSuccessful>

RunOnce

The RunOnce node is used when you want to execute the child only once. If the child is asynchronous, it will tick until either SUCCESS or FAILURE is returned.

After that first execution, you can set value of the Input Port then_skip to:

  • TRUE (default), the node will be skipped in the future.
  • FALSE, return synchronously the same status returned by the child, forever.

Precondition

The Precondition decorator evaluates a script condition before ticking its child.

PortTypeDefaultDescription
ifInputPort<std::string>(required)Script condition to evaluate
elseInputPort<NodeStatus>FAILUREStatus to return if condition is false
  • If the condition is true, the child is ticked.
  • If the condition is false, the node returns the status specified in else.
  • Once the child starts (returns RUNNING), the condition is not re-evaluated until the child completes.
<Precondition if="battery_level > 20" else="FAILURE">
<MoveToGoal/>
</Precondition>
Per-Tick Evaluation

If you need the condition to be checked on every tick while the child is running (e.g., to interrupt a running action when a condition changes), use else="RUNNING":

<Precondition if="battery_ok" else="RUNNING">
<LongRunningAction/>
</Precondition>

With else="RUNNING", if the condition becomes false, the decorator returns RUNNING instead of FAILURE, allowing the tree to continue ticking and re-check the condition.

For more details, see Pre and Post conditions and Introduction to the Scripting language.

SubTree

Cf. Compose behaviors using Subtrees.

Other decorators requiring registration in C++

An empty queue will return SUCCESS

Register for example with factory.registerNodeType<ConsumeQueue<Pose2D>>("ConsumeQueue"); Cf. t18_waypoints.cpp.

SimpleDecoratorNode

Register a simple decorator node with void BehaviorTreeFactory::registerSimpleDecorator("MyDecorator", tick_function, ports), which uses SimpleDecoratorNode internally and where tick_function is a function with signature std::function<NodeStatus(NodeStatus, TreeNode&)> and ports is a variable of type PortsList.