Visual scripting
Opal's visual scripting system — Flow — works like Unreal Blueprints. You connect nodes on a canvas to define what happens when events fire. No code required for most games, though you can drop in Expression and Script nodes when you need more power.
There are two scopes:
| Scope | Workspace tab | Runs on |
|---|---|---|
| Object Flow | Object Flow | One object — its taps, drags, collisions, timers, … |
| Scene Flow | Scene Flow | The whole scene — startup, transitions, global rules |
Both use the same node catalog, inspector, and wire types.
Prefer typing to wiring?
Any graph can also be edited as readable text — hit the Text button in the flow toolbar. See Flow as text.
Graph anatomy
Rules
Each graph is a rule: a trigger event plus the nodes wired beneath it. One object (or the scene) can have many rules — for example separate graphs for On Tap and On Scene Start.
Wires
| Wire color | Carries |
|---|---|
| White | Execution — "run this next" |
| Colored | Data — numbers, booleans, text, object references |
Click an output port and drag to a compatible input to connect. Data ports and wires use their value type's color, while execution wires stay white. Click a wire or node and press Delete to remove it.
Node catalog
The catalog on the side groups nodes by category:
- Lifecycle — On Scene Start, On Created, On Destroyed, …
- Input — On Tap, On Drag, On Key Down, …
- Drag & Drop — drop targets, pickup events
- Area / Collision — pointer enter/exit, physics collision events
- State & Variables — set/read GameState and object variables
- Sprite Animation — set Animation State, play motion, set visibility, VFX
- Timers — delay, repeat, stopwatch
- Spawn / Destroy — create prefabs, remove objects
- Components — actions and conditions from attached components (Health, Inventory, …)
When you attach components to an object, that object's catalog gains matching Component Action and Component Condition nodes automatically.
Your first graph
Example: play a sound when the player taps an object.
- Select the object in Arrange
- Switch to Object Flow
- Click + Event (or double-click the canvas) and pick On Tap
- From the catalog, add Play Audio
- Connect the white execution pin: On Tap → Play Audio
- In the Flow inspector, choose a sound asset for the node
- Press Play and tap the object
Event categories (common)
Input events
| Event | Fires when |
|---|---|
| On Tap | Quick press-and-release on the object |
| On Double Tap | Two taps in quick succession |
| On Long Press | Press held past a threshold |
| On Drag Start / Drag / Drag End | Pointer drag gestures |
| On Pointer Enter / Leave | Hover in/out (desktop) |
Objects need an Interactable (or legacy Tappable) component — or an On Tap graph, which adds Interactable — for most pointer events to fire.
Lifecycle events
| Event | Fires when |
|---|---|
| On Scene Start | Scene begins (Scene Flow) |
| On Created | Object is spawned or scene loads |
| On Destroyed | Object is removed |
Drag and drop
Use Drop Zone component or drop / droppedOn flow events for drag-and-drop puzzles, inventory, and card games. The dragged object is the source; the target is where it was dropped.
Timers
On Timer and Start Timer / Stop Timer nodes drive delayed and repeating logic without code.
Scene Flow vs Object Flow
Object Flow graphs are stored on the selected object. The On Tap trigger only fires for taps on that object unless you parameterize a different target in action nodes.
Scene Flow graphs live at scene scope. Use them for:
- Loading UI on On Scene Start
- Go To Scene when the player wins or loses
- Global timers and score tracking
- Listening for Custom Events broadcast from any object
Switch to the Scene Flow tab to author scene rules. The catalog and inspector behave the same way.
Expressions
Many node fields accept expressions — small snippets evaluated at runtime. Prefix a field with = (or use the expression toggle) to switch from a literal value to a formula.
Examples:
=self.hp + params.amount
=Math.min(self.hp, 5)
=get("health")?.hpAvailable context in Flow = expressions (varies by node):
| Symbol | Meaning |
|---|---|
self | Often the component fields for a component action/condition; otherwise may be null |
thing | The resolved scene object for the node |
params | Parameter / event payload bag |
event / source / target | Active event and actors |
get("health") | Sibling component fields on thing |
Math | Standard Math object |
For GameState and object variables, prefer the dedicated Get / Set variable / Add Counter (catalog) nodes rather than inventing a bare GameState.score identifier in a Flow expression — that form is not bound in the Flow evaluator. In UI bindings, project variables are available as self.score or self.GameState.score (see UI and HUD).
Expressions are optional — literal values and data wires work fine for simple graphs.
Variables
GameState (global)
Declare variables in the top bar Variables menu. Types: Number, Bool, Text.
In graphs:
- Set variable / Add Counter (writes a number GameState field)
- Variable at least / comparisons — branch on GameState
- Get / Set Value nodes targeting scene / GameState keys
GameState persists across Go To Scene (same play session) unless you reset it.
Object variables
In Arrange → Inspector → Variables, declare per-object fields. Read and write them with Get/Set value nodes (object scope) or autocomplete in rule fields. At runtime they live on thing.vars.
Branching and conditions
Use If / Condition nodes (or component condition nodes like health.isAlive) to split the white execution wire:
On Tap → health.isAlive?
├─ true → Play Audio
└─ false → Spawn VFX (death poof)Condition nodes output which execution pin to follow.
Custom events
Emit Event broadcasts a named custom event. On Custom Event listens for it. Choose scope:
| Scope | Reaches |
|---|---|
| Scene | Listeners in the current scene |
| Local | Same object hierarchy |
| Global | Project/session listeners |
| Targeted | One specific object or group |
Custom events decouple systems — e.g. a button emits GameOver and Scene Flow handles the rest.
Testing while authoring
- Play in viewport — full runtime test
- Live Flow overlay — while playing, toggle Flow on the canvas overlay to see nodes light up as they run
- Per-rule Test control in the flow UI — fires a synthetic event for that graph without full play (where available)
Ctrl+Z undoes graph edits (node moves and multi-node deletion each collapse into one step). Press A to open the add palette at the canvas center, or double-click the exact place you want to add. Ctrl/Cmd+D duplicates selected nodes inside an open graph.
Tips
- One event, one rule — easier to read than giant graphs; duplicate rules when needed
- Name your rules — the flow list gets long on complex objects
- Component nodes — prefer
health.damageover manual variable math when a component already models the behavior - Scene Flow for globals — don't duplicate win/lose logic on every object
- Prefabs carry graphs — save a configured object as a prefab; instances inherit its Object Flow
Related docs
- Flow as text — edit the same graphs as readable text
- Build your first game — a complete beginner workflow using Object Flow
- Getting started with components — component actions/conditions in graphs
- Object Flow integration — detailed component ↔ flow reference
- Prefabs and spawning — spawn prefabs from Spawn Object nodes
- Audio and VFX — Play Audio, Say, and Spawn VFX patterns
- Testing and debugging — live Flow overlay, Problems, and Graph Explorer
- UI and HUD — buttons, menus, and HUD bindings