Skip to content

Lifecycle and events

Compiles

Your 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.

Once
type_createdset-up, e.g. switch on networking
createdthis component now exists
Every frame
early_tickbefore gameplay moves things
tickyour gameplay logic
late_tickafter movement is written
drawvisuals and debug drawing
At the end
destroyedclean up what you made
The lifecycle events, in the order the engine sends them. You write only the ones you need.
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.

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 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())

Runs when the component goes away. Undo anything your script set up outside itself: entities it spawned, events it asked for.

Plenty of events come from other components rather than the lifecycle:

  • trigger_overlapped(other, unused) comes from a box_trigger on the same entity when something overlaps it. other is 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.

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.

✗ Never called
event export fn on_tick() {
}
✓ Called every frame
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.