Lifecycle and events
CompilesYour script never runs by itself. The engine calls its eventsevent: A function the engine (or another script) calls on your component when something happens: every frame, when it is created, when something overlaps a trigger. as things happen, and each event is a chance to act. Most of the work is choosing which event your code goes in.
type_createdset-up, e.g. switch on networkingcreatedthis component now existsearly_tickbefore gameplay moves thingstickyour gameplay logiclate_tickafter movement is writtendrawvisuals and debug drawingdestroyedclean up what you madeSet up the script: type_created
Section titled “Set up the script: type_created”event export fn type_created() { $net.set_replicated(true, false)}Runs for the script as a whole, before any of its components do anything. A script uses it to configure how the engine treats it, above all its networking (Replication). Configure here; don’t look for entities.
Set up this component: created
Section titled “Set up this component: created”let mut arrival: Entity = null
event export fn created() { arrival = $entity.get_child_named("arrival", true, $entity.get())}Runs once per component, when it has been created. Find the entities and components you’ll need and keep them in fields, so later events don’t search again.
When children arrive: children_changed
Section titled “When children arrive: children_changed”When the component’s entity is part of a level that’s still being spawned, its children may not exist yet in
created. children_changed runs when they come and go. Shipped scripts share their set-up between both:
let mut arrival: Entity = null
event export fn created() { find_parts()}
event export fn children_changed() { if $entity.are_children_pending($entity.get()) { return } find_parts()}
fn find_parts() { arrival = $entity.get_child_named("arrival", true, $entity.get())}Every frame: early_tick, tick, late_tick, draw
Section titled “Every frame: early_tick, tick, late_tick, draw”Every frame, the engine runs every component’s early_tick, then every tick, then every late_tick, then every
draw. A ticktick: One step of the game's simulation. The engine runs one tick per frame and calls your tick events in it. is one step of the simulation.
| Event | Put here |
|---|---|
early_tick |
Work that must happen before gameplay moves anything. |
tick |
Most gameplay logic. |
late_tick |
Anything that must see or overrule where things ended up this frame, like moving a player. |
draw |
Visuals and debug drawing. Not gameplay. |
Only write the frame events you need: a script with a tick does work every frame, even when it has nothing to do.
You can switch an event off and on while the game runs:
$component.set_event_enabled("tick", false, $component.get())Clean up: destroyed
Section titled “Clean up: destroyed”Runs when the component goes away. Undo anything your script set up outside itself: entities it spawned, events it asked for.
Events other things send
Section titled “Events other things send”Plenty of events come from other components rather than the lifecycle:
trigger_overlapped(other, unused)comes from abox_triggeron the same entity when something overlaps it.otheris the component that overlapped.field_replicated(field, previous)runs when a replicated field changes; see Replication.- Any event another script sends with
$component.queue_event.
The Events reference lists every event shipped scripts export.
Events are found by name
Section titled “Events are found by name”To receive the engine’s tick, your function must be event export fn tick, spelled exactly. The engine never
calls a misspelled event, and nothing warns you.
event export fn on_tick() {}event export fn tick() {}For programmers
type_created is per script type (static configuration: $net, $type natives); everything else is
per instance. queue_event defers to the end of the active event or to the end of the frame. The phase ordering
comes from the names and from observed behaviour (movement’s writes land before late_tick); the engine’s own
scheduler isn’t documented beyond that. Gameplay uses rollback ($entity.is_rollback and the print natives’
during_rollforth exist for it), so derive ticks from state and game time rather than counting calls.