Subtrees

Subtrees store the behavior tree data within a ScriptableObject allowing you to swap out behaviors on the same GameObject. Subtrees can also be nested within the behavior tree using the Subtree Reference task.

Subtree Reference Task

The Subtree Reference task allows you to specify one or more subtrees that should be loaded when the graph is initialized. By default the Subtree Reference task will load all of the subtrees specified within theĀ Subtrees field. You can also achieve more dynamic functionality by either inheriting the SubtreeReference class or implementing the ISubtreeReference interface. Below is an example of a dynamic Subtree Reference task that will load a random subtree:

using Opsive.BehaviorDesigner.Runtime.Tasks.Actions;
using Opsive.BehaviorDesigner.Runtime;
using Opsive.GraphDesigner.Runtime;
using UnityEngine;

public class RandomSubtreeReference : SubtreeReference
{
    private Subtree[] m_Selection;

    public override Subtree[] Subtrees => m_Selection != null ? m_Selection : m_Subtrees;

    /// <summary>
    /// Performs any runtime operations to evaluate the array of subtrees that should be returned.
    /// </summary>
    public override void EvaluateSubtrees()
    {
        if (m_Subtrees == null || m_Subtrees.Length == 0) {
            return;
        }

        m_Selection = new Subtree[] { m_Subtrees[Random.Range(0, m_Subtrees.Length)] };
    }
}

Before the subtree is retrieved the EvaluateSubtrees method is invoked allowing for a new subtree to be selected. The Subtrees property is then called to retrieve the subtree that should be loaded. In the example above the m_Selection array is populated when EvaluateSubtrees is called and then that reference is returned when the Subtrees property is called.

Variable Overrides

When a subtree is assigned to the Subgraph field of the Behavior Tree component the graph variables added to the subtree will automatically be added to your Behavior Tree component. This allows you to override the subtree variable with a value local to that graph. The variable name and type are not editable because it needs to match the same variable on the subtree.

Variables can also be overridden using the Subtree Reference task. Within the Subtree Reference task inspector you’ll have a similar override interface:

 

 

Reevaluation

When the behavior tree is loaded the Subtree Reference task replaced by the subtree nodes. If you have a Subtree Reference task that should switch out its functionality you can call the BehaviorTree.ReevaluateSubtreeReferences method and all Subtree References will have their EvaluateSubtrees method called and subsequently the Subtrees property in order to load the new subtrees.

See the RuntimeBehavior sample scene for an example of subclassing the SubtreeReference task and reevaluating the tree.
Pooling

Subtrees can be pooled for better performance when switching between many subtrees. When the subtree is instantiated the Deserialize method should be called to deserialize the tree. Pooled subtrees trees can then be assigned to the behavior tree component just like regular subtrees. For example:

using Opsive.BehaviorDesigner.Runtime;
using UnityEngine;

public class SubtreePooler : MonoBehaviour
{
    [Tooltip("A reference to the behavior tree that the subtree should be assigned to.")]
    public BehaviorTree m_BehaviorTree;
    [Tooltip("The subtree that should be pooled.")]
    public Subtree m_Subtree;
    [Tooltip("Specifies how large the pool should be.")]
    public int m_PoolSize = 5;

    private Subtree[] m_SubtreePool;
    private int m_Index;

    /// <summary>
    /// Pools the subtrees.
    /// </summary>
    private void Awake()
    {
        m_SubtreePool = new Subtree[m_PoolSize];
        for (int i = 0; i < m_PoolSize; ++i) {
            m_SubtreePool[i] = Object.Instantiate(m_Subtree);
            m_SubtreePool[i].Deserialize();
        }
    }

    /// <summary>
    /// Assigns a new pooled subtree to the behavior tree.
    /// </summary>
    public void Assign()
    {
        m_BehaviorTree.Subgraph = m_SubtreePool[m_Index];
        m_Index = (m_Index + 1) % m_PoolSize;
    }
}