Die ctx-API
ctx ist Ihr Griff an die laufende Szene — er wird an init übergeben, und alles Folgende hängt
daran.
Er ist bewusst klein. Wenn Sie etwas suchen und hier nicht finden, ist die Antwort mit guter Wahrscheinlichkeit ein eingebauter Schritt statt einer API.
Wo Sie sind
ctx.entity | das Objekt, an dem dieses Skript hängt |
ctx.scene | die Szene, zu der es gehört |
ctx.space | die Welt |
Lebenszyklus und Ereignisse
ctx.tick((dt, t) => {}); // jedes Bild; dt und t in Sekunden
ctx.effect(() => {}); // läuft erneut, wenn sich Gelesenes ändert; kann ein Cleanup zurückgeben
ctx.on(trigger, (payload) => {}); // einen Auslöser abonnieren
ctx.emit(trigger, payload); // einen von diesem Objekt aus auslösen
Auslöser tragen dieselben Namen wie bei Ereignissen und Patches. Die mit nützlicher Information:
| Auslöser | Sie bekommen |
|---|---|
on-keydown · on-keyup | { code, ctrl, shift, alt, meta } |
on-state-active · on-state-inactive | { stateId } |
on-collide | { other } — was Sie getroffen haben |
on-divkit-action | { id, … } — welcher Button |
on-game-control | { state } — Ruhe, Bewegung, Laufen oder Sprung |
on-drag · on-pinch · on-rotate | { dx, dy } · { scale } · { angle } |
on-vps-localized | wo der Besucher sich als befindlich herausstellte |
on-launch trifft in dem Moment ein, in dem Ihre Instanz erzeugt wird — Sie können es also nicht
verpassen, weil Sie zu spät gestartet sind.
Objekte finden
ctx.get(id);
ctx.findByName('Door');
ctx.find(LightComponent); // das erste Objekt mit diesen Komponenten
ctx.query(LightComponent, TagsComponent); // alle davon
ctx.all();
Erzeugen und entfernen
ctx.create({ name: 'Bullet', parent, components: [] });
ctx.spawn(props.bulletModel, { parent });
ctx.destroy(entity);
spawn ist das Bequeme: Reichen Sie ihm eine Ressource, und es setzt die passenden Komponenten
zusammen. Ein Modell wird ein Modellobjekt, ein Bild eine texturierte Fläche, ein Klang eine
Audioquelle.
Eingebautes Verhalten ausführen
ctx.step('play_animation', { presetId }, { targets: [enemy] });
ctx.startTransition({ durationMs: 400, easing: 'ease-out' }, () => {
// hier gemachte Änderungen laufen weich statt zu springen
});
ctx.step führt jeden der eingebauten Schritte aus — dieselben,
die Ihre Ereignisse benutzen. Animation, State-Wechsel, Szenenübergänge und Transitionen sind einen
Aufruf entfernt, Sie müssen sie also fast nie nachbauen.
Navigation
ctx.openScene(sceneOrId);
await ctx.openSpace(spaceRefOrId);
ctx.scenes();
Eingaben
Zwei Ebenen für zwei verschiedene Aufgaben.
Benannte Aktionen lesen die Tastenbelegungen des Projekts, sodass ein Besucher, der seine Tasten umlegt, respektiert wird:
ctx.input.pressed('jump');
ctx.input.justPressed('fire');
ctx.input.axis('moveX');
Rohe Tasten lesen die Tastatur direkt. Ein Druck dauert genau ein Bild, lesen Sie sie also
innerhalb von ctx.tick:
ctx.keyboard.down('KeyW');
ctx.keyboard.press('Space');
ctx.keyboard.press('ArrowLeft', { every: 200 }); // Auto-Wiederholung, in ms
ctx.keyboard.axis('KeyA', 'KeyD'); // -1, 0 oder 1
Eine benannte Belegung ignoriert Modifikatoren, die sie nicht nennt — mit gehaltener Shift zu sprinten darf „vorwärts" nicht abbrechen. Ein Tasten-Auslöser ist das Gegenteil: Ein Modifikator, den Sie nicht angehakt haben, heißt „darf nicht gehalten sein".
Den Fensterfokus zu verlieren löst gehaltene Tasten, es bleibt also nichts hängen.
Kamera
ctx.camera.entity(); // das aktive Kameraobjekt
ctx.camera.setActive(target); // Kamera wechseln; null stellt die Voreinstellung wieder her
ctx.camera.pose(); // { position, rotation, forward } in Weltkoordinaten
Der Transform der Kamera ist in jedem Steuermodus die Quelle der Wahrheit: Schreiben Sie hinein, um die Kamera zu bewegen, lesen Sie ihn, um zu sehen, wohin die Steuerung sie gesetzt hat. In den Modi Orbit und First-Person gehört die Ausrichtung der Steuerung, eine von Ihnen geschriebene Rotation wird also überschrieben — die Position wird respektiert.
Raycasting
await ctx.raycast(); // aus der Kameramitte
await ctx.raycast({ screen: { x: 0.5, y: 0 }, all: true }); // ein Bildschirmpunkt, jeder Treffer
await ctx.raycast({ origin, direction }); // ein beliebiger Strahl
await ctx.raycast({ from: entity }); // von einem Objekt aus, entlang seiner Vorwärtsachse
Jeder Treffer nennt Ihnen das Objekt, die Entfernung sowie Punkt und Flächennormale in Weltkoordinaten — nach Nähe sortiert, ein Treffer pro Objekt.
Der Strahl wird auf der Renderseite gegen echte Geometrie geworfen, die Antwort kommt also im nächsten Bild. Unsichtbare Objekte und als zu ignorieren markierte (Fadenkreuze, Gizmos) werden übersprungen, also antwortet stattdessen, was dahinterliegt, statt dass der Strahl einen Fehlschlag meldet.
Physik
ctx.physics.applyImpulse(target, { x: 0, y: 5, z: 0 });
ctx.physics.applyForce(target, vec, point);
ctx.physics.setVelocity(target, vec);
ctx.physics.teleport(target, position, { rotation, keepVelocity });
ctx.physics.setGravity(vec);
ctx.physics.getSpeed(target);
ctx.physics.isSleeping(target);
await ctx.physics.raycast(from, to, { skip: [ctx.entity] });
Die Physik besitzt seine Position. Nehmen Sie teleport zum Platzieren und Impulse oder Kräfte zum
Bewegen.
Und übergeben Sie skip, wenn Sie einen Strahl aus dem Inneren des eigenen Körpers abfeuern, sonst
treffen Sie jedes Mal sich selbst.
Messwerte stammen aus dem zuletzt synchronisierten Zustand und hinken etwa ein Bild hinterher — gut für „bewege ich mich?", falsch für exakte Momentanrechnungen.
Audio
ctx.audio.play(props.hitSound, { at: enemy, volume: 0.6, positional: true });
Jeder Aufruf startet einen eigenständigen Ton — genau das, was Schritte, Einschläge und Schüsse brauchen. Die Audio-Komponente ist eine einzelne Stimme und schneidet sich selbst ab; nehmen Sie sie nicht für Effekte.
Dinge behalten
Drei Speicher, unterschiedlich weit reichend:
// 1. die eigenen Werte dieses Skripts
const store = ctx.store('game', { score: { type: 'number', default: 0 } });
store.set('score', (v) => v + 1);
store.subscribe('score', (v) => {});
// 2. Globals — geteilt mit jedem Skript, Patch und Ereignis
ctx.setGlobal('level', 3);
ctx.getGlobal('level');
ctx.subscribeGlobal('level', (v) => {});
// 3. Nachrichten zwischen Skripten
ctx.postMessage('enemy-died', { id });
ctx.handleMessage('enemy-died', ({ id }) => {});
| Überlebt einen Szenenwechsel | Überlebt den Wechsel zwischen Spaces | Überlebt ein Neuladen | |
|---|---|---|---|
store | ja | nein | nein |
| Globals | ja | ja | nein |
| Space-bezogene Globals | ja | nein — bewusst isoliert | nein |
Keines der drei wird zwischen Besuchen gespeichert. Muss etwas bestehen bleiben, schicken Sie es selbst irgendwohin, solange Sie noch eine Verbindung haben.
Oberfläche
const ui = ctx.getDivKit(entity);
ui.get('score');
ui.set('score', (v) => v + 1);
ui.subscribe('lives', (v) => {});
ui.onAction('restart', () => {});
Siehe Interface-Karten.
Weiter: Was Sie wirklich bauen werden — vollständige Skripte zum Kopieren.