Zum Hauptinhalt springen

Ihr erstes Skript

Ein Skript ist eine TypeScript-Datei, die Sie an ein Objekt hängen. Es läuft im Player, einmal für jedes Objekt, das es benutzt.

Hier ist ein vollständiges — tippen Sie das Objekt an, und es beginnt sich zu drehen:

Scripts/spinner.ts
init((ctx) => {
const props = defineProps({
speed: { type: 'number', default: 1, min: 0, label: 'Speed' },
});

let spinning = false;

ctx.on('on-click', () => {
spinning = !spinning;
});

ctx.tick((dt) => {
if (!spinning) return;

const t = ctx.entity.getComponent(TransformComponent);
t?.update({
rotation: { ...t.$data.rotation, y: t.$data.rotation.y + props.speed * dt },
});
});

return () => {
// optional: aufräumen, wenn dieses Objekt verschwindet
};
});

Vier Dinge passieren hier:

initbekommt ctx, Ihren Griff an die laufende Szene. Alles beginnt hier
definePropsdeklariert Einstellungen, die der Editor im Inspektor zeigt, je Objekt ausgefüllt
ctx.onabonniert einen Auslöser — dieselben, die Ereignisse und Patches benutzen
ctx.tickläuft jedes Bild, mit dt in Sekunden

init und defineProps sind immer verfügbar — Sie importieren sie nie.

Objekte lesen und schreiben

const transform = ctx.entity.getComponent(TransformComponent);

transform.position; // einen Wert lesen
transform.$data; // das Ganze als schlichtes Objekt lesen
transform.update({}); // schreiben

Zwei Fallen, und es sind die beiden, in die alle tappen:

Einen verschachtelten Wert zu schreiben ersetzt ihn vollständig

update({ position: { y: 2 } }) setzt auch x und z auf null — Sie haben eine ganze neue Position übergeben, in der nur y ausgefüllt war.

Spreaden Sie, was Sie behalten wollen:

transform.update({ position: { ...transform.$data.position, y: 2 } });
Ein Material ist eine Liste von Slots

material.update({ color }) tut überhaupt nichts, weil color in einem Slot lebt und nicht ganz oben. Schreiben Sie den Slot:

material.update({
materials: [{ ...material.$data.materials[0], color: '#ff0000' }],
});

Und weisen Sie niemals direkt in $data zu. Es sieht aus, als hätte es funktioniert, und die Änderung wird verworfen.

Einstellungen im Inspektor

defineProps ist das, was ein Skript wiederverwendbar macht: dasselbe Skript an zehn Objekten, an jedem anders konfiguriert, ganz ohne Kopieren und Einfügen.

const props = defineProps({
speed: { type: 'number', default: 1, min: 0, softMax: 10, suffix: 'm/s' },
target: { type: 'entity', label: 'Look at' },
sound: { type: 'resource', resource: 'audio' },
mode: { type: 'select', options: ['chase', 'patrol'], default: 'patrol' },
});
TypDer Editor zeigtIhr Skript bekommt
numberein Feld oder einen Reglereine Zahl
stringein Textfeldeine Zeichenkette
booleaneinen Schaltereinen Boolean
coloreine Farbauswahleine Farbzeichenkette
selectein Dropdowneine Ihrer Optionen
entityeine Objektauswahldas Objekt selbst, einsatzbereit
sceneeine Szenenauswahldas Szenenobjekt
resourceeine Ressourcenauswahleinen Verweis zum Weiterreichen
arrayeine Listeein Array
groupeinen betitelten Blockein verschachteltes Objekt

Wissenswert: label und help fürs Panel, min und max für echte Grenzen, softMax dafür, wo ein Regler endet, ohne größere getippte Werte zu verbieten, showWhen, um ein Feld nur bei einem bestimmten Wert eines anderen zu zeigen, und group, um eine lange Liste ordentlich zu halten.

defineProps muss wörtlich hingeschrieben sein

Der Editor liest Ihre Einstellungen, ohne das Skript auszuführen, er muss sie also direkt sehen — nicht aus Variablen gebaut oder von einer Funktion zurückgegeben.

Werte werden jedes Mal frisch gelesen, props.speed spiegelt also immer, was gerade im Inspektor steht.

Dinge importieren

import { TransformComponent } from '@was/engine';
import helpers from 'Scripts/helpers';

Sie können die Komponentenklassen der Engine importieren und andere Ressourcen über ihren Pfad: Ein anderes Skript gibt Ihnen seine Exporte, ein Patch sein kompiliertes Modul, und alles andere einen Verweis, den Sie an ctx.spawn oder ctx.audio.play weiterreichen können.

Benennen Sie eine Ressource um oder verschieben sie, aktualisieren sich diese Importe von selbst.

Was die Sandbox bietet — und was nicht

Skripte laufen von der Seite isoliert, was verhindert, dass ein schweres Skript das Rendern blockiert. Deshalb sind nicht verfügbar:

  • window, document, das DOM;
  • fetch, localStorage, Netzwerkzugriff jeder Art;
  • jede Rendering-Bibliothek — Sie ändern die Szene über Komponenten, nicht durch Zeichnen;
  • Browser-Timer — nehmen Sie stattdessen ctx.tick.

Was Sie stattdessen nutzen: Interface-Karten für Oberflächen, ctx.audio für Ton, ctx.store und Globals, um Dinge zu behalten.

Wann Ihr Skript startet und stoppt

Eine Instanz entsteht, wenn ihr Objekt lebt und auf dem Bildschirm ist — nichts darüber deaktiviert, seine Szene aktiv — und wird zerstört, sobald das nicht mehr gilt.

on-launch feuert, sobald sie erzeugt wurde, Sie verpassen es also nie. Die Funktion, die Sie aus init zurückgeben, ist Ihr Aufräumen: abmelden, Töne stoppen, Zustand leeren.

Skripte laufen nicht im Editor, und es gibt kein Hot Reload

Der Editor zeichnet Ihre Szene, führt aber keine Logik aus. Bearbeiten, dann Preview öffnen. Ein laufendes Erlebnis übernimmt ein geändertes Skript beim nächsten Neustart.


Weiter: Wo ein Skript wohnt