Visual scripting
What It Is
HorizonCode is Horizon Engine's visual scripting system — a Blueprint-style node graph you author in the editor, no text required. It is the same idea as the material editor, pointed at gameplay logic instead of shaders: you wire nodes together and the engine runs the graph. Every graph in the project — widget logic, level scripts, the GameInstance, standalone classes — runs on one central runtime, so graphs share state and events and can reference one another.
Mechanics
How a Graph Works
A graph is built from nodes connected by two kinds of wire. Execution links (white) define control flow — they run in order from an event. Data links are typed (Float, Bool, Int, String, Vec2, Color, Transform, or an object Reference) and are evaluated on demand when a node needs a value. Nothing runs until an event fires from the host — a button click, the frame tick, a level loading, an input action.
The canvas is the same one the material editor uses: right-click (or drag a wire into empty space) for the searchable add-menu, pan with the right mouse button, box-select with the left, zoom with the wheel. Bool/Int/Float/String inputs accept inline values directly on unwired pins.
Where graphs live
The Four Hosts
| Host | Lifetime · events |
|---|---|
| Game Instance | One app-wide class that outlives scene changes —
OnInit (before anything loads),
OnShutdown,
OnWindowFocusChanged. Reachable from
anywhere via Get Game Instance. Open it
under View ▸ Game Instance. |
| Level Script | One graph per scene, saved inside the scene —
OnLevelLoaded,
OnLevelUnloaded. Open it under
View ▸ Level Script. |
| Widget graph | Each UI Widget's logic — Construct,
Destruct, Tick and one
event per element interaction.
UI → |
| HorizonCode Class | A standalone, reusable class asset —
Construct, Destruct plus
the custom events it declares. Instantiated with
Create Object. |
Structure
Variables & Functions
- Variables — named, typed, persistent state (single values or arrays), listed on the left of the graph editor with Get/Set nodes. Private to their graph unless marked public; each running instance gets its own copy, seeded from the declared defaults.
-
Functions — reusable sub-graphs with typed
inputs and outputs and an access modifier: public
functions can be called across a class boundary — from another
graph, or from Lua/Python via
callWidgetFunction; private ones stay internal. - Events — the entry points the host raises; a graph can also declare its own and emit them to listeners.
Composition
Talking Between Graphs
Because there is a single runtime, a graph can hold a reference to another running class and delegate to it, event-dispatcher style:
Bind Event— subscribe to another class's event through a reference; when it fires, your matching event runs.Emit Event— broadcast an event to everyone bound to this class.Call Function (Ref)— call a public function on a referenced instance;Get/Set (Ref)reads or writes its public variables.Get Game Instance/Get Self— the references you feed into the nodes above;Is Validguards references that may have been destroyed.
Toolbox
The Node Palette
The searchable palette groups nodes by category: Events, Flow (Branch, Sequence, For Each, Delay, Do Once, Flip Flop), Variables, Literals, Math, Logic, Strings, Arrays, Widgets, Objects & References, Debug, and the single generic Engine Call node that reaches every engine subsystem — entity, transform, physics, camera, environment, audio, scene streaming, save games and more (19 groups, ~220 functions; the Environment group alone exposes every sky and weather field as a get/set pair). Widget graphs add Get/Set Property for every element property.
Full reference: every built-in node and the complete Engine Call catalogue live on the HorizonCode Node Reference page.
Input-driven
Player Classes
HorizonCode projects can create Player
Controller and Player Character classes
(Content Browser ▸ HorizonCode Player). When play
begins, the engine spawns one instance of each and feeds it the
project's input assets: every
mapped action arrives as
Input.<Action>.Pressed /
.Released / .Axis events, alongside
BeginPlay and a per-frame Tick — the
natural home for movement and camera logic.
Ship native
The C++ Compiler
HorizonCode is always interpreted in the editor
and in play-in-editor — that keeps iteration instant. When you
export with the Compile HorizonCode profile
toggle on, an export-time compiler walks every graph in the
project and translates each one into a native C++ class, built
with the host toolchain into
HorizonCodeGen.dylib / .dll /
.so and shipped beside the game.
Hybrid at runtime — nothing ever breaks
A graph that fails validation isn't a build error — it simply
ships interpreted, the reason is logged in the
per-class report (_hcgen/hc_report.txt), and the
rest of the export is unaffected. At runtime the game consults a
compiled-class table first for every class it spawns; a hit runs
native code, a miss (fallback, missing library, or an engine
version mismatch) runs that one graph interpreted, exactly as
the editor does. Compiled and interpreted instances live in the
same runtime with the same ids, so references, event binding and
cross-class calls work identically on both sides.
Proven identical
Behavioural parity is tested, not just intended: a contract suite runs a library of fixture graphs both interpreted and compiled and asserts equal traces — same call order, same arguments, same results, same final variable state. Event cascades are bounded identically on both sides (dispatch recursion depth 32, 256 fires per cascade) so runaway Bind/Emit cycles can't hang either mode.
Checking your graph
You don't have to export to find out whether a graph compiles — every graph editor has a Compile button that runs the same translation and either reports clean or highlights the offending node with a red halo.
Note: Compile HorizonCode requires a C++ toolchain on the exporting machine (the editor can install one — see Preferences). It defaults on for Shipping profiles and off for Development, so day-to-day iteration never pays a build cost.