Composants personnalisés
Un plugin peut ajouter son propre composant aux objets. Son interface s'affiche dans l'inspecteur, à côté de Transform et Material.
C'est utile quand votre outil concerne un seul objet : des notes sur un objet, un contrôle sur mesure, ou un générateur qui reste attaché à ce qu'il crée. Un plugin de ce type n'a pas besoin de panneau.
Déclarer le composant
Listez-le sous components dans le manifeste. panels peut être vide :
{
"id": "entity-notes",
"entry": "main.tsx",
"panels": [],
"components": [{ "id": "notes", "title": "Notes", "entry": "main.tsx" }]
}
Écrire le composant
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(); // nouveau rendu quand les données de scène lues ici changent
const props = useProps(); // les valeurs du formulaire de l'inspecteur
const entity = useEntity(); // l'objet auquel le composant est attaché
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>
);
}
Ces hooks font le travail :
| Hook | Ce qu'il vous donne |
|---|---|
useEntity() | l'objet auquel votre composant est attaché |
useWorld() | la scène dans laquelle se trouve cet objet |
useProps() | les valeurs du formulaire déclaré avec defineProps |
useSignals() | un nouveau rendu quand les données de scène lues pendant le rendu changent |
useSyncedState() | un état stocké sur l'objet et synchronisé comme n'importe quel composant |
useSelected() | si l'objet est sélectionné dans l'éditeur |
useViewportPointer() | les événements de souris dans la zone d'affichage, avec l'objet et le point sous le pointeur |
defineProps utilise les mêmes types de champs que les propriétés de script : nombre, chaîne,
booléen, couleur, liste de choix, entité, scène, espace, ressource, enregistrement (record), tableau
et groupe. Votre composant obtient ainsi un vrai formulaire dans l'inspecteur, sans travail
supplémentaire.
Modifier les valeurs depuis votre composant
props.$set(patch) modifie les champs de votre propre composant, comme le fait l'inspecteur : la
modification est synchronisée chez tout le monde et peut être annulée. Elle est vérifiée par
rapport à defineProps : les nombres restent entre leurs min et max, et un champ que vous
n'avez pas déclaré lève une erreur. Utilisez-la pour faire glisser une valeur dans la zone
d'affichage :
<Handle axis={[0, 1, 0]} onDrag={(p) => props.$set({ height: p[1] })} />
Les noms de champs qui commencent par $ sont réservés à cet usage : defineProps les refuse donc.
Déclarer l'état
defineState déclare l'état du composant avec ses valeurs par défaut, de la même façon que
defineProps déclare le formulaire :
const useScore = defineState({ points: { type: 'number', default: 0 } });
const [score, setScore] = useScore(); // à l'intérieur du composant
Les scripts lisent alors cet état avec ses types, et avec les mêmes valeurs par défaut.
Le traduire
i18n() ou useEditorLocale() de @was/extension vous donnent t(key, vars) sur les
fichiers de langue de votre plugin, dans la
langue de l'éditeur.
Comment il s'exécute
Votre composant est du vrai React 19. Les hooks, useEffect, le contexte et Suspense fonctionnent
comme d'habitude.
L'éditeur dessine votre composant avec ses propres éléments. C'est pour cela qu'il a l'air de faire partie de l'éditeur.
Deux conséquences :
- Vous construisez uniquement avec les éléments fournis. Le HTML libre n'est pas disponible.
Dans l'inspecteur :
Column,Row,Text,Button,Divider,InputText,InputNumber,InputColor,Slider,CheckboxetSelect. Dans la zone d'affichage :Overlay,Gizmo,Group,Box,Sphere,Line,HandleetControl. Donnezwhen="selected"à unGizmoou à unOverlaypour ne l'afficher que sur l'objet sélectionné. - Les gestionnaires d'événements s'écrivent comme d'habitude.
onClickfonctionne normalement. La seule chose impossible : faire circuler un événement DOM.
Comment l'ajouter à un objet
Votre composant peut être ajouté de deux façons :
| Ajouté depuis | Résultat |
|---|---|
| + new component, dans le groupe Extensions | il est ajouté à l'objet déjà sélectionné |
| La barre d'outils, sous +, dans le groupe Plugins | un nouvel objet est créé avec votre composant, puis sélectionné |
L'intitulé Plugins dans la barre d'outils indique d'où vient un bouton inconnu.
Un dossier de plugin connecté depuis votre ordinateur apparaît aussi dans cette liste de la barre d'outils s'il a un panneau : vous pouvez donc l'ouvrir sans passer à chaque fois par le panneau Plugins. Un dossier qui ne contient qu'une surcouche n'y apparaît pas, puisqu'il n'y a rien à ouvrir.
L'utiliser dans les scripts et dans le projet publié
L'interface du composant ne fonctionne que dans l'éditeur. Ses valeurs sont enregistrées sur l'objet, si bien que le projet peut s'en servir quand il est joué :
- les scripts lisent et modifient le composant avec
ctx.plugin('<plugin id>/<component id>')→ L'API ctx ; - le code de votre plugin pour le projet publié fait de même → Code d'exécution.
Confiance
Un composant personnalisé démarre dès que quelqu'un ouvre un projet qui le contient. C'est pour cela qu'il passe par le contrôle plus strict décrit dans Confiance et vérification.
Suite : Code d'exécution — faites fonctionner votre plugin dans le projet publié.