Saltar al contenido principal

La API de ctx

ctx es la forma en que tu script trabaja con la escena en marcha. En esta página tienes todo lo que puede hacer.

init le pasa ctx a tu script. La API es pequeña a propósito. Si no encuentras algo aquí, busca un paso integrado que lo haga.

Dónde estás​

NombreQué es
ctx.entityel objeto al que está añadido este script
ctx.scenela escena en la que está el objeto
ctx.spaceel espacio: todo el mundo en el que está el visitante

Fotogramas y disparadores​

ctx.tick((dt, t) => {});          // en cada fotograma; dt y t en segundos
ctx.effect(() => {}); // vuelve a ejecutarse cuando cambia lo que lee; puede devolver una limpieza
ctx.on(trigger, (payload) => {}); // reacciona a un disparador
ctx.emit(trigger, payload); // envía un disparador desde este objeto

La mayoría de los disparadores tienen los mismos nombres que en los eventos. Tres disparadores de entrada con ramas llegan a un script como disparadores separados:

  • Key (on-key) llega como on-keydown y on-keyup;
  • Press on the object (on-mouse) llega como on-mousedown, on-mouseup y on-click;
  • Hover (on-hover) llega como on-hover y on-blur.

Los disparadores de tiempo, distancia, visibilidad, señales y variables solo funcionan en los eventos. En un script, usa en su lugar ctx.tick, ctx.subscribeGlobal y ctx.handleMessage.

Estos disparadores te dan datos útiles:

DisparadorQué recibes
on-keydown · on-keyup{ code, ctrl, shift, alt, meta }
on-state-active · on-state-inactive{ stateId }
on-collide{ other }: con qué has chocado
on-divkit-action{ id, … }: qué botón
on-game-control{ state }: quieto, moverse, correr o saltar
on-drag · on-pinch · on-rotate{ dx, dy } · { scale } · { angle }
on-vps-localizeddónde resultó estar el visitante

on-launch llega en cuanto empieza tu script, así que no te lo puedes perder.

Busca objetos​

ctx.get(id);
ctx.findByName('Door');
ctx.find(LightComponent); // el primer objeto con estos componentes
ctx.query(LightComponent, TagsComponent); // todos
ctx.all();

Crea y elimina objetos​

ctx.create({ name: 'Bullet', parent, components: [] });
ctx.spawn(props.bulletModel, { parent });
ctx.destroy(entity);

spawn es la forma fácil. Dale un recurso y añade por ti los componentes adecuados:

  • un modelo se convierte en un objeto de modelo;
  • una imagen se convierte en un plano con la imagen encima;
  • un sonido se convierte en una fuente de audio.

Ejecuta pasos integrados​

ctx.step('set_visibility', { visible: false }, { targets: [enemy] });

ctx.startTransition({ durationMs: 400, easing: 'ease-out' }, () => {
// los cambios que hagas aquí se animan de forma suave en lugar de saltar
});

targets elige los objetos sobre los que actúa el paso, como el campo Target object de un evento. Sin él, el paso actúa sobre el objeto de este script.

Usa un paso antes de programarlo tú

ctx.step ejecuta cualquiera de los pasos integrados, los mismos que usan tus eventos. Las animaciones, los cambios de estado y las transiciones de escena están a una sola llamada.

Lleva al visitante a otra escena o a otro espacio:

ctx.openScene(sceneOrId);
await ctx.openSpace(spaceRefOrId);
ctx.scenes();

Entrada​

Hay dos formas de leer la entrada.

Las acciones con nombre usan las combinaciones de teclas del proyecto. Si un visitante cambia sus teclas, tu script sigue funcionando:

ctx.input.pressed('jump');
ctx.input.justPressed('fire');
ctx.input.axis('moveX');

Las teclas directas leen el teclado sin intermediarios. Una pulsación dura exactamente un fotograma, así que léela dentro de ctx.tick:

ctx.keyboard.down('KeyW');
ctx.keyboard.press('Space');
ctx.keyboard.press('ArrowLeft', { every: 200 }); // repetición automática cada 200 ms
ctx.keyboard.axis('KeyA', 'KeyD'); // -1, 0 o 1
Las teclas modificadoras funcionan distinto en cada caso

Una acción con nombre ignora los modificadores que no menciona, así que mantener Shift pulsado para esprintar no detiene «avanzar». Con un disparador de tecla pasa lo contrario: un modificador que no forma parte de su tecla no puede estar pulsado.

Cuando la ventana pierde el foco, se sueltan todas las teclas pulsadas, así que ninguna se queda atascada.

Cámara​

ctx.camera.entity();           // el objeto de la cámara activa
ctx.camera.setActive(target); // cambia de cámara; null vuelve a la de por defecto
ctx.camera.pose(); // { position, rotation, forward } en coordenadas del mundo

El transform de la cámara funciona igual en todos los modos de control. Escribe en él para mover la cámara. Léelo para ver dónde han puesto la cámara los controles.

En los modos orbit y firstPerson, los controles fijan la rotación, así que la rotación que escribas se sustituye. La posición que escribas sí se mantiene.

Rayos​

Un rayo es una línea que sale de un punto en una dirección. Lanza uno para saber qué hay a lo largo de esa línea.

await ctx.raycast();                                       // desde el centro de la cámara
await ctx.raycast({ screen: { x: 0.5, y: 0 }, all: true }); // un punto de la pantalla, todos los impactos
await ctx.raycast({ origin, direction }); // cualquier rayo que quieras
await ctx.raycast({ from: entity }); // desde un objeto, hacia donde mira

Cada impacto te da el objeto, la distancia, y el punto y la normal de la superficie en coordenadas del mundo. Los impactos se ordenan del más cercano al más lejano, uno por objeto.

Usa await

La respuesta llega en el siguiente fotograma.

Los objetos invisibles se saltan, igual que los objetos marcados para ignorarse, como las retículas y los gizmos. Recibes lo que hay detrás de ellos en lugar de un fallo.

Física​

ctx.physics.applyImpulse(target, { x: 0, y: 5, z: 0 });
ctx.physics.applyForce(target, vec, point);
ctx.physics.setVelocity(target, vec);
ctx.physics.teleport(target, position, { rotation, keepVelocity });
ctx.physics.setGravity(vec);

ctx.physics.getSpeed(target);
ctx.physics.isSleeping(target);

await ctx.physics.raycast(from, to, { skip: [ctx.entity] });
Cambiar el transform de un cuerpo dinámico no hace nada

La física controla dónde está. Usa teleport para colocarlo, e impulsos o fuerzas para moverlo.

Sáltate tu propio cuerpo

Cuando lances un rayo desde dentro de tu propio cuerpo, pasa skip. Si no, el rayo chocará siempre con tu propio cuerpo.

Las lecturas van más o menos un fotograma por detrás. Sirven para saber si te estás moviendo, pero no para cálculos exactos.

Audio​

ctx.audio.play(props.hitSound.id, { at: enemy, volume: 0.6, positional: true });

play recibe el id del sonido. Para un ajuste de sonido de defineProps, es props.hitSound.id.

Cada llamada reproduce su propio sonido, así que los pasos, los golpes y los disparos nunca se cortan entre sí. El componente de audio reproduce un sonido cada vez y se corta a sí mismo, así que no lo uses para efectos.

Guarda y comparte valores​

Hay tres formas de guardar valores y compartirlos con otros scripts:

// 1. los valores propios de este script
const store = ctx.store('game', { score: { type: 'number', default: 0 } });
store.set('score', (v) => v + 1);
store.subscribe('score', (v) => {});

// 2. globales: compartidas con todos los scripts, patches y eventos
ctx.setGlobal('level', 3);
ctx.getGlobal('level');
ctx.subscribeGlobal('level', (v) => {});

// 3. mensajes entre scripts
ctx.postMessage('enemy-died', { id });
ctx.handleMessage('enemy-died', ({ id }) => {});
Guardado enTras cambiar de escenaTras pasar a otro espacioTras recargar
storesínono
globalessísíno
globales limitadas al espaciosíno: cada espacio guarda las suyas, a propósitono
Al cerrar la pestaña se borra todo

Nada de esto se guarda entre visitas. Si necesitas un valor la próxima vez, envíalo tú a algún sitio mientras el visitante sigue conectado.

Plugins​

Lee y cambia el componente personalizado de un plugin, y usa los disparadores y pasos que añade un plugin:

const health = ctx.plugin('acme.health/health');   // '<plugin id>/<component id>'
for (const e of health.entities()) {
const max = health.props(e)!.max as number;
health.setState(e, { hp: max });
}

ctx.on('acme.patrol:reached', ({ point }) => {}); // un disparador de un plugin
ctx.step('acme.patrol:pause', { seconds: 2 }); // un paso de un plugin

ctx.plugin tiene entities(), has(e), props(e), state(e), setState, setProps, setEnabled, add y remove. Sus tipos vienen del plugin, así que tu editor de código conoce los campos. Consulta Código de ejecución.

Interfaz​

Lee y cambia valores de una tarjeta de interfaz, y reacciona a sus botones:

const ui = ctx.getDivKit(entity);

ui.get('score');
ui.set('score', (v) => v + 1);
ui.subscribe('lives', (v) => {});
ui.onAction('restart', () => {});

Consulta Tarjetas de interfaz.


Siguiente: Recetas: scripts completos para copiar.