Components
A component is a plain Zig struct that opts into editor discovery. There is no base class to inherit and no registration boilerplate — a single marker is enough.
Marking a struct as a component
pub const Health = struct {
pub const is_component = true;
max: f32 = 100.0,
current: f32 = 100.0,
};
pub const is_component = true;marks the struct for discovery. Set it tofalseto temporarily opt out without deleting the struct.- The editor parses real Zig source (not a regex), so the marker works regardless of formatting, comments, or conditional compilation.
- The struct must be
puband its name must start with a capital letter. - Component type names must be unique across the project. Duplicates are reported and the second definition is ignored.
User scripts live in your project's assets/ folder. After adding or editing a script, click Refresh in the Asset Browser to re-scan.
Supported field types
Public fields with a default value become editable controls in the Inspector.
| Type | Inspector control |
|---|---|
f32 |
number field |
i32 |
integer field |
bool |
checkbox |
engine.Vec3 |
three-axis vector field |
engine.GameObjectRef |
drop target — drag an object from the Hierarchy |
engine.ComponentRef |
drop target — reference another component |
engine.AssetRef |
drop target — reference an asset |
Fields prefixed with _ (e.g. _timer) are treated as private runtime state and are not shown in the Inspector.
Node lifecycle
Every node in a scene follows a deterministic lifecycle from the moment it is loaded until it is destroyed. Components attached to the node receive lifecycle callbacks at each stage.
Loading and activation order
When a scene is loaded, nodes are processed in depth-first traversal order. For each node, the engine:
- Constructs the node and all its components (zero-initialised).
- Calls
awakeon every component — the component knows it exists and can set up internal state, but should not assume other nodes are ready. - Calls
enableon every component — the component becomes active. If the node starts disabled,enableis deferred until the node is enabled. - Once all nodes are loaded and enabled, calls
starton every component in the same depth-first order — at this point every node in the scene exists and every component has been awakened.
Per-frame update
After start, the engine enters the update loop. Each frame:
- Calls
updateon every enabled component — this is where gameplay logic runs (movement, input, spawning). The update order within a frame is deterministic but unspecified; do not rely on one component updating before another in the same frame.
Deactivation and destruction
disable— called when a node is deactivated (e.g. visibility off, parent disabled), either immediately or at the end of the current frame.destroy— called when a node is removed from the scene. Afterdestroyreturns, the component's memory is reclaimed.
Summary
Construct → awake → enable → start → [update × frames] → disable → destroy
Lifecycle hooks
All hooks are optional — implement only the ones you need.
| Hook | Called when |
|---|---|
awake(self) or awake(self, frame: engine.Frame) |
Object is first loaded — use for self-setup that does not depend on other objects |
enable(self) or enable(self, frame: engine.Frame) |
Object becomes active — good for subscribing to events or starting timers |
start(self) or start(self, frame: engine.Frame) |
Scene starts running — every node in the scene is ready; safe to resolve object references |
update(self, frame: engine.Frame) |
Every frame (preferred) — main gameplay logic |
update(self, time: engine.Time) |
Every frame (legacy) |
disable(self) or disable(self, frame: engine.Frame) |
Object becomes inactive — unsubscribe from events, pause timers |
destroy(self) or destroy(self, frame: engine.Frame) |
Object is destroyed — final cleanup, release resources |
The engine.Frame context bundles everything a script needs: frame.time, frame.input, frame.transform, frame.objects, and frame.services. See the Input System page for a full field reference.
engine.Time
| Field | Type | Meaning |
|---|---|---|
.delta |
f32 |
seconds since the last frame |
.elapsed |
f32 |
total seconds since the scene started |
.frame |
u64 |
frame counter |
Built-in components
Built-in components ship with the engine and require no script. Add them via Add Component ▾ in the Inspector. Authoring your own built-in is a contributor task — see Building from Source.
Next steps
- Follow the Tutorial for a hands-on walkthrough.
- See the CLI Reference to build and package without the editor.