[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2.3 BTTest Tutorial

This is a tutorial to show how you can use a Behaviour Tree (BT) in your application.

The source files of this tutorial can be found in the 'apps/bttest' directory that is included with CEL. The world and entity file can be found in the 'data' directory ('walktut_world' and 'walktut_entities').

During the game you can use the following inputs:

This tutorial adds a Behaviour Tree to the previous tutorial WalkTut. As before with the QuestTest tutorial, this tutorial will focus only on the changes to WalkTut neccesary to add a Quest. All additional code can be found in the MainApp::CreateBehaviourTree () method in app.cpp.

A CEL behaviour tree is made up of nodes (classes implementing iBTNode) which come in the forms of selectors, decorators and leaf nodes. Each node upon execution returns either true or false signalling its success or failure.

A behaviour tree running in CEL will execute the root node once per frame, evaluating the whole tree until the root node either fails or succeeds. The intention is that behaviour trees will be assigned to entities to model their behaviour and decide their actions.

To create a behaviour tree, first all nodes of the tree must be created. This requires a refrence to the plugin manager that can be obtained by:

 
csRef<iPluginManager> plugin_mgr = 
  csQueryRegistry<iPluginManager> (object_reg);

Each node is then created by a call to the method csLoadPlugin(). An example of this for a default selector is:

 
csRef<iBTNode> root_node = 
  csLoadPlugin<iBTNode> (plugin_mgr, "cel.selectors.default"); 

The default selector, executes its children in the order they were added until one succeeds. Provided one child succeeds so does the selector, but if all children fail the selector fails. Other standard BT nodes can be loaded with the following identifiers.

In the bttest application in lines 214 to 253, you will see a number of nodes set up representing each of the above types at least once. These will shortly be connected to demonstrate a working bt but first a number of specific details for certain nodes will need to be set up.

Selectors require no further setup, other than the connection of children that will be covered later. Some decorators, however, do. For example:

 
csRef<iExecutionLimitDecorator> explicit_execution_limit_node = 
  scfQueryInterface<iExecutionLimitDecorator> (execution_limit_node); 
explicit_execution_limit_node->SetExecutionLimit("1");

This sets up the execution limit decorator to only allow its child to be executed once. Similarly, the loop decorator must be set up with the number of iterations to perform and the parameter check condition must be set up with a value and parameter to compare. Examples of this can be found in lines 260-267 of app.cpp.

Finally the triggerfired node and btaction nodes, that act as wrappers to the recently refactored cel triggers and rewards, must be set up. The trigger fired node requires a trigger be created and assigned whilst the btaction node requires a reward to be created and assigned. This is marginally more complex as the trigger example below shows:

 
csRef<iTriggerType> trigger_type = csLoadPlugin<iTriggerType> (plugin_mgr, 
  "cel.triggers.inventory"); 
csRef<iTriggerFactory> trigger_factory = trigger_type->CreateTriggerFactory (); 
csRef<iInventoryTriggerFactory> explicit_trigger_factory = 
  scfQueryInterface<iInventoryTriggerFactory> (trigger_factory); 
explicit_trigger_factory->SetEntityParameter ("player"); 
explicit_trigger_factory->SetChildEntityParameter ("box3"); 
csRef<iTrigger> trigger = trigger_factory->CreateTrigger (params); 
csRef<iTriggerFiredCondition> explicit_trigger_node = 
  scfQueryInterface<iTriggerFiredCondition> (trigger_check_node); 
explicit_trigger_node->SetTrigger (trigger);

First a trigger type object of the required type must be loaded. This can be used to create a trigger factory, that is set up to create the required trigger. The specific setup details for the trigger factory will depend on the type of trigger used. The BT wrapper for triggers is capable of using any CEL trigger.

A similar method is used for rewards, in lines 284-325, however as all rewards in this behaviour tree are of the same type (debug message) the type need only be loaded once and the factory only created once. After that the message parameter (for this specific reward type) can be edited and a new reward made. There is no need to repeat the loading of the type and creation of the reward factory. This method of editing the setup and not recreating the factory can also be applied to triggers where multiple instances of the same trigger type are needed.

With all nodes setup they must now be connected. This is a relatively simple process whereby a child is added to a parent using the parent's AddChild () method. For example:

 
root_node->AddChild (initial_sequence_node);

The full process of connecting this tree is encapsulated in lines 325-247 of app.cpp. The connected tree's behaviour can be described as:

Finally, for the tree to begin execution. A final node of type behaviour tree must be loaded, the tree's root node connected as a sole child and the behaviour tree node executed. This is done by:

 
csRef<iBTNode> tree = csLoadPlugin<iBTNode> (plugin_mgr, 
  "cel.behaviourtree"); 
tree->AddChild(root_node); 
tree->Execute(params);

And so completes this tutorial. Hopefully, by following this example you can now construct your own cel behaviour tree. The more interesting design challenges now come from how to combine these tools to create complex and fun behaviours for the characters in your game. For discussions on design choices when constructing behaviour trees, the author highly recommends http://www.aigamedev.com.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

This document was generated using texi2html 1.76.