Paquets partagés
Vous pouvez importer ces paquets dans votre propre code. Voici ce que vous pouvez utiliser, où, et ce que chaque paquet vous apporte.
Dans le code, les objets que vous voyez dans l'éditeur s'appellent des entités (entities). C'est la même chose.
Les paquets que vous pouvez importer dépendent de l'endroit où votre code s'exécute :
| Paquet | Ce qu'il vous apporte | Dans un script | Dans un plugin | Dans une extension de composant |
|---|---|---|---|---|
@was/ecs | les entités, les composants, le monde | ✓ | ✓ | ✓ |
@was/engine | les classes de composants | ✓ | ✓ | ✓ |
@was/signals | des valeurs réactives | — | ✓ | ✓ |
@was/svdt | des schémas et la validation | ✓ | ✓ | — |
@was/utils | de petites fonctions utilitaires | ✓ | ✓ | — |
@was/ui | la bibliothèque de composants de l'éditeur | — | ✓ | ✓ |
@was/icons | le jeu d'icônes de l'éditeur | — | ✓ | ✓ |
react | React 19 | — | ✓ | ✓ |
ctx.effectUn script ne peut pas importer @was/signals directement. Utilisez ctx.effect à la place. Il
fonctionne de la même façon, et il s'arrête quand son objet disparaît : rien ne reste en marche.
@was/signals : valeurs réactives
Un signal est une valeur qui sait qui la lit. Quand elle change, seul ce qui en dépend se met à jour.
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); // ne se relance que quand score change
});
batch(() => {
score.value += 1;
score.value += 1; // les effets s'exécutent une fois, pas deux
});
stop();
| Fonction | Ce qu'elle fait |
|---|---|
signal(v) | une valeur que l'on peut observer ; lisez et écrivez .value |
computed(fn) | une valeur dérivée, recalculée seulement quand ce qu'elle lit change |
effect(fn) | s'exécute tout de suite, puis à chaque changement de ce qu'elle a lu ; renvoie une fonction d'arrêt |
batch(fn) | regroupe des écritures, pour que les observateurs ne s'exécutent qu'une fois, à la fin |
untracked(fn) | lit une valeur sans en dépendre |
ref(obj) | rend tout un objet réactif, jusqu'à la moindre valeur imbriquée |
snapshot(obj) | une copie simple, non réactive |
raw(obj) | l'objet sous-jacent, sans suivi |
readonly(obj) | une vue dans laquelle on ne peut pas écrire |
Pour les panneaux React, un paquet complémentaire vous donne useSignal, useComputed,
useSignalEffect et useLiveSignal. Un composant se redessine alors à partir d'un signal, sans
aucun branchement supplémentaire.
@was/ui : composants de l'éditeur
Construisez les panneaux de vos plugins et vos extensions de composants avec les mêmes composants que l'éditeur. Vos panneaux ont alors l'air de faire partie de l'éditeur, et non d'une page web intégrée. La bibliothèque contient ces composants :
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
Pour les icônes, utilisez @was/icons.
Les panneaux ne peuvent utiliser que les classes utilitaires fournies avec la bibliothèque. Les
valeurs arbitraires comme text-[13px] ne sont pas incluses. Pour une taille hors de l'échelle,
utilisez un style en ligne.
@was/ecs : entités et composants
Ce paquet contient Entity, Component et Space. Avec lui, un plugin lit et modifie une scène
grâce à l'API complète, et non à une API séparée et limitée.
Space : le monde
| Appel | Renvoie |
|---|---|
getEntity(id) | une entité, ou rien |
hasEntity(id) | si elle existe |
getEntities() | toutes les entités |
queryEntities(A, B, …) | toutes les entités qui ont tous ces composants |
makeQueryEntities(A, B, …) | la même requête, construite une fois pour être réutilisée |
createEntity(id?, components?) | une nouvelle entité, ajoutée au monde |
addEntity(…e) · removeEntity(…e) | ajoute une entité au monde ou l'en retire |
getSystem(S) · hasSystem(S) | un système, ou s'il existe |
execute() | exécute une image |
queryEntities pour trouver des entitésDemander ainsi « tout ce qui a une lumière et un transform » est rapide. getEntities() vous
donne le monde entier, et c'est à vous de le filtrer.
Entity : un objet du monde
| Appel | Ce qu'il fait |
|---|---|
getComponent(Type) | renvoie un composant, ou null |
getComponents(A, B) | en renvoie plusieurs à la fois, dans cet ordre |
hasComponent(A, B) | vérifie si l'entité les a tous |
addComponent(…c) | ajoute des composants ; un type qu'elle a déjà est ignoré |
removeComponent(…c) | retire des composants, par classe ou par instance |
component(fn) | exécute fn pour chaque composant, présent et à venir |
clone() · clean() | copie l'entité, ou retire tous ses composants |
.id · .components | son id, et ses composants classés par type |
Component : les données
| Appel | Ce qu'il fait |
|---|---|
.x ou get('x') | lit un champ ; dans un effet, s'y abonne aussi |
$data | le composant entier, sous forme d'objet simple |
$rawData | l'objet stocké, sans abonnement ; en lecture seule |
update({ … }) | écrit des données. C'est la seule façon d'écrire. |
updateAt(path, value) | écrit en profondeur dans un gros composant sans tout revalider |
version(field?) | un compteur qui augmente à chaque changement, pour dépendre d'un champ sans le lire |
reset(data?) | revient aux valeurs par défaut |
clone() | crée une copie |
version() pour les grandes listesLire une grosse valeur vous abonne à chacun de ses éléments. Dépendez plutôt de son compteur de version : vous savez qu'elle a changé sans surveiller chaque élément. C'est ainsi qu'un panneau reste rapide avec une liste de vingt mille éléments.
@was/engine : classes de composants
Chaque type de composant est une classe : TransformComponent, MaterialComponent,
RigidBodyComponent et les autres. Importez la classe et passez-la à getComponent,
hasComponent ou query.
Pour la liste complète avec tous les champs, consultez la référence des composants.
@was/svdt : schémas
Ce paquet vous permet de décrire la forme de vos données et de les vérifier.
import { s, compile } from '@was/svdt';
const schema = s.object({ speed: s.f64(1), name: s.string('') });
const codec = compile(schema); // compiler une seule fois, là où vous le déclarez, jamais à chaque appel
const value = codec.parse(input);
Décrire une forme
- Nombres :
s.f32 s.f64 s.i8 s.u8 s.i16 s.u16 s.i32 s.u32, chacun avec une valeur par défaut. - Scalaires :
s.bool,s.string,s.color,s.literal,s.enum,s.unknown,s.ref. - Vecteurs :
s.vec2,s.vec3,s.mat4. - Composites :
s.object,s.variant,s.array,s.record,s.union,s.preprocess,s.lazy.
Vous pouvez enchaîner ces méthodes sur n'importe lequel d'entre eux : .default(v),
.optional(), .nullable(), .min(n), .max(n), .int().
Utiliser un codec compilé
| Appel | Ce qu'il fait |
|---|---|
parse(input) | valide et normalise ; lève une erreur si l'entrée est incorrecte |
safeParse(input) | la même chose, mais renvoie un succès ou une erreur au lieu de la lever |
parseAt(path, v) | valide un seul champ sans toucher au reste |
equals(a, b) | comparaison en profondeur |
diff(a, b) | renvoie ce qui a changé |
apply(target, p) | applique un diff |
invert(p) | inverse un diff |
pack · unpack | convertit vers et depuis un format binaire compact |
Pour examiner le schéma lui-même plutôt que des données, utilisez introspect, keys et
requiredKeys.
@was/utils
De petites fonctions utilitaires : pick, omit, assign, keys, values, entries,
capitalize, basename, extname, dispose (qui réunit plusieurs fonctions de nettoyage en une
seule), et des fonctions de types MIME pour vérifier le type des fichiers.
Suivant : Glossaire — les termes employés dans cette documentation.