Completeness

Though some of the runtime API has already been covered in the Flow chapter, the aim here is to just list everything and go through it from start to end.

Basics

There are three major types you will be working with when coding agains the Behave runtime:

Additional types include the all-important BehaveResult enum and the tick/reset forward delegates:

The ExceptionHandling enum will be covered later.

Library

Let’s just cover the library class really quickly as it is quite simple and straight forward:

The above class is generated based on a library asset named MyLibrary. It, like its editor-time counterpart, contains all the data of your library. All key strings have been translated to six enums, consecutively valued:

Whenever the Behave API gives you or expects an integer value, you will usually need to cast it to or from one of these – depending on the context. The exceptions are the InstantiateTree and InstantiateCollection methods of the library class, which respectively take a TreeType and a CollectionType value directly and their counterparts, Type which return the corresponding enum value when passed a Tree or Collection reference.

Instantiation

To get things moving in the Behave runtime, you first need to instantiate a tree or a collection. For this you have the InstantiateTree and InstantiateCollection methods. Additionally InstantiateTree takes an agent parameter, since rather than just instantiating, InstantiateTree goes through the following process:

  1. Create an instance of the generated Tree class matching the given TreeType.
  2. Assign the IAgent reference as the default handler agent for the Tree instance (accessible via Tree.Owner).
  3. Run Tree.ReflectForwards on the Tree instance, providing the IAgent as parameter.
  4. Return the Tree instance.

If the IAgent reference passed is derived from a generated agent blueprint, step 3 is skipped, since all action handlers are already known through the abstract interface of the blueprint.

Both methods also take an optional ICollection parameter. For trees, this collection is simply assigned via Tree.ActiveCollection. For collections, the specified collection is assigned as the fallback for the new collection.

Queries

As mentioned, Type will return the corresponding enum value of either a Tree or ICollection reference – assuming that those have been instantiated from the collection. An alternative method to check for instantiation is through DidInstantiate, which simply returns a boolean when given either reference type.

Finally, SupportedRecords will return an array of RecordType values supported by the given ICollection. If the ICollection was not instantiated by this library, an empty array is returned.

IAgent

Jumping a little in the initial list of trees, let’s cover the agents next since they are comparatively simpler than the Tree.

Implementing the IAgent interface requires that you implement the three methods it holds:

If you define an agent blueprint in your library and inherit from the resulting generated class, you will not have to implement any of the IAgent methods.

The default handlers of IAgent, as well as direct handlers for all the actions used by the trees supported by the agent blueprint, are automatically generated as virtual methods in the blueprint class:

Agent blueprint types also define a static SupportedTrees property, returning an array of TreeType values corresponding to the trees supported by that blueprint.

Notice that library classes are prefixed BL, while agent blueprints are prefixed BA.

Handlers

IAgent defines the default handlers for an agent. Since each action can have both its own tick and reset handlers, the default Tick and Reset handlers are only invoked if no custom handlers exist for an action.

This is where the IAgent instance passed on Tree instantiation comes in. If no forward has been set either manually or through introspection from Tree.ReflectForwards, the default handler for the corresponding event is invoked on that IAgent instance.

As the init phase does not have a default handler, the absence of a specific forward for init on an action will result in an assumption of successful init.

For agent blueprints, the default implementation of all custom handlers is to invoke the corresponding default handler. For init handlers, BehaveResult.Success is just immediately returned.

Since priority selectors do not have names, evaluations of these always result in invocation of the default SelectTopPriority handler.

Parameters

Prior to version 2.2, the following was true for parameters (up to date description follows):

Depending on which implementation of a handler you are using, you may receive one or more of the following parameters:

Of these, sender, agent and data are probably the most straight forward. The sender parameter is simply a reference to the Tree instance from which the current tick originated.

The agent parameter is a reference to the default agent of that tree – unless a different IAgent reference was passed to the Tick call.

Similarly, the data parameter is directly what was passed as the second parameter of the Tick call. If nothing was passed, this will always default to null. The data parameter can be useful for evaluating a tree in the context of something.

The stringParameter and floatParameter parameters are very straight forward. These are simply the parameters set in the tree canvas on the actual node resulting in this invocation. Default values are "" and 0.0f respectively.

Finally the IDs parameter as sent to the SelectTopPriority handler is an array of integer values corresponding to BLYourLibrary.PriorityType values. The only valid return values of SelectTopPriority are the values contained in the IDs array. SelectTopPriority will never get invoked with an empty IDs array.

Each handler receives a “sender” parameter of type Tree. This is simply a reference to the Tree instance from which the current tick originated. From this sender parameter, the active agent reference can be obtained – as well as action parameters and much more.

The SelectTopPriority handler gets passed an additional parameter, “IDs” of type int[]. It is an array of integer values corresponding to BLYourLibrary.PriorityType values. The only valid return values of SelectTopPriority are the values contained in the IDs array. SelectTopPriority will never get invoked with an empty IDs array.

Tree

The more complex of the tree core classes is the Tree class. Every tree defined in your library, through the Behave editor UI, results in a generated class inheriting from Tree.

These generated classes are private classes contained in the generated library class and can (or should) only be instantiated through the InstantiateTree method of that class.

Execution of the tree is primarily controlled via the Tick and Reset methods of the class. Tick will tick the node connected to the tree root, which in turn will tick its children if any and the result value of the root node is finally passed back as the return value of the Tick call.

During tree evaluation, Tick will commonly return a value of BehaveResult.Running until the tree evaluation ends up completing or failing the requirements of the root node. At this point it is common to call the Reset method on the tree instance and then return to ticking it again from a fresh state.

TickWrapping will automatically respond to a non-Running result by resetting the tree for another pass. Thus if you have no other needs to respond to the result, you might as well use this method.

An alternative to the regular Tick function, TickContinuously will keep ticking the tree as long as it returns Running or until the given iteration cap it reached. In either case, the last return value of the tree is then returned by TickContinuously.

As mentioned in the Parameters description of IAgent Handlers, the Tick and Reset methods optionally take an alternative default IAgent reference and an object reference for context.

There is much more than just Tick and Reset to Tree classes though:

Forwards

As mentioned earlier, actions can have custom handlers in stead of the default ones defined by the IAgent interface. These can be set directly by calling Set[Init/Tick/Reset]Forward on a tree instance, passing a BLYourLibrary.ActionType value cast to int as well as a method matching one of the core handler delegates.

A different way of registering forwards en-masse is by calling ReflectForwards on the tree instance, passing a reference to a class which will be searched for possible handler matches via introspection. These are the handlers picked up by ReflectForwards:

Note that the object reference passed to ReflectForwards does not necessarily have to implement the IAgent interface. And that you can call all of the forward setters as well as ReflectForwards as many times as you like for a given tree reference – on as many target instances as you like. Additionally, it is completely valid to have one target method handle the forwards of multiple actions.

ReflectForwards is not a destructive operation – any forwards already in place, which do not match handlers on the passed reference, will remain unaffected.

ResetForwards will clear any set forwards on that tree – including those set up when passing an IAgent reference during tree instantiation. Afterwards, for reflected agents, the default IAgent defined handlers will be invoked for all actions. For agent blueprints, however, the original virtual handlers will be used.

Specifying the optional target parameter on ResetForwards will reset only forwards to handlers on that object.

Accessors

For more specialised handlers, the Tree class has several accessors for data on the current state of the tree.

Owner is set when the tree is instantiated, using the IAgent reference passed as instantiation parameter. Whenever no forward is set for a particular action event, that event is forwarded to the Owner of the tree in question. This is also where IAgent.SelectTopPriority calls go. Do note that setting Owner does not clear any already set forwards. For this, a call to ResetForwards is needed.

ActiveID and ActiveContext are most useful in a tick handler. The context of a tree can be used to expand the functionality of actions. Cast it to BLYourLibrary.ContextType for use. See the Nodes section for more information on context behaviour.

If working in the default handler or in a handler set up for multiple actions, ActiveID cast to BLYourLibrary.ActionType tells you which specific action is currently being evaluated.

ActiveStringParameter and ActiveFloatParameter contain data configured per-component in the tree editor. This allows you to customise the handling of a particular instance of an action.

If you are working in threads or have a function being used both inside and outside the tree tick, the TickActive accessor quickly tells you if a tick is currently being evaluated or if the tree is currently inactive.

ActiveData holds the same value as was optionally passed to the original Tick call. There are no requirements on this data, but it allows you to provide some context for the tick, relevant for your handlers.

ActiveCollection will by default return the collection set as default collection when the tree was instantiated. Alternatively, a new default collection can be set directly via this property. The active collection is used for record get and set requests coming from nodes in the tree.

LastTicked can be useful as debug information or for that very special hack, while Frequency and DataSize are static – Frequency being a direct output of the frequency variable specified by the tree designer via the Behave editor UI and DataSize being an estimate on memory consumption for this particular tree instance.

Once you determine that you have DebugAvailable (which it will only be if the library has been built for debug), you may access the Name of the tree, the currently ActiveComponent as well as check if the tree is Plugged in or has been unplugged by the debugger. Plugged also has a setter which you effectively can use to throw your own Behave breakpoints.

Composition

When composition is of more use than inheritance, action classes can be (re)used as handlers for a single tree action in stead of relying on the handling of multiple actions on singular agent implementations. This is achieved via the IActionClass interface and Tree.ReflectForwards.

The IActionClass interface lets you handle not just Init, Tick, and Reset, but also OnForward, which is invoked just before the action forwards are hooked up for the sending tree.

Just like with the other events, the action in question can be accessed via Tree.ActiveID and based on all available data, the IActionClass can decide whether or not to allow the forward. By returning false from OnForward, the tree will abort its forwarding attempt.

Action classes are not only intended for one to one relations between tree & agent instances and action class instances. They work equally well for handling multiple action types on one IActionClass or having multiple tree and agent instances make use of the same IActionClass instance.

Collections

Containers of your designed records, collections can be instantiated just like behaviour trees – via the InstantiateCollection method.

Instantiated collections can be used directly as well as set as the default collection of a tree – either at instantiation or later. Default collections are used whenever a behaviour tree node performs a record operation.

Aside from instantiating pre-defined collections, implementing the ICollection interface will let you implement custom record handlers.

InstantiateCollection takes an optional ICollection parameter aside from the first CollectionType parameter. This second collection is used as a fallback. If the instantiated collection is queried about a record it does not support, it will refer this query to its fallback collection.

Aside from fallbacks, collections can have handlers for specific records forwarded – similarly to how action handlers can be forwarded. The library methods SetGetForward, SetSetForward, ReflectForwards and ResetForwards provide an interface for adjusting these forwards.

When a forward is in place, any request to that collection for that specific record, will be handled by the forward in stead – essentially functioning as an override.

Library.ReflectForwards works just like Tree.ReflectForwards. The handlers picked up are:

Get requests to records not supported by instantiated collections with no fallback, result in -1. Similar set requests result in false – as to set requests to curve and graph records with no forward set.

Debugging

In order to transmit the local state to remote debuggers, you need to update the debugger at some rate.

If you want to completely control when and how this happens, simply call Debugger.Update at your leasure. However, would you rather just start up the debugger and not have to worry about its runtime afterwards, DebugUpdater.Start lets you start a continuous update of the debugger – letting you specify a frequency and defaulting to 20 frames per second.

Calling Start while the debug updater is already running will merely change the frequency at which it is updating. Enabling debugger auto-launch in the library build settings means a call to Start on tree instantiation – passing the frequency value specified in those same settings.

Passing a value of zero or less to DebugUpdater.Start results in the debugger getting updated every frame. However this is not recommended.