Machine.js

 by mary rose cook
Make behaviour trees in JavaScript
v1.1 changelog

What is this library?

Machine.js lets you use a hierarchical state machine to control a JavaScript object.

1. Define a behaviour tree as JSON.

{
    identifier: "idle", strategy: "prioritised",
    children: [
        {
            identifier: "photosynthesise", strategy: "sequential",
            children: [
                { identifier: "makeEnergy" },
                { identifier: "grow" },
                { identifier: "emitOxygen" },
            ]
        },
        { identifier: "gatherSun" },
        { identifier: "gatherWater" },
    ]
};    

2. For each leaf state, define a function that enacts the behaviour for that state. So, for the gatherSun state, define a function that gathers sun.

3. For each state, define a can function that returns true if the actor may move to that state. So, for the gatherSun state, define a function canGatherSun that returns true if the sun is out.

Licence

The code is open source, under the MIT licence. It uses Base.js by Dean Edwards.

Demo

A tiny ecosystem is living right now in your browser debug console. Rain falls, the sun shines. An oak tree absorbs water and sunlight, makes energy and grows. Open your console and see. View source to see the code.

Tutorial

1. A primer on behaviour trees

A behaviour tree is a normal state machine, except the states are connected in a hierarchical structure. At fork states, the actor controlled by the tree does nothing. A fork state simply leads into one of a set of subsequent states (its children). At leaf states - states with no children - action is taken by the actor.

Movement from state to state is controlled by two mechanisms: strategies and can functions. Each fork state has a strategy that determines which child state to move to. Each state has a can function that returns true if the actor is allowed to move to that state.

2. Import libraries

You will need to import machine.js and base.js. If you are running your behaviour tree in a web page, you might do that like this:

3. Define an actor

This is the object that will be controlled by the behaviour tree. Any normal JavaScript object will do.

4. Write a behaviour tree

Write some JSON that defines your actor's behaviour tree.

5. Define can and action functions

For each leaf state in your behaviour tree, define a synonymous function. This will be run once when the actor moves to the corresponding state. For each state (leaf or fork), also define a function called canNameOfState. This should return true if the actor is allowed to move to the corresponding state.

For example, if your leaf state is called kiss, you would define a function called kiss that does the kissing, and a function called canKiss that returns true if kissing is allowed. Put all these functions into an object:

6. Put it all together

Instantiate your actor. Make an instance of Machine. Use the instance of machine to generate a behaviour tree, passing in the tree JSON, your actor and the object with all the state functions. Repeatedly call tick() on the current state to get the next state.

Reference

Syntax of the JSON

States are defined as JSON hashes. All must have an identifier property that is set to the name of the state. Fork states also have a strategy property and a children property that is set to a list of subsequent states.

Can functions

Each state in the machine has a corresponding can function called canNameOfState. This returns true if the actor can move to this state. You can omit this function if the state is always permissable.

Strategies

Fork states are those that have children. A fork state has a strategy assigned to it that determines which state the machine should subsequently move to. Machine.js supports two strategies:

Prioritised

Call the can function of the first child. If it returns true, move to that child state. If it does not, try the next child, and so on. Once a child has been run, or the final child has been tested and returns false, go back to the parent fork state.

Sequential

Go through all the child states, executing each one for which the corresponding can function returns true. Once all the children have been processed, move back to the parent fork state.

Warping

Sometimes, you want the actor being controlled by the behaviour tree to respond to events. For example, the player might give a soldier orders to return to base.

You can do this with warp(). First, define your behaviour tree so that it has a special return to base branch that is a direct descendent of the root node:

Next, define a function on the actor that can be called to invoke the return to base order. In it, instead of using the tick() function to get the next state, you use warp() and pass in the identifier of the return to base state:

A word of warning. The warp() function is a violation of the deterministic beauty of a behaviour tree. Use it sparingly.