Zum Hauptinhalt springen

Gemeinsame Pakete

Diese Pakete können Sie in Ihrem eigenen Code importieren. Hier sehen Sie, was Sie wo nutzen können und was jedes Paket bietet.

Entitäten sind Objekte

Im Code heißen die Objekte, die Sie im Editor sehen, Entitäten. Es ist dasselbe.

Welche Pakete Sie importieren können, hängt davon ab, wo Ihr Code läuft:

PaketWas es bietetIn einem SkriptIn einem PluginIn einer Komponenten-Erweiterung
@was/ecsEntitäten, Komponenten, die Welt✓✓✓
@was/enginedie Komponentenklassen✓✓✓
@was/signalsreaktive Werte—✓✓
@was/svdtSchemas und Validierung✓✓—
@was/utilskleine Hilfsfunktionen✓✓—
@was/uidie Komponentenbibliothek des Editors—✓✓
@was/iconsdie Icons des Editors—✓✓
reactReact 19—✓✓
Im Skript ctx.effect nutzen

Ein Skript kann @was/signals nicht direkt importieren. Nutzen Sie stattdessen ctx.effect. Es funktioniert genauso und endet, wenn sein Objekt verschwindet. So läuft danach nichts weiter.

@was/signals: reaktive Werte​

Ein Signal ist ein Wert, der weiß, wer ihn liest. Ändert er sich, wird nur aktualisiert, was von ihm abhängt.

import { signal, computed, effect, batch, untracked } from '@was/signals';

const score = signal(0);
const doubled = computed(() => score.value * 2);

const stop = effect(() => {
render(score.value); // läuft nur erneut, wenn sich score ändert
});

batch(() => {
score.value += 1;
score.value += 1; // Effekte laufen einmal, nicht zweimal
});

stop();
FunktionWas sie tut
signal(v)ein Wert, den man beobachten kann; lesen und schreiben über .value
computed(fn)ein abgeleiteter Wert, der nur neu berechnet wird, wenn sich das Gelesene ändert
effect(fn)läuft sofort und erneut, sobald sich das Gelesene ändert; gibt eine Stopp-Funktion zurück
batch(fn)fasst Schreibvorgänge zusammen, sodass Beobachter nur einmal am Ende laufen
untracked(fn)liest einen Wert, ohne von ihm abzuhängen
ref(obj)macht ein ganzes Objekt reaktiv, bis in jeden verschachtelten Wert
snapshot(obj)eine einfache, nicht reaktive Kopie
raw(obj)das zugrunde liegende Objekt, ohne Nachverfolgung
readonly(obj)eine Ansicht, in die man nicht schreiben kann

Für React-Panels gibt es ein Begleitpaket mit useSignal, useComputed, useSignalEffect und useLiveSignal. Eine Komponente rendert dann aus einem Signal neu, ohne zusätzliche Verdrahtung.

@was/ui: Editor-Komponenten​

Bauen Sie Plugin-Panels und Komponenten-Erweiterungen aus denselben Komponenten, die der Editor nutzt. Ihre Panels sehen dann aus wie ein Teil des Editors und nicht wie eine eingebettete Webseite. Die Bibliothek enthält diese Komponenten:

Accordion · Avatar · Button · Card · Checkbox · Chip · CloseButton
DimensionInput · Draggable · Dropdown · Flag · Icon · IconButton · Input
Menu · Modal · Outside · Panel · Popover · Portal · Render · Scroll
Search · Section · Segmented · Select · Skeleton · Switcher · Tabs
Toast · Toolbar · Tooltip

Für Icons nutzen Sie @was/icons.

Das Stylesheet ist fertig vorgebaut

Panels können nur die Utility-Klassen nutzen, die mit der Bibliothek ausgeliefert werden. Beliebige Werte wie text-[13px] sind nicht enthalten. Für Größen außerhalb der Skala nutzen Sie ein Inline-style.

@was/ecs: Entitäten und Komponenten​

Dieses Paket enthält Entity, Component und Space. Damit liest und ändert ein Plugin eine Szene über die vollständige API, nicht über eine separate, eingeschränkte.

Space: die Welt​

AufrufGibt zurück
getEntity(id)eine Entität oder nichts
hasEntity(id)ob sie existiert
getEntities()alle Entitäten
queryEntities(A, B, …)jede Entität, die alle diese Komponenten hat
makeQueryEntities(A, B, …)dieselbe Abfrage, einmal gebaut für wiederholte Nutzung
createEntity(id?, components?)eine neue Entität, bereits in der Welt
addEntity(…e) · removeEntity(…e)fügt eine Entität der Welt hinzu oder entfernt sie
getSystem(S) · hasSystem(S)ein System, oder ob es existiert
execute()berechnet einen Frame
Entitäten mit queryEntities finden

Die Frage nach „allem mit Licht und Transformation“ ist so schnell beantwortet. getEntities() liefert die ganze Welt, und Sie müssen selbst filtern.

Entity: ein Objekt in der Welt​

AufrufWas er tut
getComponent(Type)gibt eine Komponente zurück oder null
getComponents(A, B)gibt mehrere auf einmal zurück, in dieser Reihenfolge
hasComponent(A, B)prüft, ob alle vorhanden sind
addComponent(…c)fügt Komponenten hinzu; ein Typ, den sie schon hat, wird ignoriert
removeComponent(…c)entfernt Komponenten, per Klasse oder per Instanz
component(fn)ruft fn für jede Komponente auf, jetzt und künftig
clone() · clean()kopiert die Entität oder entfernt alle ihre Komponenten
.id · .componentsihre Id und ihre Komponenten, nach Typ geordnet

Component: die Daten​

AufrufWas er tut
.x oder get('x')liest ein Feld; innerhalb eines Effekts abonniert es das Feld auch
$datadie ganze Komponente als einfaches Objekt
$rawDatadas gespeicherte Objekt, ohne Abonnement; nur zum Lesen
update({ … })schreibt Daten. Nur so wird geschrieben.
updateAt(path, value)schreibt tief in eine große Komponente, ohne alles neu zu validieren
version(field?)ein Zähler, der bei jeder Änderung hochgeht. So hängen Sie von einem Feld ab, ohne es zu lesen
reset(data?)setzt auf die Standardwerte zurück
clone()erstellt eine Kopie
version() für große Listen nutzen

Wer einen großen Wert liest, abonniert jedes Element darin. Hängen Sie stattdessen von seinem Versionszähler ab: Sie erfahren, dass er sich geändert hat, ohne jedes Element zu beobachten. So bleibt ein Panel auch mit einer Liste von zwanzigtausend Einträgen schnell.

@was/engine: Komponentenklassen​

Jeder Komponententyp ist eine Klasse: TransformComponent, MaterialComponent, RigidBodyComponent und so weiter. Importieren Sie die Klasse und übergeben Sie sie an getComponent, hasComponent oder query.

Die vollständige Liste mit allen Feldern finden Sie in der Komponenten-Referenz.

@was/svdt: Schemas​

Mit diesem Paket beschreiben Sie die Form Ihrer Daten und prüfen sie.

import { s, compile } from '@was/svdt';

const schema = s.object({ speed: s.f64(1), name: s.string('') });
const codec = compile(schema); // einmal kompilieren, dort wo Sie es deklarieren, nie bei jedem Aufruf
const value = codec.parse(input);

Eine Form beschreiben​

  • Zahlen: s.f32 s.f64 s.i8 s.u8 s.i16 s.u16 s.i32 s.u32, jeweils mit Standardwert.
  • Skalare: s.bool, s.string, s.color, s.literal, s.enum, s.unknown, s.ref.
  • Vektoren: s.vec2, s.vec3, s.mat4.
  • Zusammengesetzte Typen: s.object, s.variant, s.array, s.record, s.union, s.preprocess, s.lazy.

An jeden davon können Sie Folgendes anhängen: .default(v), .optional(), .nullable(), .min(n), .max(n), .int().

Einen kompilierten Codec nutzen​

AufrufWas er tut
parse(input)prüft und normalisiert; wirft bei ungültiger Eingabe einen Fehler
safeParse(input)dasselbe, gibt aber Erfolg oder einen Fehler zurück, statt zu werfen
parseAt(path, v)prüft ein Feld, ohne den Rest anzufassen
equals(a, b)tiefer Vergleich
diff(a, b)gibt zurück, was sich geändert hat
apply(target, p)wendet einen Diff an
invert(p)kehrt einen Diff um
pack · unpackwandelt in eine kompakte Binärform um und zurück

Um statt der Daten das Schema selbst zu untersuchen, nutzen Sie introspect, keys und requiredKeys.

@was/utils​

Kleine Hilfsfunktionen: pick, omit, assign, keys, values, entries, capitalize, basename, extname, dispose (fasst Aufräumfunktionen zu einer zusammen) und MIME-Typ-Hilfen, mit denen Sie Dateitypen prüfen.


Weiter: Glossar — die Begriffe, die in dieser Dokumentation vorkommen.