Tu primer script
Un script es un archivo de TypeScript que adjuntas a un objeto. Corre en el reproductor, una vez por cada objeto que lo use.
Aquí tienes uno completo: tocas el objeto y empieza a girar.
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: limpiar cuando este objeto desaparezca
};
});
Aquí pasan cuatro cosas:
init | recibe ctx, tu asa sobre la escena en marcha. Todo empieza aquí |
defineProps | declara los ajustes que el editor muestra en el inspector, rellenados por objeto |
ctx.on | se suscribe a un disparador: los mismos que usan eventos y patches |
ctx.tick | corre en cada fotograma, con dt en segundos |
init y defineProps están siempre disponibles: nunca los importas.
Leer y escribir objetos
const transform = ctx.entity.getComponent(TransformComponent);
transform.position; // leer un valor
transform.$data; // leer todo como objeto llano
transform.update({ … }); // escribir
Dos trampas, y son las dos que pillan a todo el mundo:
update({ position: { y: 2 } }) también pone x y z a cero: le has entregado una posición
completamente nueva con solo y rellenado.
Expande lo que quieras conservar:
transform.update({ position: { ...transform.$data.position, y: 2 } });
material.update({ color }) no hace absolutamente nada, porque color vive dentro de un slot y no
en el nivel superior. Escribe el slot:
material.update({
materials: [{ ...material.$data.materials[0], color: '#ff0000' }],
});
Y nunca asignes directamente dentro de $data. Parece que funcionó, y el cambio se descarta.
Ajustes en el inspector
defineProps es lo que hace que merezca la pena reutilizar un script: el mismo script en diez
objetos, configurado distinto 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' },
});
| Tipo | El editor muestra | Tu script recibe |
|---|---|---|
number | un campo o un deslizador | un número |
string | un campo de texto | una cadena |
boolean | un interruptor | un booleano |
color | un selector de color | una cadena de color |
select | un desplegable | una de tus opciones |
entity | un selector de objeto | el objeto en sí, listo para usar |
scene | un selector de escena | el objeto escena |
resource | un selector de recurso | una referencia que puedes pasar |
array | una lista | un array |
group | un bloque con título | un objeto anidado |
Conviene saber: label y help para el panel, min y max para límites de verdad, softMax
para dónde termina un deslizador sin prohibir valores mayores escritos a mano, showWhen para
revelar un campo solo cuando otro tiene cierto valor, y group para mantener ordenada una lista
larga.
defineProps hay que escribirlo literalmenteEl editor lee tus ajustes sin ejecutar el script, así que necesita verlos directamente: ni construidos a partir de variables ni devueltos por una función.
Los valores se leen frescos cada vez, así que props.speed siempre refleja lo que hay en el
inspector en ese momento.
Importar cosas
import { TransformComponent } from '@was/engine';
import helpers from 'Scripts/helpers';
Puedes importar las clases de componente del motor y otros recursos por su ruta: otro script te
da sus exports, un patch te da su módulo compilado, y cualquier otra cosa te da una referencia que
puedes pasar a ctx.spawn o ctx.audio.play.
Renombra o mueve un recurso y estos imports se actualizan solos.
Qué te da el sandbox, y qué no
Los scripts corren aislados de la página, lo que impide que un script pesado atasque el renderizado. Así que no están disponibles:
window,document, el DOM;fetch,localStorage, ningún tipo de acceso a red;- ninguna biblioteca de renderizado: cambias la escena a través de componentes, no dibujando;
- los temporizadores del navegador: usa
ctx.tick.
Qué usas en su lugar: tarjetas de interfaz para la interfaz,
ctx.audio para el sonido, ctx.store y los globals para guardar cosas.
Cuándo arranca y se detiene tu script
Una instancia se crea cuando su objeto está vivo y en pantalla —nada desactivado por encima, su escena activa— y se destruye cuando eso deja de ser cierto.
on-launch se dispara en cuanto se crea, así que nunca te lo pierdes. La función que devuelves
desde init es tu limpieza: desuscribirte, parar sonidos, vaciar el estado.
El editor dibuja tu escena pero no ejecuta la lógica. Edita y luego abre Preview. Una experiencia en marcha recoge un script editado en su siguiente reinicio.
Siguiente: Dónde vive un script