Anatomy of a script
CompilesReal scripts have a few more parts than hello world. This one is a welcome mat: when a player steps on it, it greets them by name, but no more than once every three seconds.
Hover over a step, or tab to it, to light up the lines it describes.
// welcome_mat.vscript: greets every player who steps on the mat.//// Place it on an entity that also has a box_trigger component. The trigger// tells every script on its entity when something overlaps it.
// Seconds to wait before greeting again.let cooldown_seconds: float = 3.0;
// What to say. {} is replaced by the player's name.let greeting: string = "Welcome, {}!";
// When we last greeted someone, in milliseconds of game time.let mut last_greeting_ms: int = -100000;
// How many greetings so far.let mut greeted: int = 0;
// The trigger calls this when something overlaps it.event export fn trigger_overlapped(other, unused) { if !other { return; } if !cooled_down() { return; }
let player: Entity = find_player_entity($component.get_entity(other)); if !player { return; }
let user: User = $entity.get_owning_user_from_entity(player); $debug.print_line(greeting, [$user.get_name(user, false)], false);
greeted = greeted + 1; last_greeting_ms = $time.get_gameplay_time_ms();}
// True once enough time has passed since the last greeting.fn cooled_down() -> bool { let waited: int = $time.get_gameplay_time_ms() - last_greeting_ms; return waited >= $time.seconds_to_ms(cooldown_seconds);}
// The player entity that `touched` belongs to, or null if it is not a player.fn find_player_entity(touched: Entity) -> Entity { let player: Component = $entity.get_hierarchy_singleton("catch_player.vscript", touched); if !player { return null; } return $component.get_entity(player);}- What it is and how to use itlines 1-4
A comment at the top saying what the script does and what it needs. This one needs a
box_triggeron the same entity; that’s the component that notices players walking in. - Settings: constantslines 6-10
Lines that start with
letat the top of a script are fieldsfield: A variable declared at the top of a script. Every component made from the script has its own copy.. A plainletis a constantconstant: A field declared with plain let. The script cannot change it while running, but a level can set a different value for each placed component in the editor.: the script never changes it, but whoever places the mat in a level can set their own value, such as a longer cooldown or a different greeting. The part after:is the type:floatis a number with a fractional part,stringis text. - State: mutableslines 12-16
let mutmakes a mutablemutable: A field declared with let mut. The script's running state: it changes while the game runs. field, one the script changes as it runs. These remember things between events: when we last greeted someone, and how many greetings so far.intis a whole number. - The eventlines 18-19
The box trigger calls
trigger_overlappedwhen something overlaps it, handing over two values. We name themother(the component that walked in) andunused(we don’t need it). - Leave earlylines 20-25
Guard clauses.
!means not: if there is noother, or the cooldown hasn’t passed,return: stop here and do nothing. Leaving early keeps the rest of the function about the normal case. - Is it a player?lines 27-30
Balls overlap triggers too. We ask our own helper function whether the thing that overlapped belongs to a player; if not, we stop.
- Greet themlines 32-33
Find the useruser: A person playing, or a bot. Players, the balls they hold and the things they spawn are owned by a user. who owns that player, read their name, and print the greeting with the name filled into
{}. - Remember itlines 35-36
Update the state: one more greeting, and the time it happened.
=stores a new value in a mutable. - A helper that answers a questionlines 39-43
A plain
fnis a helper only this script calls.-> boolsays it hands back abool(true or false).returngives the answer: has at leastcooldown_secondspassed? - A helper that finds somethinglines 45-52
Takes an
Entity, looks for thecatch_playerscript anywhere in that entity’s hierarchy, and returns the player’s entity, ornull(nothing) when it isn’t a player.
The shape every script shares
Section titled “The shape every script shares”Almost every script you’ll read or write has the same four layers, top to bottom:
| Layer | What it is | Written as |
|---|---|---|
| Parent | Optional. The script this one builds on. | super "path" (Inheritance) |
| Fields | Settings and state, one copy per component. | let and let mut |
| Events | What the engine (or other scripts) call. | event export fn |
| Helpers | Your own functions, to keep events short. | fn |
Put fields before any function. After that, order functions however reads best; the language doesn’t care.
For programmers
Helpers are statically bound; virtual/override exist for inheritance. Parameters without a type (other,
unused) take any value (var). Locals and fields are strongly typed at compile time where annotated, and
$entity.get_hierarchy_singleton returns Component, which is nullable, as every engine handle is. There are
no exceptions: natives signal failure by returning null/0/false, so check results.