Every scene has its own script, which runs while the scene is shown. Use it to react to buttons and other inputs, change what's on screen, and start chapters. Scripts are written in TypeScript or JavaScript.
This page covers how a scene script is put together and when each part runs. For everything the engine gives you to call, see Coding.
Editing a scene's script
Open the scene in the Scene Editor and click the code button in its title bar to switch to the script. Click it again to go back to the scene.
New scenes start with an empty onStart() and onExit().
- To change the language, select the scene root and set Script language in the inspector's Scene section.
- To use the same script in several scenes, move it into a Shared Script.
How a script is written
A scene script is a list of members, separated by commas. Members can be lifecycle hooks, your own methods and your own values:
// A value, kept on this.retries
retries: 0,
// A lifecycle hook
onStart() {
this.dom.title.textContent = "Welcome";
},
// Your own method
onPlayPressed() {
this.retries++;
SceneManager.SwitchScene("level_1");
},
- Every member ends with a comma, including methods.
- Inside any member,
thisis the scene.this.domholds the scene's nodes, andthis.varsholds its scene variables. - Don't write members as arrow functions (
onPlayPressed: () => {...}), becausethisisn't the scene inside them. - Scene scripts can't use
importorexport. Put shared code in Project Scripts instead.
See Script Types for how each of these is typed.
Lifecycle hooks
| Hook | When it runs |
|---|---|
onEnter() |
The scene has been built and is about to start. this.dom is ready, but the sub-scenes inside it haven't started yet. |
onStart() |
After onEnter(), once every sub-scene inside this one has run its own onEnter() and onStart(). |
onExit() |
The scene is being removed, because the game switched to another scene, the scene was detached, or the scene containing it was removed. |
For a menu scene with a sub-scene card inside it, the hooks run in this order:
menuonEnter()cardonEnter()cardonStart()menuonStart()
When the game leaves menu, its onExit() runs, then card's.
- All three hooks are optional.
- A hook can be
async, but nothing waits for it.onStart()runs as soon asonEnter()reaches its firstawait. - The scene's music and ambient sound, set in its inspector, start before
onEnter(). - The script is only loaded once. Values you keep on
thisare still there the next time the scene is shown, so reset them inonEnter()if you need a fresh start (see Variable Scope).
Calling your methods from the scene
Many inspector fields take the name of a method in the scene's script, which is called when something happens to that node:
| Field | Your method receives |
|---|---|
| Button Action set to Event | Nothing |
| A node's Events list (see Shared Node Properties) | The browser event |
| Slider On Change and On Change End | The slider's value, as a number |
| Checkbox On Change | true or false |
| Text Input On Change | The text |
| Sprite Loop On Event | The event's name, its data and the full event |
// Slider's On Change is "onVolumeChanged"
onVolumeChanged(value: number) {
AudioManager.SetMasterVolume(value / 100);
},
You can add your own arguments in brackets, like onChoose(2) or onChoose("left"). In an Events list they come before the browser event. Everywhere else they come after the value.
If the name doesn't match a method in the script, nothing happens and no error is shown, so check the spelling. Fields with a + button next to them add the method to your script for you.
Calling your methods from a chapter
A tag like [OpenShop(sword, price=10)] in a chapter calls every handler registered for OpenShop when the text reaches it. Register your method in onStart():
onStart() {
Revealer.Register("OpenShop", this.openShop, this);
},
openShop(event: RevealerEvent) {
const item = event.args[0]; // "sword"
const price = Number(event.kwargs.price); // arguments arrive as text
},
Passing this as the last argument makes the scene this inside your method, and removes the handler when the scene exits. If your method returns a promise, the text waits until it finishes. See Coding for the built-in events.
Working with other scenes
Every loaded scene can be reached by name, so one scene can call another's methods:
SceneManager.GetScene("hud")?.showMessage("Saved");
this.attachedSceneslists the sub-scenes inside this scene.- A Scene node with an id is in
this.dom, as the element the sub-scene is drawn into. SceneManager.SwitchScene("level_1")moves to another scene, which runs this scene'sonExit().
Cleaning up
Handlers you register with this as the owner are removed for you when the scene exits. This works for Store, Revealer, InputManager, AudioManager and Logger.
Listeners on the scene's own nodes go away with the scene. Anything else keeps running after the scene has gone, so stop it in onExit():
timer: 0,
onStart() {
this.timer = setInterval(() => this.tick(), 1000);
},
onExit() {
clearInterval(this.timer);
},
tick() {
log("tick");
},
That includes timers, requestAnimationFrame loops, and listeners you add to window or document.