1.4 – Behavior Tree Architecture

1.4.0 – The design

1.4.1 – Behavior
Behavior is the basic building block of the tree. Every node is essentially a child of the base class Behavior. The core functions are a tick() for single entry, and onInitialize(), onUpdate() and onTerminate(). Each behavior has a current status. Status can be INVALID (not initialized), RUNNING, SUCCESS (after terminated) and FAILURE (after terminated).
Behavior.h

Status tick(Blackboard* _blackboard) /* single entry point for updating this behavior */
{
  if (m_eStatus != Status::BH_RUNNING)
    onInitialize(_blackboard);
  m_eStatus = onUpdate(_blackboard);
  if (m_eStatus != Status::BH_RUNNING)
    onTerminate(m_eStatus);
  return m_eStatus;
}
virtual void onInitialize(Blackboard* _blackboard) { }
virtual Status onUpdate(Blackboard* _blackboard) { return m_eStatus; }
virtual void onTerminate(Status _status) { }

1.4.2 – Decorator
Decorator is a behavior with only one child. I created 1 types of decorator. Repeater to repeatedly running a child node for given times.
Repeater.h

Status onUpdate(Blackboard* _blackboard)
{
  for (;;)
  {
    m_pChild->tick(_blackboard);
    if (m_pChild->getStatus() == Status::BH_RUNNING) break;
    if (m_pChild->getStatus() == Status::BH_FAILURE) { return Status::BH_FAILURE; }
    if (++m_iCounter == m_iLimit) { return Status::BH_SUCCESS; }
    m_pChild->reset();
  }
  return Status::BH_INVALID;
}

1.4.3 – Composite
Composite has more than one child. A composite can be either a Sequence, a Selector, or a Parallel. Sequence returns SUCCESS when every child nodes return SUCCESS, FAILURE if one child returns FAILURE. Selector returns SUCCESS when one child node returns SUCCESS, FAILURE if all children return FAILURE. Parallel executes all children “at the same time”.
Sequence.h

virtual Status onUpdate(Blackboard* _blackboard) override
{
  for (;;)
  {
    Status status = (*m_CurrentChild)->tick(_blackboard);
    if (status != Status::BH_SUCCESS) { return status; }
    if (++m_CurrentChild == m_Children.end()) { return Status::BH_SUCCESS; }
  }
  return Status::BH_FAILURE;
}

Selector.h
virtual Status onUpdate(Blackboard* _blackboard) override
{
  for (;;)
  {
    Status status = (*m_CurrentChild)->tick(_blackboard);
    if (status != Status::BH_FAILURE) { return status; }
    if (++m_CurrentChild == m_Children.end()) { return Status::BH_FAILURE; }
  }
}

Parallel.h
virtual Status onUpdate(Blackboard* _blackboard) override
{
  unsigned int iSuccessCount = 0, iFailureCount = 0;
  for (auto it: m_Children)
  {
    Behavior* behavior = it;
    if (behavior->isTerminated() == false) behavior->tick(_blackboard);
    if (behavior->getStatus() == Status::BH_SUCCESS)
    {
      iSuccessCount ++;
      if (m_eSuccessPolicy == PL_REQUIRE_ONE) { return Status::BH_SUCCESS; }
    }
    if (behavior->getStatus() == Status::BH_FAILURE)
    {
      iFailureCount ++;
      if (m_eSuccessPolicy == PL_REQUIRE_ONE) { return Status::BH_FAILURE; }
    }
  }
  if (m_eSuccessPolicy == PL_REQUIRE_ALL && iSuccessCount == m_Children.size()) { return Status::BH_SUCCESS; }
  if (m_eFailurePolicy == PL_REQUIRE_ALL && iFailureCount == m_Children.size()) { return Status::BH_FAILURE; }
  return Status::BH_RUNNING;
}

1.4.4 – Data
XML is used to data loading.

<BehaviorTree name="BT_Test_2">
    <ActiveSelector>
        <Sequence name="FindSequence"> <!-- Attack the enemy if seen. -->
            <Condition id="SeeEnemy"/>
            <Action id="MoveToEnemy"/>
            <Action id="AttackEnemy"/>
        </Sequence>
    </ActiveSelector>
</BehaviorTree>

You May Also Like

Leave a Reply

Your email address will not be published. Required fields are marked *

The AI Project