Variables

Type-safe local state storage for nodes using the generic Variable system.

Overview

Variables are the way to store node-local state. Each variable is strongly typed and scoped to a node:

Node MyNode() => Sequence("MyNode", () =>
{
    // Create variables - they are local to this node
    var counter = Variable(() => 0);
    var targetPos = Variable(() => Vector3.zero);
    var isMoving = Variable(() => false);

    OnTick(() =>
    {
        counter.Value++;
        Debug.Log($"Count: {counter.Value}");
    });
});
Node MyNode() => Sequence("MyNode", () =>
{
    // Create variables - they are local to this node
    var counter = Variable(() => 0);
    var targetPos = Variable(() => Vector3.zero);
    var isMoving = Variable(() => false);

    OnTick(() =>
    {
        counter.Value++;
        Debug.Log($"Count: {counter.Value}");
    });
});

Creating Variables

Variables are created with the Variable<T> method:

// Create variable with initial value
var health = Variable(() => 100);

// Type is inferred from the lambda return type
var position = Variable(() => Vector3.zero);

// Works with any type
var items = Variable(() => new List<Item>());
var target = Variable(() => (Transform)null);
// Create variable with initial value
var health = Variable(() => 100);

// Type is inferred from the lambda return type
var position = Variable(() => Vector3.zero);

// Works with any type
var items = Variable(() => new List<Item>());
var target = Variable(() => (Transform)null);

The lambda is called once during node initialization to set the initial value.

Reading and Writing Values

Access variables via the .Value property:

var counter = Variable(() => 0);

// Read the value
int current = counter.Value;
Debug.Log($"Current count: {current}");

// Write the value
counter.Value = 10;

// Modify the value
counter.Value++;
var counter = Variable(() => 0);

// Read the value
int current = counter.Value;
Debug.Log($"Current count: {current}");

// Write the value
counter.Value = 10;

// Modify the value
counter.Value++;

Using Variables as Functions

Variables support implicit conversion to Func<T>, so you can pass them directly to methods that expect a function:

var counter = Variable(() => 0);

void LogValue(Func<int> getValue) => Debug.Log(getValue());

// Pass variable directly - implicit conversion
LogValue(counter);
var counter = Variable(() => 0);

void LogValue(Func<int> getValue) => Debug.Log(getValue());

// Pass variable directly - implicit conversion
LogValue(counter);

The .Fn property provides access to the cached Func<T> delegate if you need to store it separately:

var counter = Variable(() => 0);

// Get the cached function delegate
Func<int> getValue = counter.Fn;
int current = getValue();
var counter = Variable(() => 0);

// Get the cached function delegate
Func<int> getValue = counter.Fn;
int current = getValue();

Ready for more advanced patterns? ClosureBT supports Functional Reactive Programming (FRP) through composable transformation pipelines for complex data flows.

Next: Composite Nodes

Comprehensive overview of all composite nodes: Sequence, Selector, Parallel, Race, and SequenceAlways.

Read about Composite Nodes →