This file sets up the dog entity by creating the Ogre 3D model, and setting up the AI logic. This particular implementation hardcodes the behavior tree in C++, using a type-safe templatized tree builder.

The behaviors are defined using modular trees that are parameterized, for example for turning or moving. On the high-level, the dog AI logic is broken down into bodily, active and passive behaviors.

#include "Dog.h"

#include <alive/TreeBuilder.h>
#include <alive/engine/ConstantNode.h>
#include <alive/tree/Parallel.h>
#include <alive/tree/Repeat.h>
#include <alive/tree/ErrorHandler.h>

Random Forward Movement

This is a helper function used to build a behavior tree procedurally based on the parameters provided. This provides locomotion & movement at the specified speed, gait type, and for a certain amount of time.

alive::Node* makeRandomMove(float time, char* type = "walk", float speed = 1.0f)
{
    return alive::TreeBuilder()
    .composite<alive::ParallelNode>()
      .execute<ActionTranslate>()
          .Speed(speed)
      .end()
      .execute<ActionAnimate>()
            .Speed(1.0f)
            .Loop(true)
            .Name(type)
        .end()
      .execute<ActionWaitFor>()
        .Time(time)
        .Random(time)
      .end()
    .end();
}

Random Turning

This is another helper function to build a parametric behavior tree that plays a turning animation, and rotates the actor at the same time, for a specified duration.

alive::Node* makeRandomTurn(float time)
{
    return alive::TreeBuilder()
    .composite<alive::SelectorProbability>()
      .Weight(1.0f)
      .composite<alive::ParallelNode>()
        .execute<ActionRotate>()
          .Speed(+1.0f)
        .end()
        .execute<ActionAnimate>()
              .Speed(1.0f)
              .Loop(true)
              .Name("turn left")
          .end()
          .execute<ActionWaitFor>()
            .Time(time)
            .Random(time)
          .end()
        .end()
      .Weight(1.0f)
      .composite<alive::ParallelNode>()
        .execute<ActionRotate>()
          .Speed(-1.0f)
        .end()
        .execute<ActionAnimate>()
            .Speed(1.0f)
            .Loop(true)
            .Name("turn right")
        .end()
        .execute<ActionWaitFor>()
          .Time(time)
          .Random(time)
        .end()
      .end()
      .end();
}

Passive Dog Behaviors

A helper function that creates a behavior tree to randomly select from a bunch of passive behaviors like sitting and lying down. Execute these animations once and bail out.

alive::Node* makePassive()
{
    return alive::TreeBuilder()
    .composite<alive::SelectorProbability>()
    .Weight(1.0f)
        .execute<ActionAnimate>()
        .Speed(0.34f)
          .Loop(false)
          .Name("sitting")
        .end()
    .Weight(1.0f)
      .execute<ActionAnimate>()
        .Speed(0.67f)
        .Loop(false)
        .Name("laying idle")
      .end()
    .end();
}

Active Dog Behaviors

Another helper function to create a behavior tree to randomly select active behaviors like walking around or chaising tail. Fail now and then also so the logic falls back to something else…

alive::Node* makeActive()
{
    return alive::TreeBuilder()
    .decorator<alive::ErrorHandlerNode>()
      .composite<alive::SelectorProbability>()
      .Weight(1.0f)
        .node<alive::ConstantNode>()
          .TheTask(ERROR)
        .end()
      .Weight(0.25f)
        .execute<ActionAnimate>()
        .Speed(1.0f)
          .Loop(false)
          .Name("chasing tail")
        .end()
      .Weight(5.0f)
        .composite<alive::SequenceNode>()
          .add(makeRandomTurn(1.0f))
          .end()
          .add(makeRandomMove(4.0f))
          .end()
        .end()
      .end()
    .end();
}

Bodily Behaviors

A behavior tree that runs with high priority. Executes peeing behaviors, and sleeping for example. It also bails out with a high probability to force the top-level behavior to fall back to alternatives.

alive::Node* makeBodily()
{
    return alive::TreeBuilder()
    .decorator<alive::ErrorHandlerNode>()
      .composite<alive::SelectorProbability>()
        .Weight(20.0f)
          .node<alive::ConstantNode>()
            .TheTask(ERROR)
          .end()
        .Weight(2.0f)
        .composite<alive::SequenceNode>()
          .execute<ActionAnimate>()
              .Speed(0.2f)
            .Loop(false)
            .Name("laying idle")
          .end()
          .execute<ActionAnimate>()
            .Speed(0.2f)
              .Loop(false)
              .Name("sleeping")
          .end()

        .end()
        .Weight(4.0f)
        .composite<alive::SequenceNode>()
            .add(makeRandomTurn(1.5f))
            .end()
            .add(makeRandomMove(8.0f, "stalking prey", 0.5f))
            .end()
            .execute<ActionAnimate>()
              .Speed(1.0f)
              .Loop(false)
              .Name("peeing")
            .end()
            .add(makeRandomTurn(1.5f))
            .end()
            .add(makeRandomMove(6.0f, "jumpy run", 2.0f))
            .end()
        .end()
        .Weight(4.0f)
        .composite<alive::SequenceNode>()
          .execute<ActionAnimate>()
            .Speed(1.0f)
            .Loop(false)
            .Name("scratching back")
          .end()
          .execute<ActionAnimate>()
            .Speed(1.0f)
              .Loop(false)
              .Name("scratching")
          .end()
        .end()
        .end()
      .end();
}

Construction

Set-up the different components of this game entity: model and behavior.

void Dog::init(Ogre::SceneManager& sceneManager)
{
    Ogre::SceneNode *root = sceneManager.getRootSceneNode();
    m_pDogNode = root->createChildSceneNode(Ogre::Vector3(0.f, 0.f, 0.f));
    m_pDogEntity = sceneManager.createEntity("puppyEntity", "puppy_r1_ogre.mesh");
    m_pDogNode->attachObject(m_pDogEntity);
    m_pDogEntity->setCastShadows(true);

    alive::Node* behave = alive::TreeBuilder()
      .decorator<alive::RepeatNode>()
        .composite<alive::SelectorNode>()
            .add(makeBodily()).end()
          .add(makeActive()).end()
          .add(makePassive()).end()
        .end()
      .end();

    m_Brain.registry.setAddObserver(*new InitializeAction(*this));
    m_Brain.add(*behave);
}

Updates

There's no additional C++ logic to be executed at runtime. The behavior tree is wired up during initialization, and it deals with everything automatically now.

void Dog::update(const Ogre::FrameEvent& evt)
{
    m_Brain.tick();
}

Action Initialization

Each action is modular and only knows what it needs to know. Actions also have a single virtual extension point implemented using a visitor pattern, which can be used to implement the initialization.

This visitor essentially connects the data stored within the Dog actor and passes it to the actions on a need-to-know basis.

void InitializeAction::visit(ActionAnimate& action)
{
    action.setup(*m_pEntity);
}


void InitializeAction::visit(SpatialAction& action)
{
    action.setup(*m_pNode);
}

InitializeAction::InitializeAction(Dog& dog)
:    m_pEntity(dog.m_pDogEntity)
,    m_pNode(dog.m_pDogNode)
{
}