Ignitor Docs ← getignitor.com
🇺🇸 EN 🇪🇸 ES

Orquestador de Escenas#

Una escena acá es un clip cinemático horneado a un flipbook de PNG — un mp4 importado una vez (ffmpeg lo extrae cuadro a cuadro en el momento de importar) y reproducido como una secuencia plana de imágenes, exactamente igual que una animación de personaje. No es lo mismo que una cinemática (core/cutscene.js, autorada por el Editor de Cinemáticas): una cinemática es una lista ordenada de pasos escriptados del motor — caminatas de NPC, líneas de SAY, esperas — delimitada a una sola sala. El paso PLAYSCENE de una cinemática reproduce una escena del Orquestador de Escenas como uno de sus pasos y bloquea hasta que termina; las dos herramientas editan dos formas de datos distintas y no son intercambiables. No hay ningún códec de video en el runtime — por diseño (ver la nota de cierre) — una escena es solo cuadros PNG avanzando sobre un reloj, así que se reproduce igual en cada plataforma que el motor soporta, incluidos los ports que no pueden depender de <video>.

Llegas a él desde el Hub (con el dev server corriendo). La barra de herramientas tiene un desplegable Escena, + Nuevo, Guardar (Ctrl+S, escribe el descriptor y lo registra), un gestor de Clips, una herramienta Replace Color (cuentagotas + tolerancia/mezcla, hornea un PNG recoloreado para un clip), Deshacer/Rehacer, y GC (barre los recursos de escena sin usar a una carpeta de basura reversible). La columna izquierda es una vista previa (con paneo/zoom y controles de transporte) sobre una línea de tiempo de pistas; el panel derecho muestra propiedades de lo que esté seleccionado — pista, keyframe o cue.

La pantalla del Orquestador de Escenas — la línea de tiempo tipo flipbook de un clip cinemático.
Orquestador de Escenas — línea de tiempo tipo flipbook de un clip.

Límites: cuánto video cabe en una escena#

Una escena reproduce cuadros PNG, y el player decodifica todos los cuadros de todos los clips de la escena antes de arrancarla: eso es lo que hace que se vea igual en todas las plataformas, y también es el techo. El presupuesto que importa no es el tamaño del mp4 sino cuadros × ancho × alto × 4 bytes ya decodificados. El importador muestra esa cifra junto a la estimación de disco y la usa como compuerta: ámbar arriba de 1 GB (carga, pero lento, y una máquina modesta puede no lograrlo), rojo arriba de 3 GB (el import se rechaza: baja los fps o la escala, o recorta). El mismo límite se aplica durante el import mismo, así que no hay forma de colar un clip.

En segundos, con los tamaños de cuadro y las tasas que más vas a usar:

Tamaño de cuadro fps cómodo (≤ 1 GB) límite duro (3 GB)
1920 × 1080 24 ~5 s ~16 s
1920 × 1080 12 ~11 s ~32 s
960 × 540 24 ~22 s ~65 s
960 × 540 12 ~43 s ~2 min

Las escenas son para piezas cinemáticas cortas: un logo, una title card, unos segundos de movimiento. Lo más largo va en un editor de video, recortado a las piezas que de verdad necesitas.

Cómo grabar para importar (OBS o cualquier grabador de pantalla):

  • Resolución = la de tu proyecto (1920 × 1080 para un proyecto 1080p), nunca mayor. Un cuadro más grande se escala al dibujar de todos modos; solo cuesta disco, RAM y tiempo de import. Si le pasas una grabación 4K, el importador baja la escala solo para ajustar al ancho del proyecto.
  • 24–30 fps. A 60 fps duplicas los cuadros sin ganancia visible en un flipbook. El importador remuestrea a los fps que elijas, así que una fuente a 60 funciona, solo decodifica el doble de cuadros.
  • El bitrate casi no importa. Cada cuadro se decodifica completo al importar, así que un master de alta calidad no cuesta nada extra; lo que pesa es la cantidad y el tamaño de los PNG en que se convierte.
  • Clips de segundos, no de minutos, y recorta en el importador (desde + cantidad) cuando la grabación trae más que la toma que quieres.

Anatomía de una escena#

Un descriptor de escena ({ id, duration, bg?, endMode?, keepMusic?, onEnd?, tracks: [...] }) es un objeto de datos puro — serializado como un pequeño módulo ES y guardado en projects/<id>/scenes/<sceneId>.js. Guardar también agrega, de forma idempotente, una entrada a core/scenes/index.js, pero ese archivo es solo un mapa chico id→descriptor; en la práctica el runtime nunca necesita ese registro hecho al guardar, porque _ensureScene() de shell/main.js importa dinámicamente el descriptor de forma perezosa en el primer PLAYSCENE/REWINDSCENE y lo registra ahí — el core queda totalmente desacoplado de las rutas del proyecto.

Una escena tiene cinco tipos de pista, todas compartiendo una sola línea de tiempo:

  • Pistas de sprite (la opción por defecto, sin kind) — un clip (un Frame[] con nombre, del registro de clips del proyecto) más una lista de keyframes ({ t, x, y, scale, rot, alpha, ease }) que maneja una curva de transformación independiente encima de la reproducción cuadro a cuadro propia del clip. in/out acotan cuándo la pista es visible en el tiempo de la escena; z ordena las capas; anchor y un preset opcional de fx (un filtro visual por clip, resuelto a un filtro real solo en el shell) completan el cuadro. Acá corren dos relojes en paralelo: la línea de tiempo maestra (keyframes, navegable, con ease) y el reloj interno del clip (ciclo de cuadros, reusando exactamente el mismo primitivo tickAnim() que usan las animaciones de personaje). El registro en sí (scenes/clips.js) es un Frame[] plano por clip ({ sprite, ms } por cuadro). Una entrada con cualquier otra forma aparece como omitida en el gestor de Clips, con su id, y se vuelve a escribir intacta cuando guardas — nunca se pierde.
  • Pistas de audio (kind: 'audio') — un clip de audio posicionado y recortable (src, at, trimStart/trimEnd, vol, fadeIn/fadeOut, muted). Cuando el reloj maestro cruza at, dispara un único token, disparado por flanco, SCENEAUDIO:<src>|<trimStart>|<dur>|<vol>|<fadeIn>|<fadeOut>; el shell lo reproduce como un one-shot en el bus de música (GameAudio.playMusic), detenido automáticamente (con el propio fade-out del clip) cuando termina la escena — salvo que la escena tenga keepMusic (ver Cómo termina una escena).
  • Pistas de cue (kind: 'cue') — una lista de pares { t, token }: tokens de efecto arbitrarios disparados en el instante en que el reloj maestro llega a t. Así es como una escena dispara cualquier cosa a mitad de reproducción — una flag, un sonido, un warp de sala — sin esperar a que la escena termine.
  • Pistas de FX (kind: 'fx') — un preset de filtro de pantalla completa (glitch/blur/etc.) con su propia ventana in/out y opacity, dibujado sobre todo el cuadro en vez de como un sprite posicionado.
  • Pistas de texto (kind: 'text') — un rótulo o título en pantalla. Como una pista de sprite lleva una lista de keyframes (posición, escala, rotación, alpha en el tiempo, con easing por segmento) y un z, pero en vez de un clip dibuja una línea de texto con un lote completo de estilo: fuente y tamaño, color, alineación, negrita / cursiva / subrayado / tachado, un contorno (outlineWidth), una sombra, y lineHeight/letterSpacing. El texto en sí se autora monolingüe y va respaldado por un lid, así el Editor de Traducción completa los demás idiomas después — la misma regla content-i18n que las líneas habladas y los nombres de objeto. Se renderiza idéntico en el preview del editor, la exportación MP4 y en el juego (el core lleva la forma; el shell resuelve el lid y el font).

Edición en la línea de tiempo#

Dos herramientas de puntero viven en la barra. ➤ Seleccionar (V) es la default — haz clic en una pista o clip para seleccionarlo, arrastra para moverlo por su carril. ✂ Cortar (C) parte un clip en el marcador del cabezal: haz clic en cualquier clip y se vuelve dos clips consecutivos. Cada mitad reinicia su propia reproducción desde el frame 0, así que un corte es una partición genuina en dos clips independientes, no un recorte de uno.

Las pistas se reordenan verticalmente: agarra el encabezado de una pista y arrástralo arriba o abajo a un nuevo lugar, y el editor renumera el z según el orden resultante de arriba hacia abajo — lo que ves apilado en la línea de tiempo es el orden de pintado en pantalla. El propio clip de una pista de sprite se puede arrastrar verticalmente fuera de su carril con el mismo efecto.

Cuando dos clips de audio se solapan en el tiempo, aparece un helper ⤬ crossfade en las propiedades del clip seleccionado: un clic fija el fadeOut del clip anterior y el fadeIn del posterior al largo del solape, convirtiéndolo en un crossfade estilo DAW hecho con los campos de fade que el runtime ya reproduce — sin tipo de pista nuevo, sin datos especiales.

El pipeline de tokens de efecto (una sola gramática, un solo despacho)#

Tanto los tokens de cue como la lista onEnd de la escena son tokens de efecto comunes del mismo manifiesto compartido que usa cualquier otro sistema (reglas, diálogos, cinemáticas) — autorados con el mismo widget de picker tipado, nunca texto libre para un argumento que porte un ID. La lista de verbos que se muestra acá está curada para el contexto de escena: oculta tokens que solo tienen sentido dentro de la lógica de sala en vivo (scroll de inventario, restaurar hotspot, guardar/cargar, recoger ítem) y promueve arriba los verbos de warp de sala / audio / flag / diálogo / cinemática / escena; los pickers de cue además promueven primero el cuarteto de audio (SOUND/MUSIC/STOPMUSIC/PLAYFX), ya que un cue a mitad de escena casi siempre es un golpe de audio.

onEnd guarda una lista, y sus filas llevan un asa — arrastra una para cambiar el orden en que se disparan los tokens al terminar la escena. Un cue no tiene asa a propósito: guarda una sola ranura de token, así que no hay nada que reordenar; a un cue lo mueves en el tiempo arrastrándolo por su pista.

WAIT y WAITFOR quedan afuera por otro motivo: una escena es una línea de tiempo, así que sus beats se espacian moviéndolos por la pista, no pausando entre tokens. Cuando la demora pertenece al juego en vivo —una línea hablada que necesita un momento antes de que cambie la sala— eso es una cadena de reacción, y ahí las dos esperas sí están disponibles.

En runtime, el tickScene() puro de core/scenes/timeline.js devuelve cada cuadro un arreglo fired con los tokens que vencieron; shell/main.js los pasa por el mismo despacho applyEffects() de dos niveles que se usa en todo el motor — los tokens de Nivel 1 (SETFLAG, GIVEITEM, WARPTOROOM, etc.) mutan el estado directamente, y lo que sobra (SCENEAUDIO, SAY, MUSIC) cae al despachador de acciones genérico. Un solo almacén de reglas, una sola gramática, sin un intérprete separado solo para escenas.

Cómo termina una escena#

La ventana in/out de cada pista es inclusiva en out: una pista sin out propio hereda scene.duration y se ve hasta el último milisegundo. Cortar a negro antes del final es decisión del autor — para eso está el out de esa pista. (Antes, el frame que caía justo en duration cullaba todas las pistas, así que el último frame de cualquier escena salía negro.)

Dos campos a nivel escena deciden qué pasa cuando el reloj llega a duration:

  • endMode'end' (por defecto) termina la escena y devuelve el control al juego. 'hold' la deja estacionada ahí: el reloj maestro sigue corriendo, así que transformAt sostiene el último keyframe y los clips en loop siguen ciclando — la toma final queda viva en vez de congelarse en una foto. Una escena en hold termina solo cuando el jugador la saltea (el mismo gesto de mantener presionado, o Escape), y onEnd se difiere a ese momento: dispararlo al cruzar duration ejecutaría justo el WARPTOROOM / avance de estructura que saca al jugador de la toma que pediste sostener. Es la escena de créditos clásica: un título volando, música de fondo, hasta que el jugador decide salir.
  • keepMusic — por defecto la música de la escena (una pista de audio o un cue MUSIC: — las dos van al bus de música) se corta al salir. Con este campo sobrevive a lo que venga después: el menú, el siguiente bloque de la estructura, el cuarto que estaba atrás.

Ambos se editan en el panel de la escena y solo se serializan cuando difieren del default, así que una escena que termina de la forma habitual queda byte a byte igual que antes.

Reproducción: saltar y rebobinar#

El jugador puede hacer clic para saltar un paso y mantener presionado para saltar toda una escena: mantener presionado llena una barra de progreso de ~1.1s, y al completarse el reloj de la escena se adelanta directo a duration para que el siguiente tick cruce el final de forma natural y dispare onEnd — el runtime nunca anula la escena activa directamente, así que onEnd no puede quedar varado por un salto. En una escena con endMode: 'hold', el salto completado termina la escena directamente (adelantar el reloj solo volvería a entrar al hold).

El aviso de todo esto ya no aparece encima del primer frame: sale recién con la primera tecla o clic del jugador durante la escena. Una escena en hold además lo muestra sola tras unos segundos estacionada en el final, para que nadie se quede en los créditos sin saber cómo salir.

REWINDSCENE:<sceneId>[|speed=N] (velocidad por defecto 2) reproduce una escena hacia atrás con una estética VHS, manejada por el seekScene() puro — un render sin efectos secundarios en un tiempo absoluto y decreciente. Esto es solo visual: seekScene no dispara ningún token de cue, audio, u onEnd mientras rebobina, por construcción (nunca toca sceneState ni el arreglo fired). No hay UI de autoría para esto en el propio Orquestador de Escenas — es un token a nivel de runtime, alcanzable desde el slot de efecto raw de una cinemática (la vía de escape del Editor de Cinemáticas) o desde cualquier otro lugar donde se pueda despachar un token, no es una pista o tipo de paso propio acá.

Audio mientras corre una escena#

Una escena es dueña del paisaje sonoro: arrancar una agacha (duck) de inmediato el ambiente de la sala (antes incluso de que termine la carga asíncrona del clip, así nada se cuela debajo de una escena durante el hueco de carga), y el ambiente se reanuda apenas la escena termina — incluidas las salidas anormales (un rebobinado que termina, o una carga fallida). Las propias pistas de audio de una escena viajan por el bus de música compartido vía SCENEAUDIO y no se ven afectadas por el ducking, ya que son parte intencional de la escena, no de la sala.

Importar un clip desde video#

Clips → NUEVO CLIP DESDE VIDEO elige un mp4 y lo extrae en un modo escena que se salta los pasos exclusivos de personaje (limpieza de chroma key, anclaje al lienzo del personaje) y escribe los cuadros completos en assets/scenes/<clip>/. Perillas: fps (remuestreo — una cinemática estilizada suele vivir en 12–15 fps), escala (1× / 0.75× / 0.5× — preajustada para caber en el ancho de tu proyecto cuando la fuente es más ancha), y un recorte de entrada/salida (desde + cantidad). Tres presets (Ligero 12fps·0.5×, Equilibrado 15fps·0.75×, Calidad 24fps·1×) fijan las dos primeras de un golpe.

Preview frame 0 hace más que mostrar una miniatura: muestrea un puñado de cuadros repartidos por el clip para medir cuánto pesan de verdad los PNG (un primer cuadro oscuro, solo, puede errar por una cuarta parte), y lee el tamaño, los fps y la duración del clip. Desde ahí, la línea bajo los botones muestra los dos costos en vivo mientras mueves las perillas: disco (≈ MB, cuadros × bytes por cuadro) y RAM al reproducir la escena (la cifra decodificada de la que hablan los límites de arriba), y se pone ámbar o roja con la compuerta.

El import corre junto a su destino: los cuadros se extraen directo a una carpeta de trabajo al lado de assets/scenes/<clip>/, en el disco del proyecto (nunca en la carpeta temporal del sistema), la barra de progreso sigue a ffmpeg cuadro por cuadro, y al final la carpeta terminada se pone en su lugar. Antes de arrancar, el importador compara el espacio libre de ese disco con la estimación y se niega de entrada en vez de fallar a la mitad. Las fuentes opacas se escriben como RGB; el alpha se conserva solo cuando el video de verdad lo trae. Al importar, el clip nuevo se agrega al registro de clips del proyecto (persistido la próxima vez que guardes desde el gestor de Clips). El ffmpeg que se usa es el que apuntaste en Hub Config, o el de tu PATH.

Flujo de trabajo#

  1. Elige una Escena existente o + Nuevo para arrancar una en blanco.
  2. Clips → NUEVO CLIP DESDE VIDEO para traer metraje cinemático (o reusa un clip existente), luego +🎬 / +🔊 / +✨ en la línea de tiempo para agregar una pista de sprite, audio, o FX de pantalla completa — más Agregar texto / Agregar trigger desde el panel de agregar pista para un rótulo o una pista de cue.
  3. Para una pista de sprite, coloca keyframes en la línea de tiempo para mover/escalar/ rotar/desvanecerla en el tiempo, con ease por segmento; arrastra sus manijas in/out para acotar su visibilidad.
  4. Agrega tokens de cue en momentos específicos para golpes a mitad de escena (sonido, flags, warps); define la lista onEnd de la escena para lo que pasa cuando termina.
  5. Previsualiza con los controles de transporte (reproducir/pausar, rebobinar/avanzar al final, detener) y la línea de tiempo con zoom; usa Replace Color si necesitas una variante recoloreada de un PNG importado.
  6. Guardar — escribe projects/<id>/scenes/<sceneId>.js y registra el id.
  7. Cabléalo desde un token PLAYSCENE:<id> (o REWINDSCENE:<id>) en cualquier lugar donde se pueda disparar un token — lo más común: un paso PLAYSCENE del Editor de Cinemáticas, una reacción de hotspot, o un bloque scene del Diseñador de Estructura del Juego.

Los tiempos de cue y keyframe de una escena quedan horneados contra la cantidad de cuadros y el fps del clip importado en el momento de la importación. Cada t en un cue, un keyframe, o una ventana in/out es un offset absoluto en milisegundos dentro del reloj maestro de la escena — no queda ninguna relación de vuelta al mp4 original una vez que los cuadros están extraídos. Volver a importar una versión recortada o re-codificada del mismo metraje (fps distinto, puntos de recorte distintos) produce una secuencia de cuadros nueva cuyo tiempo ya no coincide con los cues y keyframes autorados contra la anterior — van a disparar antes, después, o sobre cuadros en blanco. Trata un clip importado como definitivo antes de gastar tiempo colocando cues y keyframes contra él; si tienes que reimportar, espera tener que revisar cada cue y keyframe de las pistas que usan ese clip.