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
| Nombre | Qué es |
|---|---|
ctx.entity | el objeto al que está añadido este script |
ctx.scene | la escena en la que está el objeto |
ctx.space | el 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 comoon-keydownyon-keyup; - Press on the object (
on-mouse) llega comoon-mousedown,on-mouseupyon-click; - Hover (
on-hover) llega comoon-hoveryon-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:
| Disparador | Qué 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-localized | dó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.
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.
Navegación
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
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.
awaitLa 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] });
La física controla dónde está. Usa teleport para colocarlo, e impulsos o fuerzas para moverlo.
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 en | Tras cambiar de escena | Tras pasar a otro espacio | Tras recargar |
|---|---|---|---|
store | sí | no | no |
| globales | sí | sí | no |
| globales limitadas al espacio | sí | no: cada espacio guarda las suyas, a propósito | no |
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.