The Behave project, along with Path (now open-source MIT), were among the first projects I did in Unity after picking it up in the beginning of 2008. In an all too familiar story, I created the tools to replace those I had used at my previous job, but ended up focusing more on the tools than my game project.
I first shared the project at Unite 08, after Tom Higgins and David Helgason cornered me in a bar and persuaded me to give an open mic talk the next day on what at the time was the only full-on middleware solution integrated in the Unity editor.
This was Behave 0.3b. 1.0 was released a couple of months later and 1.2 went live in 2010 as one of the launch packages of the Asset Store.
When at Unity, my schedule was pretty packed, so 2.0 was quite a while under way. Large refactors, support for multiple engines and platforms plus feature creep did not help much either. But here we are now on 2.3.2 – fortunately updates since the release of 2.0 in August 13 did not take the time that 1.4→2.0 did.
So with the history lesson out of the way, what is Behave, really? In short, it is an AI behaviour logic system. It allows you to visually design behaviour trees, link them directly to your own code through a highly efficient compiler and finally debug the trees, running in the editor or on your target device.
One of the guiding principles behind Behave is to as much as possible avoid systemic requirements. That is designs which might chain your runtime integration into a certain way of operating. The result is a very lean and efficient runtime, with the integration possibilities more or less just limited to your imagination and practical needs.
Behaviour trees you say? Yes I do. A widely standardised method of describing behaviour logic, first used on scale in Halo, behaviour trees set themselves apart from methods like state machines in that they scale much better, are easy to read be made responsive.
I am going to assume familiarity with state machines (as you might know them from Mecanim or plugins like Playmaker) – to use them as a reference in describing behaviour trees. Though I clearly cannot describe all that is behaviour trees in the length of this article.
While state machines are in the business of selecting states within which actions are performed, behaviour trees build state implicitly from their structure and focus squarely on selecting actions to perform.
This means that while state machines allow you to set up states with any number of transitions (at scale often ending up in a hard to maintain spider-web of transitions), behaviour trees have a strict set of rules for connectivity and evaluation.
A behaviour tree is basically an upside-down tree structure – evaluation starting from the root at the top, filtering through a number of interconnected control nodes and ending in leaf nodes – actions. Actions are where you interface game logic with behaviour logic, hooking up sensors and motors.
The responsiveness of behaviour trees stems from the fact that they are most often evaluated continuously, at some frame rate. Each evaluation start at the top and given the rules for the different control nodes, the flow is directed until an action node is hit.
Each node will then, rather than block on execution, return a signal back to its parent node. The parent then interprets, reacts and returns its own signal back up the chain until the root is reached again.
This signal can be one of three: Success, Failure or Running. Success and Failure obviously meaning that the node succeeded or failed in its task and Running meaning that the node has not yet reached the conclusion of its task and requests to get re-pinged on the next tree evaluation.
Example actions could be HasTarget, which would return Success if the agent executing the tree has a target and otherwise Failure or GoToTarget, which would return Running while on its way to the target and then Success when reached or Failure when determined to be unreachable.
So while the graphical editor lets you easily connect together these control nodes and actions, you of course need to hook this up to your AI agents at some point.
This is achieved via the one-click compilation of your Behave library (the asset containing your trees), which for the Unity target compiler generates a .net assembly. As it is output in your assets folder, Unity will automatically compile it in with the rest of your code.
What this means is that once you hit compile, you will be able to access generated classes from your code, representing your behaviour trees at runtime.
The central method of the generated library class “BL[AssetName]” is the
InstantiateTreemethod. This takes as parameter first the tree type you wish instantiated (via an enum generated from the tree names in the editor) and second the agent you wish to integrate the tree with. This is the class which will need to implement the action handlers described earlier.
Out of the box Behave offers two ways of implementing action handlers. The default is you derive from the IAgent interface. In this case Behave will reflect your class for action handlers on instantiation, much like the Unity messaging system.
The second way of implementing action handlers is to define an agent blueprint in your library. At runtime, this results in an abstract agent class being defined, with predefined virtual handlers for all actions used by the trees supported by that blueprint. This method is less flexible, but removes the overhead of reflection on tree instantiation and gives you auto-complete on action handler methods in your favourite code editor.
With handlers defined, you then simply call the
Tick method on the tree instance at a frame-rate or in response to some game event and the tree will in turn call one or more of your action handlers, depending on its design.
For core character behaviour logic, I usually create a coroutine named
AIUpdate or turn
Start into a coroutine, containing a loop which ticks the tree and then yields
WaitForSeconds one divided by the frequency property of the tree. This property serves no other purpose at runtime than to communicate an intend from the designer to the programmer.
So as you can already see at this point, Behave does indeed follow the design goal of low complexity, leaving design decisions in the integration layer completely up to you.
The Behave runtime has much more runtime API and integration flexibility, but that is unfortunately a bit much to cover in this overview.
I hope you found this introduction useful and that you will consider using Behave for your next AI project. I would recommend you check out the following sources for more information on behaviour trees:
And of-course more information on Behave canbe found at:
Emil “AngryAnt” Johansen