Saltar al contenido principal

Primer script

Escribe un pequeño script que hace girar un objeto cuando lo tocas. Después, añade ajustes al inspector y descubre qué puede usar un script.

Un script es un archivo de TypeScript que añades a un objeto. Se ejecuta en el reproductor. Cada objeto que usa el script tiene su propia copia.

Este es un script completo. Toca el objeto y empieza a girar:

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 () => {
// opcional: limpia lo necesario cuando este objeto desaparece
};
});

El script tiene cuatro partes:

ParteQué hace
initArranca el script y te da ctx, tu acceso a la escena en marcha. Todo empieza aquí.
definePropsAñade ajustes al inspector. Los rellenas en cada objeto.
ctx.onReacciona a un disparador, como un toque o una colisión.
ctx.tickSe ejecuta en cada fotograma. dt es el tiempo desde el fotograma anterior, en segundos.

init y defineProps están siempre disponibles. Nunca tienes que importarlos. Las clases de componente, como TransformComponent, vienen de @was/engine.

Lee y cambia objetos​

const transform = ctx.entity.getComponent(TransformComponent);

transform.position; // lee un valor
transform.$data; // lee todo como un objeto simple
transform.update({ … }); // escribe

Hay dos errores en los que cae casi todo el mundo.

Al cambiar un valor anidado se borra el resto

update conserva los campos que no pasas, como rotation. Pero un valor que pasas sustituye entero al anterior. Así que update({ position: { y: 2 } }) también devuelve x y z a su valor por defecto, cero.

Copia con ... los valores que quieras conservar:

transform.update({ position: { ...transform.$data.position, y: 2 } });
Un material es una lista de slots

material.update({ color }) no hace nada, porque color está dentro de un slot. Cambia el slot:

material.update({
materials: [{ ...material.$data.materials[0], color: '#ff0000' }],
});

Nunca cambies $data directamente. Parece que funciona, pero el cambio se pierde.

Ajustes en el inspector​

Con defineProps, puedes poner un mismo script en diez objetos y configurarlo de forma distinta en cada uno. Sin copiar y pegar.

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' },
});
TipoEl editor muestraTu script recibe
numberun campo o un deslizadorun número
stringun campo de textouna cadena
booleanun interruptorun booleano
colorun selector de coloruna cadena con el color
selectun desplegableuna de tus opciones
entityun selector de objetosel propio objeto, listo para usar
sceneun selector de escenasel objeto de la escena
resourceun selector de recursosuna referencia que puedes pasar a otras funciones
arrayuna listaun array
groupun bloque con títuloun objeto anidado

Opciones útiles:

  • label y help: el texto que se muestra en el panel.
  • min y max: límites estrictos.
  • softMax: dónde termina el deslizador. Se puede seguir escribiendo un número mayor.
  • showWhen: muestra un campo solo cuando otro campo tiene un valor concreto.
  • group: mantiene ordenada una lista larga de ajustes.
Escribe defineProps completo

Escribe tus ajustes directamente dentro de la llamada, o el inspector no podrá mostrarlos. No los construyas a partir de variables ni los devuelvas desde una función.

Tu script siempre recibe el valor actual: props.speed vale lo que muestre el inspector en ese momento.

Importaciones​

import { TransformComponent } from '@was/engine';
import helpers from 'Scripts/helpers';

Puedes importar las clases de componente del motor. También puedes importar otros recursos por su ruta:

  • otro script te da lo que exporta;
  • un patch te da su código, el mismo que muestra el panel Code;
  • cualquier otra cosa te da una referencia, { id, type }. Pásala a ctx.spawn, o pasa su id a ctx.audio.play.

Si cambias el nombre de un recurso o lo mueves, estas importaciones se actualizan solas.

Lo que un script no puede usar​

Un script pesado no puede ralentizar la imagen. En los scripts no están disponibles:

  • window, document, el DOM;
  • fetch, localStorage ni ningún acceso a la red;
  • las bibliotecas de renderizado: cambias la escena a través de los componentes, no dibujando;
  • los temporizadores del navegador: usa ctx.tick.

En su lugar, usa esto:

  • para la interfaz, tarjetas de interfaz;
  • para el sonido, ctx.audio;
  • para guardar valores, ctx.store y las variables globales.

Cuándo empieza y termina un script​

Un script empieza cuando su objeto está activo en pantalla: no hay nada desactivado por encima de él y su escena está activa. Termina cuando eso deja de cumplirse.

on-launch se dispara en cuanto el script empieza, así que nunca te lo pierdes.

La función que devuelves desde init se ejecuta cuando el script termina. Úsala para limpiar: cancelar suscripciones, parar sonidos, borrar el estado.

Los scripts no se ejecutan en el editor

El editor muestra tu escena, pero no ejecuta ninguna lógica. Edita el script y abre la vista previa (Preview). No hay recarga en caliente: una experiencia en marcha recoge tus cambios la próxima vez que se reinicia.


Siguiente: Dónde van los scripts: en qué nivel añadir un script.