Diseñador de Estructura de Juego#
El Diseñador de Estructura de Juego organiza un juego como una secuencia ordenada de arriba
hacia abajo de bloques — cinemáticas, capítulos jugables, y pantallas de menú — y la guarda en
el structure.json del proyecto. El archivo es completamente opcional: sin structure.json
(o con su flag enabled apagado), el juego arranca de la forma clásica, directo al cuarto de
initialPlayer, y esta herramienta solo sirve para organizar checkpoints de desarrollo. Prende el
interruptor de arranque y la estructura toma el control del flujo en su lugar, reproduciendo los
bloques en orden desde el primero.
Llegas a él desde el Hub (con el dev server corriendo). El interruptor drive boot de la
barra de herramientas mapea directo a structure.enabled; el panel de abajo lista cada bloque
como una tarjeta, en orden de reproducción.
Anatomía de un bloque#
Todo bloque tiene un id, un type, y un name. Los tres tipos:
- scene — reproduce una cinemática. Su campo scene se elige de las escenas del proyecto y tiene enlace al Orquestador de Escenas. El runner arranca la escena y espera; cuando la escena termina, el flujo avanza automáticamente al siguiente bloque. Una escena también lleva un tipo de escena — Normal, Intro, o Créditos — para que el validador (ver abajo) pueda confirmar que el juego realmente abre y cierra bien; no afecta la reproducción.
- gui — abre un modal de menú (la pantalla de título, un menú de selección de capítulo,
cualquier cosa armada como GUI). Su campo gui elige entre los modales abribles por defecto
del motor (
inventory,save,options) más cada modal ya definido en elguis.jsonpropio del proyecto, con enlace al Editor de GUIs. A diferencia de una escena, un bloque GUI nunca avanza solo — el propio botón del menú (p. ej. "Nueva partida") tiene que disparar un token de efectoNEXTBLOCKpara mover el flujo hacia adelante. Si te olvidas de cablearlo, el jugador queda atrapado en ese menú para siempre; nada más avanza un bloque GUI. - chapter — un segmento jugable. Tiene una entry (entrada) canónica (ver abajo), un interruptor inventoryPersists, y una lista de checkpoints solo de desarrollo.
Los bloques se reordenan arrastrando su manija ⠿, y se eliminan con una confirmación previa.
Entry de capítulo vs. checkpoints — la distinción load-bearing#
Un bloque de capítulo lleva dos cosas que se ven idénticas pero significan cosas muy distintas:
- Entry (
block.entry) — el inicio real y canónico del capítulo: el estado que el runner de estructura aplica cuando el flujo llega a este bloque durante el juego normal (o un arranque dirigido por la estructura). Hay exactamente uno por capítulo. - Checkpoints (
block.checkpoints) — una lista abierta de atajos solo de desarrollo, cada uno una foto a la que puedes saltar para probar algún estado a mitad de capítulo sin volver a jugar todo lo anterior. Nunca corren durante el juego normal; solo existen para el lanzamiento de desarrollo?cp=(ver abajo).
Ambos tienen la misma forma — { control, actors } — y comparten la misma UI de tabla de
actores, pero solo entry es lo que un jugador real experimenta alguna vez.
Como el entry reemplaza la mochila entera, es él — y no el editor de items — donde vive el inventario inicial de un juego manejado por capítulos. Por eso, cuando creas el primer capítulo, su entry llega pre-cargado con cada item que el Editor de items marcó con un Starting owner; es un punto de partida que puedes editar libremente. Solo el primer capítulo (al que entra una partida nueva) y solo al crearlo — un capítulo posterior repartiendo otra vez los items de inicio sería un bug, y un entry que ya autoraste es tuyo. Los checkpoints de desarrollo nunca se siembran.
La forma {control, actors}#
- control — qué personaje está jugando el jugador en este punto; el runner hace aparecer a este personaje en su cuarto y lo vuelve el jugador activo.
- actors — una fila por cada personaje conocido, cada una con:
- active — si el personaje existe en el mundo en absoluto en este punto (el personaje de control siempre está activo, y su casilla queda bloqueada en encendido).
- room — en qué cuarto se coloca al personaje (solo configurable mientras está activo). El capítulo decide la sala; dónde para dentro de ella, y hacia dónde mira, siguen saliendo de su colocación en el Editor de Salas. Un personaje sin facing autorado mira hacia abajo.
- items — el inventario inicial del personaje en este punto, como chips de ítem. Los personajes inactivos quedan con inventario vacío y sin colocación — simplemente no están todavía en el mundo.
El cast de acá define el mundo — una colocación en el Editor de Salas sola no. Una vez que la estructura maneja el boot, la tabla de actores de un capítulo es la autoridad sobre quién existe: un personaje ausente de ella (o dejado inactivo) simplemente no está en el mundo cuando el capítulo corre, aunque lo hayas colocado en esa sala en el Editor de Salas. La colocación y la línea de tiempo están desacopladas a propósito — un capítulo decide cuándo existe un personaje — pero la trampa es que una colocación solo-en-el-Editor-de-Salas no hace nada acá.
El botón + Activar personajes colocados en cada tabla de actores (la del entry y la de cada checkpoint) salva ese hueco en un clic: trae a cada personaje que tiene una colocación en el Editor de Salas pero está inactivo en este cast, activándolo y sembrando la sala de cada uno desde esa colocación. Es aditivo y per-cast — tus actores curados nunca se sobrescriben, y nada se filtra a los otros capítulos.
Cuando el modo party está activo, aparece un badge 👥 en el entry de un capítulo como
recordatorio: los miembros del party comprometido aparecen en una sala vía sus partyAnchors sin
importar este cast, así que una fila de actor que dejaste inactiva acá igual puede aparecer en
el juego. Eso es la capa de party haciendo su trabajo, no el cast portándose mal.
inventoryPersists#
Un interruptor a nivel de capítulo, separado de las tablas de actores de entry/checkpoint. Cuando está activo, entrar al capítulo conserva el inventario actual del jugador (el flujo real y continuo entre actos) en vez de resetearlo a los ítems que autoró la tabla de actores de la entry. Solo afecta el camino de la entry (juego normal/dirigido por estructura) — un lanzamiento de checkpoint de desarrollo siempre aplica sus propios ítems autorados sin importar este flag.
Flujo de pruebas de desarrollo#
Dos botones lanzan el juego directo a un estado específico, contra el proyecto activo (se agrega
automáticamente ?p=<projectId> para que el lanzamiento no pueda arrancar silenciosamente el
proyecto equivocado). En la app de escritorio el juego abre en su propia ventana nativa, separada
del editor, así que puedes jugar la prueba y seguir trabajando en el Diseñador al mismo tiempo; en
la versión de navegador abre en una pestaña nueva en su lugar. Lanzar de nuevo mientras ya hay una
ventana de prueba abierta la cierra y la vuelve a abrir con el estado nuevo.
- ▶ Probar entrada (en la sección de entry del capítulo) guarda primero, y luego abre
/index.html?p=<id>&enter=<blockId>. Esto corre el flujo de estructura empezando en ese bloque — útil para revisar el estado real de entrada de un capítulo, o un bloque de scene/gui en contexto. - ▶ Probar aquí (en un checkpoint) guarda primero, y luego abre
/index.html?p=<id>&cp=<blockId>:<checkpointId>. Esto resuelve solo ese checkpoint y deja al jugador directo ahí, sin correr el flujo de estructura.
Ambos parámetros de URL los maneja shell/structure_runner.js y funcionan sin importar si
structure.enabled está prendido — son overrides de desarrollo que tienen prioridad tanto
sobre el arranque clásico como sobre uno dirigido por estructura. Un lanzamiento ?cp= también
alinea el cursor de bloque del runner al capítulo del checkpoint, así que un NEXTCHAPTER
disparado después sigue avanzando correctamente. Ambos botones se niegan a lanzar si el personaje
de control no tiene cuarto asignado, ya que el resolver del runtime lanzaría un error.
Interruptor de arranque#
El interruptor drive boot de la barra de herramientas es structure.enabled. Cuando está
prendido (y existe al menos un bloque), shell/main.js le entrega toda la secuencia de arranque
al runner de estructura, que entra al bloque 0 y reproduce la secuencia de arriba hacia abajo —
las escenas avanzan al terminar, los capítulos aplican su entry canónica y esperan un token
NEXTCHAPTER/NEXTBLOCK, y los bloques GUI esperan que el propio botón del menú dispare
NEXTBLOCK. Cuando está apagado, el arranque es clásico — directo al cuarto de initialPlayer —
y la estructura (si existe) solo es alcanzable mediante los overrides de desarrollo ?cp=/
?enter= de arriba. Este es un interruptor real y en uso: se lee al arrancar
(structureDriving() condiciona si corre enterStructureBlock()) y en cada token
NEXTCHAPTER/NEXTBLOCK/PARTYCOMMIT — no es un flag guardado pero sin usar.
Cuando la estructura se queda sin bloques#
Que terminara el último bloque era, literalmente, un no-op: un juego cuyo último bloque eran los créditos terminaba en pantalla negra — sin escena, sin menú, sin cuarto.
Ahora cada bloque de escena tiene una opción al terminar, visible en el último bloque (el único lugar donde puede importar):
- reiniciar el loop — borra la partida y vuelve a entrar al bloque 0. Es el default de una
escena marcada como Créditos finales, así que el caso común no necesita autoría. El borrado
(
RESETGAME) limpia el progreso — flags, inventarios, party, estado de puzzles — y conserva los achievements del jugador, además de la configuración y las partidas guardadas. - quedarse ahí — la conducta vieja. Validate la marca, porque el jugador se queda mirando una
pantalla negra salvo que la escena sostenga su último frame (
endMode: hold).
Para cualquier cosa condicional, autóralo: un token GOTOBLOCK:<bloque> en el onEnd de la
escena le gana a la opción, y como endMode: hold difiere el onEnd al skip del jugador, dispara
justo cuando él decide salir. Así ramificas — todos los achievements → un bloque de bonus, si no →
la pantalla de título — o lo mandas antes a una pantalla de scores o evaluación, con RESETGAME
donde quieras borrar la pizarra.
Validar#
El botón ✓ Validate revisa toda la estructura de una pasada y abre un reporte con dos partes: un resumen (estado de drive boot, conteos de bloques/escenas/capítulos, el cuarto de entrada resuelto, cuántas de las GUIs del proyecto están realmente cableadas a la estructura, cuál escena es la intro y cuál son los créditos, y el número de checkpoints por capítulo) y una lista de advertencias, cada una marcada 🔴 (un error — la estructura no puede correr) o 🟠 (algo para revisar). Detecta cosas como: ningún bloque, ningún bloque de capítulo, un primer capítulo sin cuarto de entrada, ninguna escena marcada Intro, ninguna escena marcada Créditos, más de una de cualquiera de las dos, un bloque de escena o GUI con referencia vacía o rota, y un capítulo sin personaje de control asignado. Revisa todos los checkpoints de cada capítulo, no solo la entrada canónica del capítulo — un personaje de control, integrante de reparto, o room de un actor activo que esté roto se marca en cualquier checkpoint (repeticiones de la misma referencia rota en varios checkpoints se agrupan en una sola advertencia), y el resumen suma una fila "References — N broken / M checked" para eso. Córrelo cuando quieras un chequeo de sanidad antes de activar drive boot o de entregar el proyecto.
Flujo de trabajo#
- + SCENE / + CHAPTER / + GUI al final del panel para agregar un bloque.
- Nombra cada bloque, y luego llena su campo específico de tipo: un id de scene, un id de gui, o la entry de un capítulo.
- Para un capítulo, fija el personaje de control y, en la tabla de actores de la entry, marca quién está active, coloca el room de cada personaje activo, y dales items iniciales.
- Decide inventory persists para el capítulo si no es el primero.
- Agrega checkpoints de desarrollo según necesites para probar a mitad de capítulo; usa ▶ Probar aquí / ▶ Probar entrada para saltar directo a cualquiera de ellos.
- Arrastra los bloques por su manija ⠿ para fijar el orden de reproducción.
- Corre ✓ Validate para detectar piezas faltantes — ninguna escena de intro/créditos, un cuarto de entrada sin asignar, una referencia rota — antes de confiar en la estructura.
- Activa drive boot una vez que la secuencia esté lista para gobernar de verdad el arranque del juego.
- 💾 Save para escribir
structure.json— si alguien más guardó la estructura desde que la cargaste, Save muestra un aviso de conflicto: conservar tus cambios locales o sobrescribir los de la otra persona, nunca se resuelve solo. Un servidor inalcanzable da un error en vez de dejar Save colgado.
Un bloque GUI solo avanza con
NEXTBLOCK. Nada más mueve el flujo más allá de él — ni un temporizador, ni un clic en cualquier otra parte de la pantalla. Si una pantalla de título o un menú de selección de capítulo se autora como bloque GUI y su botón de "inicio" no disparaNEXTBLOCK, el jugador llega a ese menú y el juego simplemente deja de avanzar ahí, sin ningún error que señale el error. Por la misma lógica, el menú no se puede descartar mientras la estructura maneja el flujo: la tecla de escape no lo cierra, y un botón suyo cableado conCLOSEGUIno hace nada. No hay un cuarto detrás de un bloque GUI al que volver, así que cerrarlo dejaría al jugador en una pantalla vacía sin salida. Un submenú abierto encima del bloque (las opciones, por ejemplo) sí cierra normal — solo que vuelve al menú de flujo en vez de a la nada. Consecuencia práctica: un botón "Volver" va en el submenú, nunca en el propio menú de flujo. El gotcha compañero es la distinción entry/checkpoint de arriba: soloblock.entryes lo que usa una partida real — un checkpoint puede verse idéntico y sin embargo nunca correr fuera de un lanzamiento de desarrollo?cp=.