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 →