Trying to better understand how a behavior tree

After doing some research on behavior trees, I have a… barely adequate grasp on how they work. While I fully intend to do more research and experimentation on the matter, there are two concepts that I haven’t been able to figure out. Namely, how does an action node avoid a loop when running? A coroutine can use a yield return statement, but nothing I read about behavior trees suggests that they have that ability. My other question is about what a node returns. From the way it looks, a node has no way of returning a Success state, because it will return a Running state after it completes the first “round” of tasks, and when a non-coroutine method returns something, it terminates.

I’m also interested in improving the code that I intend to use for the behavior tree. I don’t think that there are any glaring errors, aside from what I’ve already mentioned, but it never hurts to be sure.

    EnemyEntity ent;

    public enum NodeState{
        Success,
        Failure,
        Running,
        Error
    };

    delegate NodeState action(EnemyEntity ent);

    delegate bool checkCondition(EnemyEntity ent);

    NodeState sequence(action[] acts){

        foreach (action act in acts) {

            if (act (ent) == NodeState.Failure) {
                return NodeState.Failure;
            }
        }
        return NodeState.Success;
    }

    NodeState conditional(checkCondition[] conditions, action act){

        foreach (checkCondition con in conditions) {
            if (!con(ent))
                return NodeState.Failure;
        }
        return act(ent);
    }

    NodeState singleCondition(checkCondition check, action actIfTrue, action actIfFalse){
        if (check (ent)) {

            return actIfTrue (ent);
        } else {
            return actIfFalse (ent);
        }
    }
       
    action moveInDirection = delegate (EnemyEntity ent){//mostly psudo-code here

        //get the direction to move in
        //if the direction is blocked:
        return NodeState.Failure;
        //else move in the direction
        return NodeState.Running;
        //when finished moving
        return NodeState.Success;
    };

    checkCondition checkNearestEnemy = delegate (EnemyEntity ent){
       
        return ent.IsEnemyInSphere ();
    };

Behavior Trees primarily do decision making. It decides what action needs to be done. but the behavior tree itself doesn’t necessarily perform that action. in that sense Behavior Nodes aren’t meant to be looped over across several frames. you call them one frame and they typically decide what action (or actions) to do and let the character perform them. Couple seconds later the character will once again “beseech the tree for guidance on what to do next”.

Sometimes AI Systems are expensive to run (many are far more than others) which means asking the AI for an action every frame can impair gameplay. Other times a highly reactive AI would break immersion, to a point where the character would never regret for making a poor decision which “seemed like a good idea at the time”. In either case when contacting an high-level AI system you typically want to be intermittent.

When it comes to a return state, Behavior Tree nodes typically make a simple boolean return. True when this node is valid, false when this node is invalid at this time. Decision nodes (Selectors, Sequencers, Parallels, etc.) take these values from child nodes to find the proper branch to take within the tree, thus forming its basic intelligence. Some Behavior Trees also implement Decorators which override or modify what the node says is valid

Behaviour trees should only be ‘ticked’ once per frame. Performing the tick is pretty cheap, so unless you are doing something crazy* you shouldn’t need lower the tick frequency.

One tick should consist of evaluating every active node in the tree. It’s basically a bunch of bool checks. Do this and the only why you should ever get into an infinite loop is of one of your actions is an inifinite loop.

A BT is basically 3 different systems. First there is the BT architecture. Second there is the low level actions. Finally there is the high level tree. These should all be seperate and distinct.

In fact for most developers there is zero value in building the BT architecture. This is one spot where the asset store wins. Here is an asset I use that’s pretty decent. That way when I’m only spending time working on the interesting bits.

  • For the purpose of this thread AAA graphics and RTS games both count as crazy.

I think I have an idea of what you’re saying. If a coroutine/action is running, the behavior tree examines the situation and in necessary, interrupts it. When the action finishes, the behavior tree examines the situation and figures out which action to take next.

What I’m working uses a mix of sprites for the characters, and very low-poly objects for terrain and a few bosses, and AI isn’t much more complex then most enemies in a Mega Man game (chase the player, don’t fall into anything it can’t jump out of, etc.), so that’s a relief. Thanks for the link, I’ll have to check it out. Im currently playing around the the beta version of Behavior Bricks. but the Pant BT looks very promising.