Aller au contenu principal

Code d'exécution

Donnez à votre plugin du code qui s'exécute dans le projet publié : des systèmes qui font bouger les objets, des déclencheurs et des étapes pour les événements, et des réglages pour tout le projet.

Les panneaux et les composants personnalisés fonctionnent dans l'éditeur. Le code d'exécution tourne partout où le projet est joué : dans le projet publié, sur le web et dans l'application, dans l'aperçu de l'éditeur et dans les playtests.

Ajouter du code d'exécution​

Dans le manifeste, faites pointer runtime vers un fichier .ts ou .js du dossier de votre plugin :

{
"id": "acme.spin",
"name": "Spin",
"version": "1.0.0",
"entry": "main.tsx",
"permissions": [],
"panels": [],
"components": [{ "id": "spin", "title": "Spin", "entry": "spin.tsx" }],
"runtime": "runtime/index.ts"
}

Le fichier s'exécute une fois, au démarrage du projet, à côté des scripts du projet. Enregistrez-y un système. Un système s'exécute à chaque image :

// runtime/index.ts
import { Collection, System } from '@was/ecs';
import { TransformComponent, frameTime, pluginComponent } from '@was/engine';

class AcmeSpinSystem extends System.from('AcmeSpinSystem') {
execute() {
const { dt } = frameTime();
const spin = pluginComponent('acme.spin/spin', this.space);
for (const e of spin.entities()) {
const t = e.getComponent(TransformComponent)!;
const r = t.$data.rotation;
t.update({ rotation: { ...r, y: r.y + (spin.props(e)!.speed as number) * dt } });
}
}
}
Collection.registerSystem(AcmeSpinSystem);

Ce système fait tourner chaque objet qui a le composant Spin du plugin, à la vitesse réglée dans l'inspecteur.

Nommez les systèmes d'après votre plugin

Les noms des systèmes sont partagés par tout ce qui se trouve dans le projet. Si un nom est déjà pris, votre système ne démarre pas, et l'erreur indique de quel nom il s'agit. Commencez chaque nom par le nom de votre plugin, comme AcmeSpinSystem.

Ce que le code d'exécution peut utiliser​

Le code d'exécution peut importer la même chose qu'un script : @was/ecs, @was/engine, @was/utils et @was/svdt. Seuls le fichier d'entrée et les fichiers qu'il importe avec ./ sont livrés, jusqu'à 1 Mo au total.

Il n'a ni page, ni DOM, ni three.js, ni React. Il lit et modifie des composants, et le moteur dessine le résultat.

FonctionCe qu'elle vous donne
frameTime(){ dt, t } : le temps écoulé depuis l'image précédente et depuis le démarrage, en secondes
pluginComponent(key, space)votre composant personnalisé sur les objets : le lire et le modifier
pluginEvents(id, space)activer vos déclencheurs et répondre à vos étapes
pluginSettings(id)les valeurs que le projet a choisies pour vos réglages
pluginContext(id, space)le ctx des scripts : entrées, physique, rayons, son, variables globales, multijoueur

key vaut <plugin id>/<component id>, comme acme.spin/spin. id est l'id du plugin dans le manifeste.

Votre composant​

pluginComponent(key, space) vous donne :

MéthodeCe qu'elle fait
entities()les objets qui ont le composant
has(e)si l'objet a le composant, et s'il est activé
props(e)les valeurs de l'inspecteur, avec leurs valeurs par défaut
state(e)l'état propre au composant
setState(e, patch)modifie l'état
setProps(e, patch)modifie les valeurs de l'inspecteur
setEnabled(e, on)active ou désactive le composant
add(e, props)ajoute le composant à un objet
remove(e)le retire

Les scripts obtiennent la même chose avec ctx.plugin(key).

Le contexte de script​

pluginContext(id, space) renvoie le même ctx qu'un script, pour les entrées, la physique, les raycasts, l'audio, les variables globales et le multijoueur. Il vaut null tant que les scripts du projet n'ont pas démarré, et il peut changer quand le monde change : demandez-le donc au moment où vous en avez besoin. ctx.on et ctx.tick ajoutent un nouveau gestionnaire à chaque appel : abonnez-vous une seule fois par contexte.

Déclencheurs et étapes​

Un plugin qui a du code d'exécution peut ajouter ses propres déclencheurs et ses propres étapes au panneau des événements. On les utilise alors sans code, comme ceux qui sont intégrés. Ils apparaissent sous le nom de votre plugin, dans les projets qui ont le plugin.

Déclarez-les dans le manifeste :

"triggers": [
{ "id": "reached", "title": "Reached a point",
"params": { "point": { "type": "number", "default": -1, "any": -1, "label": "Point" } } }
],
"steps": [
{ "id": "pause", "title": "Pause",
"params": { "seconds": { "type": "number", "default": 0, "min": 0 } } }
]
  • Les identifiants sont en kebab-case. Le nom complet est <plugin id>:<id>, comme acme.patrol:reached.
  • Les params utilisent les types de champs de defineProps : number, string, boolean, color, select, entity et resource. Le panneau dessine un formulaire pour eux.
  • Les paramètres d'un déclencheur servent de filtre. Un événement réglé sur le point 2 ne s'exécute que lorsque le déclencheur s'active avec point: 2. Un paramètre laissé vide, ou réglé sur sa valeur any, laisse passer toutes les activations.

Activez les déclencheurs et répondez aux étapes depuis votre code d'exécution :

import { pluginEvents } from '@was/engine';

class AcmePatrolSystem extends System.from('AcmePatrolSystem') {
events = pluginEvents('acme.patrol', this.space);
off = this.events.onStep<{ seconds: number }>('pause', (targets, params) => {
// arrêter les objets de `targets` pendant params.seconds
});

execute() {
// quand un garde atteint un point :
// this.events.fire('reached', guard, { point: 2 });
}

dispose() {
this.off();
}
}
  • fire(id, object, payload) exécute les événements qui utilisent ce déclencheur sur cet objet et sur ses parents. Sans objet, il s'active partout. Les scripts le reçoivent sous la forme ctx.on('acme.patrol:reached', payload => …).
  • onStep(id, handler) s'exécute quand un événement exécute votre étape. Un script peut aussi l'exécuter, avec ctx.step('acme.patrol:pause', { seconds: 2 }). Abonnez-vous une seule fois, et appelez la fonction renvoyée dans dispose().
  • Si le plugin est retiré d'un projet, ses déclencheurs ne s'activent plus jamais et ses étapes sont ignorées.

Réglages du projet​

Certaines valeurs appartiennent à tout le projet : un tempo, une difficulté, une couleur. Déclarez-les sous settings : jusqu'à 24 champs, de types number, string, boolean, color et select.

"settings": {
"tempo": { "type": "number", "default": 1, "min": 0, "label": "Tempo" }
}

On les règle dans les réglages du projet, dans une section qui porte le nom de votre plugin.

  • Le code d'exécution les lit avec pluginSettings<{ tempo: number }>('acme.patrol'). Vous recevez tous les champs : la valeur du projet, ou la valeur par défaut. Les valeurs sont prêtes avant que votre fichier d'entrée s'exécute, et elles ne changent pas pendant que le projet tourne.
  • Un projet publié utilise les valeurs avec lesquelles il a été publié.
  • Votre panneau les lit et les modifie avec editor.pluginSettings.get() et editor.pluginSettings.set({ tempo: 2 }). Cela nécessite la permission settings:write.

Comment le code arrive dans un projet​

Le code d'exécution ne tourne que dans les projets auxquels le plugin a été ajouté.

  • Ajouter le plugin fige la version. Le projet garde le code qu'il avait à ce moment-là. Les aperçus, les playtests et les publications exécutent ce code, même après que vous en avez publié une version plus récente.
  • La mise à jour est un choix. Quand il existe un code plus récent, le plugin affiche update available dans le panneau Plugins. Update in project fait passer le projet à ce nouveau code.
  • Vos propres projets vous suivent. Si vous avez ajouté votre propre plugin à un projet, le projet exécute chaque version que vous enregistrez.
  • Une publication garde son code. Un projet publié exécute le code avec lequel il a été publié, jusqu'à ce qu'il soit publié à nouveau.

Les titres, les descriptions et les libellés des déclencheurs, des étapes et des réglages peuvent venir de vos fichiers de langue, sous la forme ${…}.

Plugins réservés aux abonnés​

Le code d'exécution d'un plugin est envoyé à toutes les personnes qui ouvrent le projet publié, et n'importe qui peut le lire dans son navigateur. C'est vrai aussi pour un plugin Subscribers only. L'abonnement décide qui peut ajouter le plugin à un projet, le mettre à jour et publier avec. Il ne cache pas le code.

Si l'abonnement prend fin, un projet qui utilise le plugin ne peut plus être republié. Renouvelez l'abonnement ou retirez le plugin. Un projet déjà publié continue de fonctionner.

Pour les développeurs​

  • Le code d'exécution tourne dans le worker de scripts du projet, avec les scripts du projet.
  • La version que garde un projet est une empreinte des fichiers de code d'exécution, des déclencheurs, des étapes et des réglages. Une empreinte différente affiche update available.

Suite : Raccourcis clavier — les touches qui accélèrent l'édition.