Understanding the Blackboard System and SharedVariables in Ceres

The Blackboard system in Ceres is a central, graph-wide storage mechanism that holds SharedVariables, enabling data exchange between any node in a graph and across multiple graphs through a global blackboard.

The akikurisu/ceres repository implements a powerful data-sharing architecture for visual scripting in Unity. At its core lies the Blackboard—a centralized storage system that manages SharedVariables, allowing nodes to communicate without direct references. This design pattern decouples node logic while maintaining type safety and runtime flexibility.

What Is the Blackboard System in Ceres?

The Blackboard acts as a container that stores a list of SharedVariable instances. According to the source code in [Runtime/Core/Models/Graph/Variables/Blackboard.cs](https://github.com/akikurisu/ceres/blob/main/Runtime/Core/Models/Graph/Variables/Blackboard.cs), each graph maintains its own blackboard instance during execution.

The system supports two distinct scopes:

  • Local Blackboard: Created per-graph, storing variables that exist only within that specific graph instance
  • Global Blackboard: A static singleton that enables cross-graph communication when variables are marked with IsGlobal = true

How SharedVariables Interact with the Blackboard

The SharedVariable Base Class

All shareable data in Ceres inherits from the abstract SharedVariable class defined in [Runtime/Core/Models/Graph/Variables/SharedVariable.cs](https://github.com/akikurisu/ceres/blob/main/Runtime/Core/Models/Graph/Variables/SharedVariable.cs). Concrete implementations include SharedInt, SharedFloat, SharedObject, and others.

Each variable maintains critical metadata:

  • IsShared: Determines if the variable participates in the blackboard system
  • IsGlobal: Controls whether the variable syncs to the global blackboard
  • IsExposed: Indicates visibility in the Unity editor inspector

Linking Variables to the Blackboard

During graph initialization, nodes register their shared variables with the blackboard. In [Runtime/Core/Models/Graph/CeresGraph.cs](https://github.com/akikurisu/ceres/blob/main/Runtime/Core/Models/Graph/CeresGraph.cs), the InitVariables method (lines 58-71) iterates through all nodes and invokes variable.LinkToSource(graph.Blackboard).

This registration process adds the variable to the blackboard's internal SharedVariables list, making it accessible via the GetSharedVariable extension method defined in [SharedVariableExtension.cs](https://github.com/akikurisu/ceres/blob/main/Runtime/Core/Models/Graph/Variables/SharedVariableExtension.cs).

Global Variable Scope

After graph compilation, the system invokes Blackboard.LinkToGlobal() (lines 152-169 in Blackboard.cs). This method connects the graph's local blackboard to the static global instance. Any variable marked IsGlobal = true becomes available to all other graphs in the application through the same GetSharedVariable API.

Runtime Workflow: From Registration to Retrieval

The Blackboard system operates through a precise lifecycle:

  1. Graph Construction: When CeresGraphCompiler.Compile executes, it instantiates the graph and triggers InitVariables

  2. Variable Registration: Each node calls LinkToSource, binding its SharedVariable instances to the graph's blackboard

  3. Variable Lookup: At runtime, nodes query values using:

    var healthVar = executionContext.Graph.Blackboard.GetSharedVariable("PlayerHealth") as SharedInt;
  4. Global Synchronization: Post-compilation, LinkToGlobal promotes global variables to the singleton blackboard, enabling cross-graph communication

Practical Code Examples

Setting a Shared Variable in a Property Node

The following excerpt from [PropertyNode_SetSharedVariableTValue.cs](https://github.com/akikurisu/ceres/blob/main/Runtime/Flow/Models/Nodes/Properties/PropertyNode_SetSharedVariableTValue.cs) demonstrates how nodes write to the blackboard:

protected override UniTask Execute(ExecutionContext executionContext)
{
    // Retrieve the variable from the graph's blackboard
    if (executionContext.Graph.Blackboard.GetSharedVariable(propertyName) is SharedInt variable)
    {
        // Write the incoming value
        variable.Value = inputValue.Value;
    }
    executionContext.SetNext(exec.GetT<ExecutableNode>());
    return UniTask.CompletedTask;
}

Getting a Variable from a Custom Script

External systems can access the blackboard directly:

public class HealthManager : MonoBehaviour
{
    void Update()
    {
        // Assume a running Ceres graph is attached to this GameObject
        var graph = GetComponent<CeresGraphComponent>().Graph;
        var healthVar = graph.Blackboard.GetSharedVariable("PlayerHealth") as SharedInt;
        if (healthVar != null)
        {
            Debug.Log($"Current health: {healthVar.Value}");
        }
    }
}

Declaring a Global Variable in a Node

To share data across multiple graphs, declare a global variable:

[SharedVariable("Score", IsGlobal = true, IsExposed = true)]
public class ScoreVariable : SharedInt { }

When the graph compiles, this variable automatically synchronizes with the global blackboard, making it accessible from any other graph in the application.

Key Source Files and Architecture

File Role Direct Link
Blackboard.cs Core storage, global linking, creation helpers View
SharedVariable.cs Abstract base for typed variables, serialization, observation View
SharedVariableExtension.cs Extension methods for look-up (GetSharedVariable) and binding View
CeresGraph.cs Graph container, initialization of variables/ports, linking to global blackboard View
PropertyNode_SetSharedVariableTValue.cs Example node that sets a shared variable at runtime View
FlowBlackboard.cs (editor UI) Visual representation of the blackboard in the Unity editor View

Summary

  • The Blackboard in Ceres serves as a centralized storage container for SharedVariables, enabling decoupled communication between nodes in a visual scripting graph.
  • SharedVariable instances register with the blackboard via LinkToSource, making them accessible through the GetSharedVariable extension method.
  • The system supports both local (graph-specific) and global (application-wide) variable scopes, with global variables synchronizing through LinkToGlobal.
  • Variables can be exposed to the Unity inspector, observed for changes, and accessed from external C# scripts, providing flexibility for both visual and code-based workflows.

Frequently Asked Questions

What is the difference between a local and global SharedVariable in Ceres?

A local SharedVariable exists only within its specific graph's blackboard and cannot be accessed by other graphs. A global SharedVariable, marked with IsGlobal = true, synchronizes with the static global blackboard via Blackboard.LinkToGlobal(), making it readable and writable from any graph in the application.

How do nodes access SharedVariables at runtime?

Nodes access SharedVariables through the graph's blackboard using the GetSharedVariable extension method defined in SharedVariableExtension.cs. For example: executionContext.Graph.Blackboard.GetSharedVariable("Health") as SharedInt. This retrieves the typed variable instance, allowing nodes to read or write the Value property.

Can SharedVariables be observed for changes?

Yes, the SharedVariable base class in SharedVariable.cs provides an Observe() API that enables reactive patterns. This allows systems to subscribe to value changes without polling, making it suitable for event-driven architectures within Ceres graphs.

Where is the Blackboard system implemented in the Ceres source code?

The core implementation resides in Runtime/Core/Models/Graph/Variables/Blackboard.cs for storage and global linking, Runtime/Core/Models/Graph/Variables/SharedVariable.cs for the variable base class, and Runtime/Core/Models/Graph/CeresGraph.cs for initialization logic. Extension methods for variable lookup are found in SharedVariableExtension.cs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →