Skip to content

Visual scripting ​

Flow is Opal's visual scripting: connect nodes on a canvas to define what happens when events fire. Reach for it whenever an object should react to input, collisions, or timers, or a scene needs startup and win/lose rules. No code is required for most games — it works like Unreal Blueprints — though Expression and Script nodes are there when you need more power.

There are two scopes:

ScopeWorkspace tabRuns on
Object FlowObject FlowOne object — its taps, drags, collisions, timers, …
Scene FlowScene FlowThe 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.


Understanding Flow ​

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 colorCarries
WhiteExecution — "run this next"
ColoredData — 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.


Example: your first graph ​

Play a sound when the player taps an object:

  1. Select the object in Arrange
  2. Switch to Object Flow
  3. Click + Event (or double-click the canvas) and pick On Tap
  4. From the catalog, add Play Audio
  5. Connect the white execution pin: On Tap → Play Audio
  6. In the Flow inspector, choose a sound asset for the node
  7. Press Play and tap the object

Try it live: the coin collector is the same idea — one On Tap rule drives the counter. Published builds and play embeds use that graph; the playable below is a fuller demo of the system, not a 1:1 of the six-node example.

Try Opal's Flow — playable demo (fuller than the single-rule example)

The Text button

Every graph has a Text toggle in the flow toolbar (next to Tidy). Hit it to see the same rule as readable text — useful for copying, diffing, or pasting into a chat. See Flow as text.


Event categories (common) ​

Input events ​

EventFires when
On TapQuick press-and-release on the object
On Double TapTwo taps in quick succession
On Long PressPress held past a threshold
On Drag Start / Drag / Drag EndPointer drag gestures
On Pointer Enter / LeaveHover 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 ​

EventFires when
On Scene StartScene begins (Scene Flow)
On CreatedObject is spawned or scene loads
On DestroyedObject 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.

Every Flow timing node — Wait, Cooldown, a timed Say, a Move To with a duration — runs on the game clock, the same clock physics, motion, VFX, and OpalScript's Time.Wait use. A paused game holds every pending Wait; a scene change or Stop drops them. Wait 0 resumes on the next frame.


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:

text
=self.hp + params.amount
=Math.min(self.hp, 5)
=get("health")?.hp

Available context in Flow = expressions (varies by node):

SymbolMeaning
selfOften the component fields for a component action/condition; otherwise may be null
thingThe resolved scene object for the node
paramsParameter / event payload bag
event / source / targetActive event and actors
get("health")Sibling component fields on thing
MathStandard Math object

For GameState and object variables, write with Add Counter / Set Value and read with Get Value (or =GameState.score in a Flow expression). HUD labels use the UI Inspector Data bindings card — see UI and HUD and Variables and GameState.

Expressions are optional — literal values and data wires work fine for simple graphs.


Variables ​

Declare project GameState in the Variables dock (Variables in the bottom shelf). Types: Number, True / False, Text, List, Object. Each GameState field has a lifetime (Session, Saved, or Scene). Object variables live on the Inspector Variables card — same types, no lifetime.

The full reference — lifetimes, list indexes, object references, UI vs Flow — is Variables and GameState.

In graphs, write with Add Counter / Set Value (and the list or object nodes). Read with Get Value, or =GameState.score in a Flow expression. HUD labels use Data bindings → GameState.


Branching and conditions ​

Use If / Condition nodes (or component condition nodes like health.isAlive) to split the white execution wire:

text
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:

ScopeReaches
SceneListeners in the current scene
LocalSame object hierarchy
GlobalProject/session listeners
TargetedOne specific object or group

Custom events decouple systems — e.g. a button emits GameOver and Scene Flow handles the rest.


Using Flow: testing while you build ​

  • 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. If a node never flashes, execution never reached it — check the previous white wire.
  • Problems panel — live validation: missing asset, broken wire, duplicate scene name
  • 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. Full debug recipes: Testing and debugging.


Tips ​

  1. One event, one rule — easier to read than giant graphs; duplicate rules when needed
  2. Name your rules — the flow list gets long on complex objects
  3. Component nodes — prefer health.damage over manual variable math when a component already models the behavior
  4. Scene Flow for globals — don't duplicate win/lose logic on every object
  5. Prefabs carry graphs — save a configured object as a prefab; instances inherit its Object Flow

The same events and components are available as typed OpalScript. Tiny behaviors and a complete flyer live on the high-level scripting track.


See also ​