Script Graphs
A script graph is gameplay logic drawn as nodes and wires instead of typed. It is
the visual sibling of state machines and behavior trees:
the graph is a .esgraph asset, an entity runs it, and the leaves it calls are the
same actions you register once in code. It suits the short, event-driven logic
that is awkward as a state machine — when the game starts, spawn this; when the key
is pressed, branch on that.
The .esgraph file is the runtime definition: nothing compiles it ahead of time,
so the graph the editor runs and the graph the package ships are the same bytes.
Creating and opening a graph
Section titled “Creating and opening a graph”- In the Content Browser, New → New Script Graph. The new asset opens in a
document tab (double-click any
.esgraphto reopen it). - Right-click the canvas to add an event, then wire what it should do. An empty graph says exactly that.
- Select the entity that should run it, add the
ScriptGraphAgentcomponent and set itsgraphfield to the asset.
The asset is referenced by path and preloaded with the scene, so it is ready before
the agent first ticks. Graphs run in Play (and in the exported game), in the
Update schedule beside the state-machine interpreter.
Two kinds of wire
Section titled “Two kinds of wire”| Wire | Carries | Direction |
|---|---|---|
| Exec (the control wire) | “run next” | Pushed: an event lights its entry node and the wire is walked depth-first. |
| Data (number, boolean, string, entity, any) | a value | Pulled: a node reads its inputs by evaluating backwards up the wire when it runs. |
Wires only connect compatible types. An input with nothing wired into it holds a literal you type on the node.
Order is what the picture promises: a Sequence node finishes pin 0’s whole chain before pin 1 starts. A pure node (no exec pins) feeding two pins of the same node is evaluated once per activation, so both pins see the same value; the next activation evaluates it again, which is what lets a While condition become false.
Adding nodes
Section titled “Adding nodes”The Node button in the toolbar (or right-click on the canvas) lists nodes in groups (Events, Flow, Variables, Values, Actions, Queries, Graphs):
| Group | Nodes |
|---|---|
| Events | Start, Every frame, Destroy, On event (listens for a named entity event). Each is an entry point; a graph can have several. |
| Flow | Branch, Sequence (configurable pin count), While, Delay. |
| Variables | Get variable, Set variable. |
| Values | Number, Boolean, String literals. |
| Actions | Nodes with an exec pin that do something: timeline.play / timeline.pause, spriteAnim.play / restart / stop, audio.play / stop, soundEvent.trigger / stop, property.set, entity.despawn, debug.log, scene.load / reload, ui.setVisible, ui.setPage, fsm.fire, blackboard.set — plus every action you register in code. |
| Queries | Value-only nodes (no exec pin): math.* (add, subtract, multiply, divide, min, max, abs, clamp, lerp, random), compare.* (equal, less, greater), logic.* (and, or, not), string.concat, property.get, entity.self, entity.byName, input.down / pressed / axis / pointer — plus every registered condition. |
| Graphs | Every other callable graph in the project, to call it with a Call graph node (empty until a graph declares an input). |
The menu lists the verbs by their registry name; once placed, a node is titled readably (Find entity by name, Key pressed, Set property…) with the name underneath.
An action or condition you register with registerAction / registerCondition — the
same ones state machines and behavior trees use — appears here with the ports its
declaration gives it. The file stores only the verb’s name, so an old graph is
redrawn from the current declaration rather than keeping ports its verb no longer has.
Arrange lays the nodes out left to right by what feeds what; positions are editor-only and ignored at runtime.
Variables
Section titled “Variables”The panel beside the canvas has three sections — Inputs, Outputs and Variables — each with a count and an Add button, searchable, showing how often each entry is read and written (or unused).
- Add under Variables declares a typed variable with a default. It is declared by the asset but valued per entity — two entities running the same graph each keep their own.
- Drag a variable onto the canvas: a plain drop asks Get or Set; hold
Ctrl/⌘to drop a Get,Altto drop a Set, with no question. - Rename or change a variable’s type from its row. Deleting one that nodes still use leaves those nodes on the canvas, without a variable, and asks first.
Callable graphs
Section titled “Callable graphs”A graph becomes callable when you Add an entry under Inputs (an input pin) or Outputs (an output pin). Another graph then runs it with a Call graph node, handing values in and reading results back — the signature lives on the callee, so changing it redraws every caller.
A call runs the callee to completion, synchronously, inside the caller’s step. Each call site keeps its own frame for the callee, the callee spends the caller’s step budget, and nesting is bounded — a graph that reaches itself through a cycle stops rather than overflowing the stack.
Safety limits
Section titled “Safety limits”Every node activation costs a step, and an instance has a budget per tick (4096 by default). A graph that loops forever — a While that never turns false — stops, names the node that exhausted the budget, and stays stopped, instead of freezing the editor. Calls nest at most 16 deep. A reference to a graph, prefab or verb that does not exist is reported once, not every frame.
From code
Section titled “From code”Graphs can also be registered without an asset:
import { registerScriptGraph, ScriptGraphAgent } from 'esengine/logic';
registerScriptGraph('door-open', graphData); // a ScriptGraph object
cmds.spawn().insert(ScriptGraphAgent, { graph: 'door-open' });ScriptGraphAgent.graph takes either a registered name or a .esgraph path. See
AI → Authoring in the editor for the registry the
actions come from.