ActionGraph is s&box’s visual scripting system. Instead of writing code, you connect nodes in a graph to define behavior. Each node represents an action or expression, and links between nodes carry values or signals that control execution order.
ActionGraphs live alongside your C# code — you can trigger them from component properties, call them programmatically, and extend them with custom nodes written in C#.
Getting started
Add an Actions Invoker component
Select a GameObject in the scene, click Add Component, and find Actions Invoker under the Actions category. This is the quickest way to attach an ActionGraph to any object.You can also find action properties on built-in components such as Colliders when Is Trigger is enabled.
Create a new graph
Each action starts as an Empty Action. Click any empty action to create a new ActionGraph and open it in the editor.
Add nodes
Right-click in empty space to open the node creation menu. Drag a link from an output socket to get a filtered list of nodes that match that value type.
Connect nodes and run
Link nodes by dragging from an output socket to an input socket. The root node’s signal output is the entry point — execution follows white signal links from left to right.
Node types
Root node
Every graph has exactly one root node. It cannot be deleted. When the graph runs, a signal fires from its output signal socket. If your graph accepts parameters, the root node exposes value sockets for each one.
Expression nodes
Expression nodes (shown in green) perform calculations without side effects. They have no signal sockets and evaluate on demand when another node reads their output.
Action nodes
Action nodes (shown in blue) have white arrow-shaped signal sockets. They run when they receive an input signal and fire an output signal when done. Control flow nodes like If, While, For Each, and For Range have additional output sockets that fire on specific conditions.
Links, variables, and constants
Links connect an output to an input. Signal links (white, arrow-shaped) control execution order. Value links carry typed data between nodes.
Graphs cannot contain cycles — you cannot follow links from outputs back to the same node. Use control flow nodes like While and For Each to loop instead.
Variables let you store and reuse values across nodes. Create one by dragging a link and selecting Add Variable, then name it. Get and set variable nodes appear under the Variables category in the right-click menu. You can also right-click an input socket directly to insert a variable value.
Constants are hard-coded values set directly in the Properties panel when a node is selected. They are useful for fixed inputs like ranges and durations.
If links are getting messy, use variables to clean up long-distance connections, or shift-click on an existing link to insert a reroute node.
Using ActionGraph from C#
You can expose ActionGraph properties directly on your components so designers can wire up behavior without touching code.
Declaring an ActionGraph property
public class ExampleComponent : Component
{
[Property]
public Action ExampleAction { get; set; }
protected override void OnUpdate()
{
if ( Input.Down( "attack1" ) )
{
ExampleAction?.Invoke();
}
}
}
Any property with a delegate type appears in the inspector as an ActionGraph slot. Use Action for a graph that takes no parameters and returns nothing. Click + in the inspector to add one or more callbacks.
Use [SingleAction] if you only want one callback and prefer the inline display:
[Property, SingleAction]
public Action ExampleAction { get; set; }
Passing parameters
Use a generic Action<> to pass values into the graph:
[Property]
public Action<int, string, GameObject> ExampleAction { get; set; }
The root node in the graph will have a socket for each parameter in order. To give the parameters meaningful names, define a custom delegate type:
public delegate void ExampleDelegate( int someNumber, string someText, GameObject someObject );
[Property]
public ExampleDelegate ExampleAction { get; set; }
Running an ActionGraph
Call the property like any other delegate. Use the null-conditional ?.Invoke() pattern to safely handle graphs that haven’t been created yet:
Pass arguments when the delegate has parameters:
ExampleAction?.Invoke( 123, "Hello, World!", GameObject.Parent );
Returning values
Use Func<T> for graphs that return a value:
[Property]
public Func<int> TestExpression { get; set; }
The graph will have an Output node you must connect to return a value. Async nodes (like delays) cannot appear between the root and the output.
Async graphs
To wait for a graph that contains delays to finish, declare the delegate with a Task return type:
[Property]
public Func<Task> ExampleAction { get; set; }
With parameters:
[Property]
public Func<int, string, GameObject, Task> ExampleAction { get; set; }
As a custom delegate:
public delegate Task ExampleDelegate( int someNumber, string someText, GameObject someObject );
[Property]
public ExampleDelegate ExampleAction { get; set; }
Await the result or use ContinueWith outside of async code:
await ExampleAction?.Invoke( 123, "Hello, World!", GameObject.Parent );
ExampleAction?.Invoke( 123, "Hello, World!", GameObject.Parent )
.ContinueWith( task => { } );
Custom nodes
There are two ways to create your own node types.
Action Resources (.action files)
Extract a set of existing nodes into a reusable custom node without writing any C#.
Select the nodes
In the ActionGraph editor, select the nodes you want to package. Do not include the root node.
Create the custom node
Right-click the selection and choose Create Custom Node…. Give it a name and save it to a file.
Use it like any other node
The selected nodes are replaced by a single new node. Double-click it to open and edit the internals. If the original selection had outgoing links, an Output node is created automatically.
C# method nodes
Annotate a static method with [ActionGraphNode] to make it available in the node creation menu:
/// <summary>
/// Increments the value by 1.
/// </summary>
[ActionGraphNode( "example.addone" ), Pure]
[Title( "Add One" ), Group( "Examples" ), Icon( "exposure_plus_1" )]
public static int AddOne( int value )
{
return value + 1;
}
/// <summary>
/// Waits for one second.
/// </summary>
[ActionGraphNode( "example.waitone" )]
[Title( "Wait One" ), Group( "Examples" ), Icon( "hourglass_bottom" )]
public static async Task WaitOne()
{
await Task.Delay( TimeSpan.FromSeconds( 1d ) );
}
Add [Pure] to make the node an expression node (no signal sockets). Omit it for action nodes (with signal sockets). Use [Title], [Group], and [Icon] to control how the node appears in the editor.