Sharing data using Blackboard

To share data between different tasks and states, we employ a feature known as theBlackboard.TheBlackboard serves as a central repository where tasks and states can store and retrieve named variables,allowing for seamless data interchange. Each instance of a behavior tree or a state machine gets its own dedicatedBlackboard. It has the capability to store various data types,including objects and resources.

Using theBlackboard, you can easily share data in your behavior trees, making the tasks in the behavior tree more flexible.

Accessing the Blackboard in a Task

EveryBTTask has access to theBlackboard, providing astraightforward mechanism for data exchange.Here’s an example of how you can interact with theBlackboard in GDScript:

@exportvarspeed_var:StringName=&"speed"func_tick(delta:float)->Status:# Set the value of the "speed" variable:blackboard.set_var(speed_var,200.0)# Get the value of the "speed" variable, with a default value of 100.0 if not found:varspeed:float=blackboard.get_var(speed_var,100.0)# Check if the "speed" variable exists:ifblackboard.has_var(speed_var):# ...

If you are accessing a variable that holds an object instance, and it isexpected that the instance may be null or freed, you can do it like this:

@exportvarobject_var:StringName=&"object"func_tick(delta:float)->Status:# Get object instance stored in the "object" variable.# - Important: Avoid specifying a type for "obj" in GDScript#   to prevent errors when the instance is freed.varobj=blackboard.get_var(object_var)ifis_instance_valid(obj):# ...

It is recommended to suffix variable name properties with_var, like in the example above, which enables theinspector to provide a more convenient property editor for the variable. This editorallows you to select or add the variable to the blackboard plan, and provides awarning icon if the variable does not exist in the blackboard plan.

🛈 Note: The variable doesn’t need to exist when you set it in code.

Editing the Blackboard Plan

The Blackboard Plan, associated with eachBehaviorTreeresource, dictates how theBlackboard initializes for eachnew instance of theBehaviorTree.BlackboardPlan resource stores default values, type information, and data bindingsnecessary forBehaviorTree initialization.

To add, modify, or remove variables from the Blackboard Plan, follow these steps:

  1. Open the LimboAI editor and load the behavior tree you want to edit.

  2. In the editor, click on the small button located inside the tab. This will open theBlackboardPlan in the Inspector.

  3. In the Inspector, click the “Manage…” button to show the blackboard plan editor.

  4. In the blackboard plan editor, you can add, remove, or reorder variables, and modify their data type and hint.

  5. The hint provides additional information about the variable to the Inspector, such as minimum and maximum values for an integer variable. Learn more aboutproperty hints in the official Godot documentation.

  6. You can specify the default values of the variables directly in the Inspector.

Overriding variables in BTPlayer

EachBTPlayer node also has a “Blackboard Plan” property,providing the ability to override values of the BehaviorTree’s blackboard variables.These overrides are specific to the BTPlayer’s sceneand do not impact other scenes using the sameBehaviorTree.To modify these values:

  1. Select the BTPlayer node in the scene tree.

  2. In the Inspector, locate the “Blackboard Plan” property.

  3. Override the desired values to tailor the blackboard variables for the specific scene.

Task parameters

In some cases, it can be beneficial to allow behavior tree tasks to export parametersthat can either bebound to a blackboard variable or specified directly by the user.For this purpose, LimboAI provides special parameter types that begin with “BB”,such asBBInt,BBBool,BBString,BBFloat,BBNode, and more.For a complete list, please refer to theBBParam class reference.

Usage example:

extendsBTAction@exportvarspeed:BBFloatfunc_tick(delta:float)->Status:varcurrent_speed:float=speed.get_value(scene_root,blackboard,0.0)...

Connecting variables in BTs to HSMs: Variable mapping

EachBTState creates a new blackboard scope for itsBehaviorTree instance. Because BehaviorTrees are reusable resources that can runin different contexts or on different agents, they do not automatically seevariables defined in the HSM. At runtime, HSM variables are usually accessibleinside the BT via the parent scope blackboard, but the recommended way to accessthem is throughmapping.

Note

To learn more about scopes, seeBlackboard.

When both the BehaviorTree and the HSM declare variables in their respectiveBlackboardPlan resources, the editor provides aMappingsection in the BlackboardPlan inspector. Mapping is the intended and recommended way toconnect variables between related plans.

Key points about mapping

  • Mapping connects two variables so they behave as a single logical variable at runtime.

  • Linked variables are updated immediately in both scopes — they literally share thesame state in memory (no polling or copying).

  • Mapping is explicit: you decide which variables the BT exposes as inputs and outputs.

  • Linkage is bidirectional — there is no distinction between input and output.

  • Mapping does not create new variables automatically — the variables must alreadyexist in both blackboard plans before they can be linked.

Inspector workflow

  1. Define the required variables in each BlackboardPlan (HSM plan and BT plan, for example).

  2. Select theBTState node in the scene or aBTSubtreeinside a behavior tree.

  3. In the Inspector, select the Blackboard Plan property.

  4. In the BlackboardPlan resource inspector, locate theMapping section.

  5. Create mappings by pairing variables (BTvariableparentvariable).

  6. On startup the mappings are applied, and the variables are automatically linkedfor the running instance.

Programmatic alternative: linking variables in code

You can also link variables directly in code usingBlackboard.link_var:

# Example: programmatically link a BT blackboard variable to an HSM blackboard variable# Assume `hsm` is a LimboHSM instance and `bt_state` is a BTState instancevarhsm_bb:=hsm.get_blackboard()varbt_bb:=bt_state.get_blackboard()# NOTE: variable names do not need to matchbt_bb.link_var("target_pos",hsm_bb,"target_pos")

Best practices

  • Declare all BT dependencies in the BT’s BlackboardPlan instead of relying on parent scopes.

  • UseMapping to explicitly declare which variables should be shared with the HSM.

  • Prefer the inspector-based Mapping workflow for clarity and editor support; uselink_var only when runtime or programmatic setup is required.

  • Avoid writing toblackboard.get_parent() from inside BT tasks unless you havea very specific reason and accept the coupling it introduces.

Advanced topic: Blackboard scopes

TheBlackboard in LimboAI can act as a parent scopefor anotherBlackboard.This means that if a specific variable is not found in the active scope,the system will look in the parentBlackboard to find it.This creates a “blackboard scope chain,” where eachBlackboard can have its own parent scope,and there is no limit to how many blackboards can be in this chain.It’s important to note that theBlackboard doesn’t modify values in the parent scopes.

Scopes are created automatically to prevent naming collisions between contextually separate environments:

  • WithinBTNewScope.

  • UnderBTSubtree decorators.

  • WithLimboState that have a non-empty blackboard plan defined.

  • UnderLimboHSM nodes: A new scope is created at the root level,and eachBTState child also receives its own separate scope.

Sharing data between several agents

The blackboard scope mechanism can also be used for sharing data between several agents.In the following example, we have a group of agents, and we want to share a common target between them:

extendsBTAction@exportvargroup_target_var:StringName=&"group_target"func_tick(delta:float)->Status:ifnotblackboard.has_var(group_target_var):varnew_target:Node=acquire_target()# Set common target shared between agents in a group:blackboard.top().set_var(group_target_var,new_target)# Access common target shared between agents in a group:vartarget:Node=blackboard.get_var(group_target_var)

In this example,blackboard.top() accesses the root scope of theBlackboard chain.We assign that scope to each agent in a group through code:

class_nameAgentGroupextendsNode2D## AgentGroup node: Manages the shared Blackboard for agents in a group.## Children of this node are assumed to be agents that belong to a common group.## This implementation assumes that each agent has a "BTPlayer" node for AI.@exportvarblackboard_plan:BlackboardPlanvarshared_scope:Blackboardfunc_ready()->void:ifblackboard_plan==null:shared_scope=Blackboard.new()else:shared_scope=blackboard_plan.create_blackboard()forchildinget_children():varbt_player:BTPlayer=child.find_child("BTPlayer")ifis_instance_valid(bt_player):bt_player.blackboard.set_parent(shared_scope)

In conclusion, theBlackboard scope chain not onlyprevents naming conflicts that can occur between state machines, behavior trees, and sub-trees,but it can also be used to share data between several agents.