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.
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.
| Fonction | Ce 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éthode | Ce 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>, commeacme.patrol:reached. - Les
paramsutilisent les types de champs dedefineProps:number,string,boolean,color,select,entityetresource. 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 valeurany, 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 formectx.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, avecctx.step('acme.patrol:pause', { seconds: 2 }). Abonnez-vous une seule fois, et appelez la fonction renvoyée dansdispose().- 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()eteditor.pluginSettings.set({ tempo: 2 }). Cela nécessite la permissionsettings: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.