Table of Contents

Quick Start Guide

What is Behavior Bricks?

Behavior Bricks is a plug-in for Unity3D that let you design the Artificial Intelligence (AI) for Non-player Characters (NPCs) in a graphical way. Using behavior trees as the graphical modelling language, Behavior Bricks provides rich and ready-to-use actions and conditions waiting for you to use them. But, if they are not enough for developing your next awesome game, you can always extend the built-in library using a powerful API. Behavior Bricks helps you in that task too, organizing your home-made behaviors in such a way that reusing them (in other games, or even in more complex behaviors) becomes a child's play.

This quick start guide shows you the basics of Behavior Bricks through the creation of your firsts behaviors; this should be enough for providing the first insights into the behavior editor. The guide assumes you have some previous experience using Unity3D, so it will not detail every single step done with it. You are also supposed to have been loaded Behavior Bricks into a new Unity project. Refer to the download instructions in other case.

The final scene of the guide is available in the Behavior Bricks package, under Samples\QuickStartGuide\Done folder. The behaviors are available in the same folder in the Collection (more about it later).

Creating the project and the scene

Start by creating a new 3D project and importing BB package ...

In this tutorial we create a small scene with two interactive game objects. The first one represents the player, and is moved around using mouse clicks. The second one is an "enemy" that wanders around, and pursues the player when sees him. For simplicity, all the models of the scene are basic primitives, so no assets are needed.

If you feel bored of setting up the initial scene, you can use the premade InitialScene in the Samples\QuickStartGuide folder of the Behavior Bricks package (remember to make a copy if you want to keep the original empty version). In other case, you should take the next steps:

The scene should be set up now.

Wander: the first enemy behavior

It's time to create our first simple behavior for the Enemy. Go to the Behavior Bricks menu (Window - Behavior Bricks), and open the editor. That will open the Behavior bricks editor. Keep in mind that the window behaves like any other window in Unity3D: it can be dragged to any location so you are able to customize your workspace as desired.

The first step is to create a new behavior tree by writing its name in the text field at the upper part of the Collection tab and cliking the Create new behavior button. Although it is not important for this tutorial, you can create a hierarchy using / in the name (for example Basic/Wander). Also you can notice that the name used in the Inspector and the asset name are independent, although in this example we have chosen to make it the same, as shown in the figure.

Once the new behavior asset has been saved it will appear in the Behaviors list and will be opened as a new tab in the behavior graphical editor, showing an initially empty canvas. In order to add a new node to the behavior you just need to right-click on any empty point of the canvas and choose one from the drop-down menu. In order to simplify the search, a text field is available for filtering the elements by name in the menu. Browse the hierarchy to have an idea about the built-in actions, conditions, behaviors and composite nodes.

Our first step will be to make the enemy go to a fixed position, in (-20, 0.5, 20), moving parallel to the upper floor edge.

At runtime, behaviors are executed by the behavior executor, a component that must be added to any game object that would be controlled by Behavior Bricks. Select the Enemy and add the component Behavior Bricks - Behavior Executor.

The new component shown in the Inspector provides a place to drop the behavior to execute. From the asset folders, look for the 'Wander' behavior, and drop it in the Behavior variable. Launch your project and you will see the Enemy moving to his left. Be aware of the Unity warning "The Enemy game object does not have a Nav Mesh Agent component to navigate. One with default values has been added". This is due to a missing component in the Enemy. Specifically, MoveToPosition action uses the scene nav mesh, which requires a nav mesh agent component. Unity automatically adds it on runtime when missing, and warns about that. You can avoid the warning adding that component beforehand yourself.

Generalizing behaviors: the blackboard

Our behavior moves the Enemy to (-20, 0.5, 20), a position hard-wired directly in the behavior. This is usually a bad idea. Keep in mind that we could have more than one game object sharing the same behavior and we usually will want to finetune some parameters of the behavior in order to provide diversity.

We can, then, do this better generalizing the behavior using the blackboard. The behavior blackboard acts as the behavior memory, structured by means of attribute-value pairs. Actions like MoveToPosition can read their parameters from there instead of using hard-wired constant values and, even better, the blackboard can be configured externally from the game object Inspector.

As seen, the value for the new blackboard field can be globally changed in the behavior properties. But, more interesting, it can also be changed in the behavior executor component properties:

Although it is not needed for this tutorial, it is possible to assign the same Wander behavior to as many game object as desired, using different wanderTarget values for all of them, creating diversity. The value provided for the wanderTarget parameter in the behavior becomes a default value used for those game objects that have not had it changed in their executor component. This acts in a similar way to the values in the prefabs and their instances.

Composing behaviors: internal nodes

A wander behavior that just goes to a fixed position is hardly motivating. We will improve it selecting a random position.

The key point here is that the actions GetRandomInArea and MoveToPosition are now connected through the wanderTarget blackboard parameter.

In this moment we have two actions in our behavior but what of them will be executed earlier? Obviously, we want the GetRandomInArea to be executed before MoveToPosition, but if you observe both actions, you will notice the R in the upper-right corner of one of the nodes.

The action marked with an R (MoveToPosition) is the root of the behavior tree and will be executed in the first place. In fact, GetRandomInArea will not be executed at all. In order to create a behavior with two actions, we need to compose them.

If needed, or if you haven't used the "InitialScene":

The final version of this Wander behavior is available as Done Wander in the Behaviors Collection.

Conditions and perception: nodes that fail

Although Behavior Bricks is mainly designed for creating the NPC and tactic or strategic AI, it can also be used for creating any other game logic, as menu control, HUD movement or even dynamic music manipulation. In this section we will create a behavior for the player, that will let us introduce new actions and our first condition.

We want the player to move around using the mouse. This requires three steps:

When the user clicks the screen, his avatar will move to that position, so we need to create a behavior that feels the environment and acts according to it. This perception is managed through conditions.

Conditions are nodes that, when reached, end immediately and inform their parent nodes whether they were evaluated as true or false. In behavior jargon, when a node returns false it is said to fail, and to succeed if returns true. The distinctive feature of the internal composite nodes (Sequence's and Repeat' s are the only ones we have used so far) is how they react to the values returned by their children conditions (if they fail or succeed).

In this section we will use the built-in CheckMouseButton condition that test if the user has just pressed one of the mouse buttons and ends with success in that case.

It is worth mentioning that while doing the behavior, we have completely ignored the fact that the CheckMouseButton condition returns a value. When a child condition succeeds (returns true), Sequence's will continue executing their next child, if any, with no delay. But if a condition fails (returns false), they will end immediately and fail themselves. As you might expect, Sequence's succeed when they have executed all their children and none has failed (returned false).

For our example, it is also important how Repeat nodes react to conditions.

When playing the scene, is likely that the user does not press the left mouse button in the very first game loop. So the CheckMouseButton will end with failure. That will cause the Sequence to also fail, and so the Repeat. The Player behavior will then end immediately (with failure) and the behavior executor will not have anything to do afterwards.

Fortunately, we can configure the Repeat node policy.

Just in case you are wondering, the Sequence node has not a configurable policy similar to that one in Repeat. As said before, the way sequences react to children failures is in fact the decisive feature that make them sequences in the first place. If you need an internal node that tries the next child when the previous one fails, then you would want to use a Selector. Selectors end as soon as one child ends with success, and execute the next child when the preceding one has failed.

Currently, both the enemy and the player move at the same speed.

You can find the final behavior in the Collection with the name DoneClickAndGo.

Changing your mind with priority selectors: wander and pursuing

Now that the player can move around, the enemy seems quite dump because he moves around completely ignoring the player. In this section we will create a new enemy behavior that will pursue the player when he is "seen", and will come back to a home (fixed) position in other case.

Note that, in fact, this behavior includes other two. The preferred behavior is pursuing the player. But this is only possible if the player is near enough to be seen. In other case, the enemy will execute the fall-back behavior consisting on moving to the home position. As soon as the player falls in the enemy field of influence, that movement should be aborted and the preferred behavior started.

This composition of behaviors with different priorities is achieved through the Priority composite node. It is a selector in the sense that as soon as one of its children ends with success, it also ends. But each child has a condition that must be true in order to launch it. And, more important, when a child is being executed, all the conditions of preceding sibling nodes are evaluated and, if any of them becomes true, the less priority behavior is immediately aborted and that one is started.

Once there, the enemy will not pursue the player any longer. A Priority selector ends when their children end, so the behavior finishes when the enemy reaches its home position. Make the Priority child of a new Repeat node, and play the scene again. Now the enemy will pursue the player even when he is in his home position.

Composing bricks: reusing behaviors

Behavior bricks promotes behavior reusability not only because behaviors can be used simultaneously in many game objects, but because behaviors can be used as black boxes in other ones. In that way, the Collection becomes the cornerstone of the reusability in Behavior Bricks.

As always, you can find the final behavior in the Collection under the name DoneEnemyBehavior.

Initializing the input parameters of a behavior tree from code

In certain situations it may be interesting to create a prefab of an entity that has a Behavior Executor component in it. In those situations the input parameters of the behavior tree executed by the behavior executor can not be initialized with objects from a scene, since Unity does not allow a prefab to be configured with objects from a scene that may or may not exist when the prefab is instantiated. In this case, we can use a spawner object that can be configured through the Unity inspector and can instatiate the prefab and configure its behavior executor via code. As we will see later this open a number of possiblities for creating and configuring game objects that execute different instances of behavior trees.

First, create a prefab from our current Enemy object in the scene and delete it from the scene. The prefab has lost the values that were asigned for the wanderArea and player parameters in the Behavior executor of the Enemy game object. Then, we are going to create a Spawner script to instantiate and configure the enemy prefab:

    using UnityEngine;
 
    public class Spawner : MonoBehaviour {
 
    public GameObject prefab;
    public Vector3 position;
 
    public GameObject wanderArea;
    public GameObject player;
 
	    void Start () {
            GameObject instance = 
              Instantiate(prefab,position,Quaternion.identity) as GameObject;
            BehaviorExecutor behaviorExecutor = 
              instance.GetComponent<BehaviorExecutor>();
            if (behaviorExecutor != null)
            {
                behaviorExecutor.SetBehaviorParam("wanderArea", wanderArea);
                behaviorExecutor.SetBehaviorParam("player", player);
            }
	    }
    }

Once you have finished, create an empty GameObject named Spawn in the scene, add the Spawner component, and set the component variables:

Now play the scene and everything should work as before.

In Behavior Bricks efficiency matters

Once we can create and configure through code instances of prefabs running behavior executor components, we can multiply the number of enemies. In the next example we will create a spawner component that creates a given number of enemies at random positions and randomly make them follow the player or one of the previously created entities.

    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
 
    public class MassSpawner : MonoBehaviour
    {
 
        public GameObject prefab;
        public GameObject wanderArea;
        public GameObject player;
 
 
        public int Spawns = 750;
        int spawnCount = 0;
        List<GameObject> entities;
 
        void Start()
        {
            entities = new List<GameObject>();
            entities.Add(player);
            InvokeRepeating("Spawn", 0f, 1.0f / 1000.0f);
        }
 
        void Spawn()
        {
            if (spawnCount <= Spawns)
            {
                GameObject instance = 
                  Instantiate(prefab, GetRandomPosition(), Quaternion.identity) 
                  as GameObject;
                BehaviorExecutor component = 
                  instance.GetComponent<BehaviorExecutor>();
                component.SetBehaviorParam("wanderArea", wanderArea);
                component.SetBehaviorParam("player", 
                                           entities[Random.Range(0, entities.Count)]);
 
                ++spawnCount;
 
                entities.Add(instance);
            }
            else
            {
                CancelInvoke();
            }
 
        }
 
        private Vector3 GetRandomPosition()
        {
            Vector3 randomPosition = Vector3.zero;
            BoxCollider boxCollider = wanderArea.GetComponent<BoxCollider>();
            if (boxCollider != null)
            {
                randomPosition = 
                  new Vector3(
                    Random.Range(
                        wanderArea.transform.position.x -
                        wanderArea.transform.localScale.x * 
                        boxCollider.size.x * 0.5f,
                        wanderArea.transform.position.x + 
                        wanderArea.transform.localScale.x * 
                        boxCollider.size.x * 0.5f),
                    wanderArea.transform.position.y,
                    Random.Range(
                        wanderArea.transform.position.z - 
                        wanderArea.transform.localScale.z * 
                        boxCollider.size.z * 0.5f,
                        wanderArea.transform.position.z + 
                        wanderArea.transform.localScale.z * 
                        boxCollider.size.z * 0.5f));
            }
 
            return randomPosition;
        }
    }

Once the enemies have been spawn you can check the scene stats, by pressing the Stats button in the Game pannel, and see how many frames per second (FPS) are you getting, 50 in the example shown in the figure.

One way to increase the performance without decreasing the number of spawns or making modifications to the behavior tree is to modify the field Max Task Per Tick of the Behavior Executor component of the Enemy prefab, by default its value is 500. Set it to 1, play the scene again and see how it affects performance. In this case our frame rate increases up to 65 FPS without apparently affecting the observed behavior of the enemies.

What's next?

Congratulations! You've just finished the first Behavior Bricks tutorial. You have learned the basics of the editor, and the main concepts: actions, conditions, blackboard, internal nodes, and reusability. If you want to test your newly acquired knowledge, you can, for example, improve the player behavior. Currently, once a position has been selected, it cannot be changed until the player avatar has reached it. It would be great if the player could select a different position using the mouse to overwrite the previous one. Using a priority selector it is easier that it sounds! And it you need some inspiration, you can always have a look at the behavior in the Collection. Finally, remember if you have had any problem following this tutorial, you have available the final scene in the Samples\QuickStartGuide\Done folder of the Behavior Bricks package.