Aller au contenu principal

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 :

Scripts/spinner.ts
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 :

PartieCe qu'elle fait
initDémarre le script et vous donne ctx, votre accès à la scène en cours. Tout commence ici.
definePropsAjoute des réglages dans l'inspecteur. Vous les remplissez pour chaque objet.
ctx.onRéagit à un déclencheur, comme un toucher ou une collision.
ctx.tickS'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.

Modifier une valeur imbriquée efface les autres

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 } });
Un matériau est une liste de slots

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' },
});
TypeL'éditeur afficheVotre script reçoit
numberun champ ou un curseurun nombre
stringun champ de texteune chaîne
booleanun interrupteurun booléen
colorun sélecteur de couleurune couleur sous forme de chaîne
selectune liste déroulantel'une de vos options
entityun sélecteur d'objetl'objet lui-même, prêt à l'emploi
sceneun sélecteur de scènel'objet scène
resourceun sélecteur de ressourceune référence que vous pouvez transmettre
arrayune listeun tableau
groupun bloc avec un titreun objet imbriqué

Options utiles :

  • label et help — le texte affiché dans le panneau.
  • min et max — 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.
Écrivez 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 son id à 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.store et 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.

Les scripts ne s'exécutent pas dans l'éditeur

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.