Eigene Komponenten
Die zweite Art Plugin öffnet gar kein Panel. Sie hängt sich als Komponente an ein Objekt und zeichnet ihre Oberfläche im Inspektor des Editors, neben Transform und Material.
Das ist die richtige Form, wenn Ihr Werkzeug von einem bestimmten Objekt handelt: Notizen zu einem Objekt, ein eigenes Bedienelement, ein Generator, der zu dem gehört, was er erzeugt.
Deklarieren
Deklarieren Sie sie in components; panels darf leer bleiben:
{
"id": "entity-notes",
"entry": "main.tsx",
"panels": [],
"components": [{ "id": "notes", "title": "Notes", "entry": "main.tsx" }]
}
Schreiben
import {
Button,
Column,
Text,
defineProps,
useEntity,
useSignals,
useSyncedState,
} from '@was/extension';
import { TransformComponent } from '@was/engine';
const useProps = defineProps({ step: { type: 'number', default: 0.5 } });
export default function Notes() {
useSignals(); // beim Rendern gelesene Szenendaten verfolgen
const props = useProps(); // Werte aus dem Inspektor-Formular
const entity = useEntity(); // das Objekt, an dem das hängt
const [note, setNote] = useSyncedState('note', '');
return (
<Column gap={2}>
<Text muted>{note}</Text>
<Button
onClick={() =>
setNote(`y=${entity.getComponent(TransformComponent)?.get('position').y}`)
}
>
Remember
</Button>
</Column>
);
}
Vier Hooks erledigen die Arbeit:
| Hook | Liefert Ihnen |
|---|---|
useEntity() | das Objekt, an dem Ihre Komponente hängt |
useProps() | die Werte aus dem mit defineProps deklarierten Formular |
useSignals() | ein Neurendern, wenn sich beim Rendern gelesene Szenendaten ändern |
useSyncedState() | Zustand, der am Objekt liegt und wie jede Komponente synchronisiert wird |
defineProps benutzt dieselben Feldtypen wie Skript-Eigenschaften — Zahl, Zeichenkette, Boolean,
Farbe, Auswahl, Objekt, Ressource — Ihre Komponente bekommt also ein ordentliches
Inspektor-Formular geschenkt.
Wie es läuft
Jede Komponenten-Erweiterung jedes Plugins läuft in einem gemeinsamen Worker, mit React 19 darin.
Hooks, useEffect, Context und Suspense funktionieren genau so, wie Sie es erwarten.
Was der Worker erzeugt, ist kein DOM, sondern eine Beschreibung davon, die der Editor mit seinen eigenen Komponenten zeichnet. Genau das lässt eine Erweiterung nativ wirken statt wie eine eingebettete Seite.
Zwei Folgen dieser Grenze:
- Die Menge der Elemente ist eine feste Liste. Sie bauen aus den bereitgestellten Komponenten, nicht aus beliebigem HTML.
- Handler überqueren die Grenze nicht. Dem Editor wird nur gesagt, dass ein Handler existiert;
ein Klick kommt als Nachricht zurück und ruft Ihre Funktion im Worker auf. Sie schreiben
onClickganz normal — das zählt nur, wenn Sie ein DOM-Event weiterreichen wollten.
Wie man sie hinzufügt
Ihre Erweiterung erscheint in + New component, in einer Gruppe Extensions — sie wird nicht besonderer behandelt als eine eingebaute Komponente.
Sie erscheint außerdem in der Werkzeugleiste unter +, gruppiert unter der Überschrift Plugins, damit klar ist, woher ein unbekannter Button kommt. Die beiden Einstiege verhalten sich bewusst unterschiedlich:
| Hinzugefügt über | Ergebnis |
|---|---|
| + New component | hängt sich an das bereits ausgewählte Objekt |
| Die Werkzeugleiste | erzeugt ein neues Objekt mit Ihrer Komponente und wählt es aus |
Ein lokal verbundener Ordner taucht in dieser Liste ebenfalls auf, wenn er ein Panel hat — sonst wäre der einzige Weg dorthin, jedes Mal das Plugins-Panel zu öffnen. Ein Ordner, der nichts als ein Overlay enthält, erscheint nicht: Es gäbe nichts zu drücken.
Vertrauen
Sie hängt sich ein, sobald irgendwer ein Projekt öffnet, das sie enthält — ganz ohne Klick. Genau das stellt sie hinter die stärkere Zustimmung, beschrieben unter Vertrauen und Prüfung.
Weiter: Tastenkürzel