Premier script
Écrivez un petit script qui fait tourner un objet quand on le touche. Ajoutez ensuite des réglages dans l'inspecteur, et voyez ce qu'un script peut utiliser.
Un script est un fichier TypeScript que vous attachez à un objet. Il s'exécute dans le lecteur. Chaque objet qui utilise le script reçoit sa propre copie.
Voici un script complet. Touchez l'objet, et il se met à tourner :
import { TransformComponent } from '@was/engine';
init((ctx) => {
const props = defineProps({
speed: { type: 'number', default: 1, min: 0, label: 'Speed' },
});
let spinning = false;
ctx.on('on-click', () => {
spinning = !spinning;
});
ctx.tick((dt) => {
if (!spinning) return;
const t = ctx.entity.getComponent(TransformComponent);
t?.update({
rotation: { ...t.$data.rotation, y: t.$data.rotation.y + props.speed * dt },
});
});
return () => {
// facultatif : faire le ménage quand cet objet disparaît
};
});
Le script a quatre parties :
| Partie | Ce qu'elle fait |
|---|---|
init | Démarre le script et vous donne ctx, votre accès à la scène en cours. Tout commence ici. |
defineProps | Ajoute des réglages dans l'inspecteur. Vous les remplissez pour chaque objet. |
ctx.on | Réagit à un déclencheur, comme un toucher ou une collision. |
ctx.tick | S'exécute à chaque image. dt est le temps écoulé depuis l'image précédente, en secondes. |
init et defineProps sont toujours disponibles. Vous ne les importez jamais. Les classes de
composants, comme TransformComponent, viennent de @was/engine.
Lire et modifier des objets
const transform = ctx.entity.getComponent(TransformComponent);
transform.position; // lire une valeur
transform.$data; // lire le tout sous forme d'objet simple
transform.update({ … }); // écrire
Deux erreurs piègent presque tout le monde.
update garde les champs que vous ne passez pas, comme rotation. Mais une valeur que vous passez
remplace entièrement l'ancienne. update({ position: { y: 2 } }) remet donc aussi x et z à leur
valeur par défaut, zéro.
Recopiez les valeurs à garder avec ... :
transform.update({ position: { ...transform.$data.position, y: 2 } });
material.update({ color }) ne fait rien, parce que color se trouve dans un slot. Modifiez
plutôt le slot :
material.update({
materials: [{ ...material.$data.materials[0], color: '#ff0000' }],
});
Ne modifiez jamais $data directement. On dirait que cela a marché, mais le changement est perdu.
Réglages dans l'inspecteur
Avec defineProps, vous pouvez mettre un même script sur dix objets et le régler différemment sur
chacun. Sans copier-coller.
const props = defineProps({
speed: { type: 'number', default: 1, min: 0, softMax: 10, suffix: 'm/s' },
target: { type: 'entity', label: 'Look at' },
sound: { type: 'resource', resource: 'audio' },
mode: { type: 'select', options: ['chase', 'patrol'], default: 'patrol' },
});
| Type | L'éditeur affiche | Votre script reçoit |
|---|---|---|
number | un champ ou un curseur | un nombre |
string | un champ de texte | une chaîne |
boolean | un interrupteur | un booléen |
color | un sélecteur de couleur | une couleur sous forme de chaîne |
select | une liste déroulante | l'une de vos options |
entity | un sélecteur d'objet | l'objet lui-même, prêt à l'emploi |
scene | un sélecteur de scène | l'objet scène |
resource | un sélecteur de ressource | une référence que vous pouvez transmettre |
array | une liste | un tableau |
group | un bloc avec un titre | un objet imbriqué |
Options utiles :
labelethelp— le texte affiché dans le panneau.minetmax— des limites strictes.softMax— la fin du curseur. On peut toujours saisir un nombre plus grand.showWhen— n'affiche un champ que lorsqu'un autre champ a une certaine valeur.group— garde une longue liste de réglages bien rangée.
defineProps en entierÉcrivez vos réglages directement dans l'appel, sinon l'inspecteur ne peut pas les afficher. Ne les construisez pas à partir de variables, et ne les renvoyez pas depuis une fonction.
Votre script reçoit toujours la valeur actuelle : props.speed vaut ce que l'inspecteur affiche à
cet instant.
Imports
import { TransformComponent } from '@was/engine';
import helpers from 'Scripts/helpers';
Vous pouvez importer les classes de composants du moteur. Vous pouvez aussi importer d'autres ressources par leur chemin :
- un autre script vous donne ce qu'il exporte ;
- un patch vous donne son code, tel que le montre le panneau Code ;
- tout le reste vous donne une référence,
{ id, type }. Passez-la àctx.spawn, ou passez sonidàctx.audio.play.
Si vous renommez ou déplacez une ressource, ces imports se mettent à jour automatiquement.
Ce qu'un script ne peut pas utiliser
Un script lourd ne peut pas ralentir l'image. Ces éléments ne sont pas disponibles dans un script :
window,document, le DOM ;fetch,localStorage, ou tout accès au réseau ;- les bibliothèques de rendu — vous modifiez la scène par les composants, pas en dessinant ;
- les minuteurs du navigateur — utilisez
ctx.tickà la place.
Utilisez plutôt :
- pour l'interface — les cartes d'interface ;
- pour le son —
ctx.audio; - pour garder des valeurs —
ctx.storeet les variables globales.
Quand un script démarre et s'arrête
Un script démarre quand son objet est actif à l'écran : rien au-dessus de lui n'est désactivé, et sa scène est active. Il s'arrête dès que ce n'est plus le cas.
on-launch se déclenche dès que le script démarre : vous ne le manquez donc jamais.
La fonction que vous renvoyez depuis init s'exécute quand le script s'arrête. Servez-vous-en
pour faire le ménage : vous désabonner, couper les sons, effacer l'état.
L'éditeur affiche votre scène, mais n'exécute aucune logique. Modifiez le script, puis ouvrez l'aperçu (Preview). Il n'y a pas de rechargement à chaud : une expérience en cours prend vos changements en compte à son prochain redémarrage.
Suivant : Où placer un script — à quel niveau attacher un script.