La herramienta greengames
Las órdenes crear, validar, probar, empaquetar, previsualizar, capturar y limites, con su sintaxis, sus opciones, sus códigos de salida, la salida en JSON y salidas reales de consola.
Corresponde aFormato 1, API 1 Para quien escribe código
En esta página
greengames es la herramienta de línea de órdenes para fabricar juegos: crea la carpeta, la valida, la prueba con el bot, la empaqueta y la muestra en el navegador.
Cómo se ejecuta#
La herramienta vive en el repositorio de Green Games. Se corre con Node 22.18 o superior, desde la raíz del repositorio, después de npm install. No hay paquete publicado en un registro ni instalador.
node packages/toolkit/bin/greengames.js validar mi-juego
npm run greengames -- validar mi-juego # lo mismoEn esta página, greengames abrevia node packages/toolkit/bin/greengames.js. No hace falta compilar nada: la herramienta es TypeScript que Node ejecuta directamente.
Casi todas las órdenes reciben un juego, que se puede dar de tres maneras:
| Forma | Ejemplo | Qué toma |
|---|---|---|
| Un slug | greengames validar mi-juego | La carpeta games/mi-juego del proyecto. |
| Una ruta | greengames validar /ruta/a/mi-juego | Esa carpeta, esté donde esté. |
--todos | greengames validar --todos | Todos los juegos de games/. |
El proyecto es el directorio más cercano hacia arriba, desde donde estás parado, que tenga una carpeta games/ y un package.json. Si no hay ninguno, es el propio repositorio de Green Games.
Resumen#
| Orden | Qué hace |
|---|---|
greengames crear <slug> [--tipo <tipo>] [--nombre "Nombre"] | Crea games/<slug> a partir de una plantilla. |
greengames validar <slug | ruta | --todos> | Revisa manifiesto, escenas, script, guion y límites. |
greengames probar <slug | ruta | --todos> | Juega la partida con el bot, sin pantalla, en cada dificultad. |
greengames empaquetar <slug | ruta | --todos> [-o archivo.ggx] | Escribe el archivo .ggx. Valida antes. |
greengames previsualizar <slug | ruta> | Servidor local con el reproductor; recarga al guardar. |
greengames capturar <slug | ruta> | Capturas del juego jugado por su bot. Necesita Docker. |
greengames limites | Muestra los límites vigentes. |
greengames importar <archivo | carpeta>... [--juego <carpeta>] | Convierte modelos, texturas, sonidos y paquetes de otras herramientas. |
Opciones comunes:
| Opción | Órdenes | Qué hace |
|---|---|---|
--json | todas menos crear y previsualizar | Escribe un único objeto JSON en vez de texto. |
--sin-tipos | validar, probar, empaquetar | No revisa los tipos del script con TypeScript. Es más rápido y revisa menos. |
--idioma en | validar | Mensajes y sugerencias del validador en inglés. |
Códigos de salida#
Todas las órdenes terminan con código 0 si no hay errores y 1 si los hay: un juego inválido, una prueba fallida, un juego que no existe o una orden mal escrita. Los avisos no cambian el código: un juego "válido con 2 avisos" termina en 0.
crear#
greengames crear <slug> [--tipo <tipo>] [--nombre "Nombre"]
greengames crear torre-de-cajas --nombre "Torre de cajas"
greengames crear salto-de-rana --tipo plataformas-2d --nombre "Salto de rana"Copia una plantilla a games/<slug> del proyecto, pone el slug como id del manifiesto y, si diste --nombre, lo usa como nombre en los dos idiomas (cambia después el inglés a mano). Sin --nombre, la plantilla de tercera persona saca el nombre del slug (torre-de-cajas da "Torre De Cajas") y las demás conservan el suyo.
- El slug lleva solo minúsculas, números y guiones.
- Si la carpeta ya existe, no la toca y termina con error.
--tipo elige la plantilla. Sin la opción, es tercera-persona.
--tipo | Qué crea |
|---|---|
tercera-persona | El juego de siempre: juntar seis monedas contra el reloj, con la cámara detrás del avatar. |
primera-persona | Un juego que se ve por los ojos del avatar. Hecho con piezas. |
plataformas-2d | Un nivel de lado, con vista lateral, mapa de baldosas y láminas. |
cenital | Un juego visto desde arriba, con mapa de baldosas de suelo y láminas. |
isometrico | Un juego en vista isométrica. Hecho con piezas. |
hibrido | Tercera persona con una zona que se juega de lado sobre un mapa de baldosas y otra isométrica. |
multijugador | Una carrera de monedas para 1 a 8 jugadores, que declara multijugador en el manifiesto. |
mundo-abierto | Un mundo de cubos que el motor genera, con biomas, donde se rompen y se ponen bloques. Ver Mundo abierto. |
Qué es cada vista, las láminas y los mapas: Escenas. Con un tipo que no existe, la orden termina con error y lista los que hay:
No existe el tipo de juego "nada". Tipos: tercera-persona, cenital, hibrido, isometrico, multijugador, mundo-abierto, plataformas-2d, primera-personaSalida real, corrida en un proyecto de prueba:
Juego creado en games/torre-de-cajas
juego.json manifiesto: nombre, descripcion, dificultades, textos
escenas/principal.json el mundo: piezas, apariciones, zonas, luces
scripts/principal.ts la logica, con la API de @greengames/sdk
recursos/portada.svg portada 16:9
pruebas/bot.json guion del bot que juega y gana la partida
Siguientes pasos:
greengames validar torre-de-cajas
greengames probar torre-de-cajas
greengames previsualizar torre-de-cajas
Cambia el nombre en ingles y la descripcion en juego.json. Guia completa: docs/GUIA-AUTOR.mdEl juego recién creado es un juego completo y pequeño que ya pasa validar y probar. El de tercera persona es juntar seis monedas contra el reloj, con lava en dos dificultades. Las plantillas con láminas traen además la carpeta laminas/, que esa lista no nombra.
validar#
greengames validar <slug | ruta | --todos> [--json] [--sin-tipos] [--idioma en]Revisa, en este orden: que los archivos existan y sean JSON; que el script se pueda unir en un solo archivo; los tipos del script con TypeScript (que solo conoce JavaScript estándar y la API: fetch o setTimeout son errores de tipo); los esquemas; y las reglas del contrato.
Cada problema es un error (impide empaquetar y probar) o un aviso (conviene leerlo; no impide nada). Qué significa cada código: El validador.
Salida real sobre una copia de patio-de-monedas, el juego de ejemplo hecho con el editor:
patio-de-monedas: validoY sobre otra copia a la que se le agregaron tres defectos a propósito: una llamada a fetch, una pieza que no existe y la aparición movida fuera del suelo.
ERROR GB330 scripts/principal.ts > linea 78, columna 2: TS2304: No se encuentra el nombre 'fetch'. "fetch" no existe dentro de la sala. Los juegos no tienen pagina ni red: usa los servicios de @greengames/sdk.
ERROR GB310 scripts/principal.ts > linea 78: el script usa fetch (`fetch(`), que no existe dentro de la sala. Los juegos no tienen red. Empaqueta los datos en la escena o en `datos` de las piezas.
ERROR GB321 scripts/principal.ts > linea 79: el script nombra la pieza "puerta", que no esta en ninguna escena. Ids disponibles: suelo, meta-poste, meta-poste-2, meta-cinta, moneda, moneda-2, moneda-3, moneda-4, moneda-5, caja, caja-2, peligro.
AVISO GB207 escenas/principal.json > apariciones[0].posicion: no hay ninguna pieza solida bajo la aparicion "inicio" [0, 0, -40]: el jugador caeria al vacio. Pon la aparicion sobre una plataforma: su "posicion" es donde quedan los pies.
patio-de-monedas: 3 error(es) y 1 aviso(s)Cómo leer una línea: nivel, código, archivo, lugar dentro del archivo, mensaje y, al final, la sugerencia de cómo corregirlo. Los problemas del script traen el archivo y la línea de tu código fuente, no del script unido.
Los mensajes de la herramienta salen sin tildes.
probar#
greengames probar <slug | ruta | --todos> [--json] [--dificultad ids] [--semilla n,...] [--semillas N] [--ganar K] [--guion nombres] [--jugadores N] [--registro]Valida el juego, lo empaqueta a .ggx, lo vuelve a abrir y juega la partida sin pantalla con el bot, en cada dificultad y con dos semillas (1 y 2), o las que diga el guion. Qué debe cumplir una corrida para aprobar: Pruebas con el bot.
| Opción | Qué hace |
|---|---|
--dificultad <ids> | Solo esas dificultades, separadas por coma. |
--semilla <n,...> | Solo esas semillas: --semilla 3,7. |
--semillas <N> | Las semillas 1 a N: --semillas 10. |
--ganar <K> | Basta con que aprueben K corridas de cada dificultad, siempre que ninguna tenga errores del juego. Para juegos con mucho azar. |
--guion <nombres> | Solo esos guiones: principal (pruebas/bot.json) o el <nombre> de pruebas/bot-<nombre>.json. |
--jugadores <N> | Pone N jugadores en la sala, todos siguiendo el guion. Para juegos multijugador. |
--registro | Muestra todo lo que el juego escribe con juego.registro. Sin la opción, solo se muestran las últimas líneas de las corridas que fallan. |
Salida real sobre la copia de patio-de-monedas:
OK facil semilla 1: victoria, puntaje 2, 3.0 s de partida en 72 ms; script 0.022 ms/cuadro (max 0.71), 12 piezas
OK facil semilla 2: victoria, puntaje 2, 3.0 s de partida en 71 ms; script 0.019 ms/cuadro (max 0.69), 12 piezas
OK normal semilla 1: victoria, puntaje 2, 3.0 s de partida en 31 ms; script 0.018 ms/cuadro (max 0.41), 12 piezas
OK normal semilla 2: victoria, puntaje 2, 3.0 s de partida en 66 ms; script 0.022 ms/cuadro (max 0.65), 12 piezas
OK dificil semilla 1: victoria, puntaje 2, 3.0 s de partida en 80 ms; script 0.024 ms/cuadro (max 0.67), 12 piezas
OK dificil semilla 2: victoria, puntaje 2, 3.0 s de partida en 73 ms; script 0.020 ms/cuadro (max 0.81), 12 piezas
patio-de-monedas: prueba aprobada (6 de 6 corridas, .ggx de 24.2 KB)Cada línea dice: dificultad y semilla; cómo terminó y con qué puntaje; cuántos segundos de partida se simularon y en cuántos milisegundos reales; el tiempo de script promedio por cuadro y el máximo de un cuadro; y el máximo de piezas vivas. Los milisegundos reales dependen de tu equipo; el resto no.
Una prueba fallida dice por qué. Aquí el guion de esa copia se cambió para mandar al bot a un punto fuera del suelo:
FALLA normal semilla 1: no termino, 16.0 s de partida en 106 ms; script 0.008 ms/cuadro (max 0.71), 12 piezas
- el bot se atasco en el paso 2 de 3 {"ir":[20,0,-6],"tiempoMax":5}: no llego al destino (faltan 16.1 unidades en horizontal y -0.0 en vertical). El bot quedo en [4.04, 0.02, -8.39] a los 6.0 s.
FALLA normal semilla 2: no termino, 16.0 s de partida en 103 ms; script 0.007 ms/cuadro (max 0.64), 12 piezas
- el bot se atasco en el paso 2 de 3 {"ir":[20,0,-6],"tiempoMax":5}: no llego al destino (faltan 16.1 unidades en horizontal y -0.0 en vertical). El bot quedo en [4.04, 0.02, -8.39] a los 6.0 s.
FALLA normal: aprobaron 0 de 2 corridas (se piden 2)
patio-de-monedas: prueba FALLIDA (0 de 2 corridas, .ggx de 24.2 KB)Y aquí el guion esperaba una derrota y el bot ganó:
FALLA normal semilla 1: victoria, puntaje 1, 3.0 s de partida en 71 ms; script 0.019 ms/cuadro (max 0.70), 12 piezas
- la partida termino en "victoria" y el guion esperaba "derrota"
FALLA normal: aprobaron 0 de 1 corridas (se piden 1)
patio-de-monedas: prueba FALLIDA (0 de 1 corridas, .ggx de 24.2 KB)Otros dos casos:
- Si el juego no pasa la validación,
probarimprime el informe del validador y termina con "no se probo porque no pasa la validacion". - Si falta
pruebas/bot.json, lo dice y termina con error.
Con varios guiones, cada línea lleva el nombre del guion entre corchetes: [principal], [derrota].
Con --jugadores, la partida corre con esa cantidad de jugadores. Salida real sobre un juego recién creado con greengames crear arena-de-monedas --tipo multijugador:
OK normal semilla 1: victoria, puntaje 1600, 9.5 s de partida en 3884 ms; script 0.048 ms/cuadro (max 0.98), 16 piezas
OK normal semilla 2: victoria, puntaje 1600, 9.7 s de partida en 4021 ms; script 0.049 ms/cuadro (max 1.26), 16 piezas
arena-de-monedas: prueba aprobada (2 de 2 corridas, .ggx de 3.1 KB)empaquetar#
greengames empaquetar <slug | ruta | --todos> [-o archivo.ggx] [--json] [--sin-tipos]Valida y, si no hay errores, escribe el .ggx. Sin -o, lo deja en dist/juegos/<slug>.ggx del proyecto. -o vale cuando empaquetas un solo juego.
patio-de-monedas: patio-de-monedas.ggx (24.2 KB)Con errores no escribe nada: imprime el informe del validador y termina con
patio-de-monedas: no se empaqueto. Corrige los errores y vuelve a intentar.Tampoco escribe un .ggx que pese más de 5 MB. Qué hay dentro del archivo: El formato .ggx.
previsualizar#
greengames previsualizar <slug | ruta> [--puerto N] [--anfitrion 0.0.0.0]Levanta un servidor local con el reproductor y tu juego, y muestra la dirección. Al guardar un archivo del juego, la página se recarga sola.
- Puerto. Por defecto usa un puerto propio de cada juego, entre 4400 y 4899, derivado del slug; si está ocupado prueba los 20 siguientes. Con
--puerto Nusa ese y, si está ocupado, termina con error. - Quién puede verlo. Escucha en
127.0.0.1: solo tu equipo. Con--anfitrion 0.0.0.0acepta conexiones de otros equipos de tu red, por ejemplo para probar en un teléfono.
La página acepta parámetros en la dirección:
| Parámetro | Ejemplo | Qué hace |
|---|---|---|
auto | auto=1 | Empieza la partida sin pasar por la pantalla de inicio. Con auto=1 la página no se recarga al guardar, para no cortar la partida. |
piloto | piloto=bot | El guion del juego maneja al jugador. |
dificultad | dificultad=facil | Dificultad de la partida. |
semilla | semilla=7 | Semilla de la partida, para repetirla igual. |
modo | modo=sala-local | Partida con bots. |
idioma | idioma=en | Idioma del reproductor y de los textos del juego. |
locales | auto=1&locales=2 | Con auto=1: cuántas personas juegan en este equipo, de 1 a 4. Abre la pantalla dividida o compartida. Sin auto, se elige en la pantalla de inicio si el juego declara multijugador.local. |
recarga | recarga=0 | Desactiva la recarga automática siempre. |
capturar#
greengames capturar <slug | ruta> [--dificultad id] [--semilla n] [--segundos 3,12,30] [--tamano 1280x720] [--movil] [--locales N] [--sin-fin] [-o dir]Saca capturas de un juego jugado por su bot. Levanta la vista previa, abre un navegador sin pantalla dentro de un contenedor (por eso necesita Docker) y deja, por defecto en capturas/<slug>/ del proyecto:
- una imagen por cada segundo pedido (por defecto, a los 3, 12 y 30 segundos);
- otra del panel de fin, salvo con
--sin-fin; - un informe JSON con el resultado, el registro del juego, los errores de consola y los cuadros por segundo.
| Opción | Qué hace |
|---|---|
--segundos 3,15,40 | En qué segundos de partida capturar. |
--tamano 1280x720 | Tamaño de la ventana. Por defecto 1280 x 720. |
--movil | Captura como un teléfono vertical (390 x 844). |
--locales N | Captura con N personas en el mismo equipo (pantalla dividida o compartida). |
--sin-fin | No espera al panel de fin. |
-o dir | Carpeta de salida. |
Los cuadros por segundo de ese informe salen de un navegador sin tarjeta gráfica: son solo referenciales. Cifras medidas con y sin tarjeta gráfica: Rendimiento.
limites#
greengames limites [--json]Imprime los límites vigentes, uno por línea, con el nombre que tienen en el código. Comienzo de la salida real:
paqueteBytes 5242880
paqueteDescomprimidoBytes 20971520
scriptBytes 524288
recursoBytes 2097152
escenas 16
piezasPorEscena 2000
piezasVivas 2500
piezasDinamicas 150La tabla completa, explicada: Límites.
importar#
greengames importar <archivo | carpeta>... [--juego <carpeta del juego>] [opciones]
greengames importar --ayudaConvierte modelos, texturas, sonidos y paquetes de otras herramientas a recursos de un juego, como el importador del editor. Solo lee archivos que ya están en tu equipo: no descarga nada.
Sin --juego solo muestra el informe de lo que haría. Con --juego escribe modelos/, texturas/ y sonidos/, y suma los créditos a creditos.json; si un id ya existe, el recurso nuevo se renombra (arbol-2). Con --escenas escribe también cada escena convertida como escenas/<id>.json. Si falta autor o licencia y no se detectan en los archivos, no escribe nada y sale con código 2.
| Opción | Qué hace |
|---|---|
--juego <carpeta> | La carpeta del juego donde escribir. |
--escenas | Escribe también las escenas convertidas. |
--json | El informe en JSON. |
--triangulos <n> | Triángulos máximos por modelo, hasta 20000. |
--textura <n> | Lado máximo de textura en píxeles: 512 por defecto, hasta 1024. |
--estilo, --colores <n> | Estilo Green Games: sombreado plano, texturas pixeladas y una paleta reducida (16 colores por defecto). |
--cubos <n> | Convierte a cubos: cuántos a lo largo del lado mayor, de 4 a 64. |
--escala <x>, --alto <x> | Factor de escala, o alto final en unidades del mundo (el avatar mide 1,8). |
--arriba y|z, --girar-frente | Eje vertical del archivo, y media vuelta para los modelos que miran hacia -Z. |
--colision <tipo> | caja (por defecto), capsula, envolvente o ninguna. |
--autor, --licencia, --origen | Quién hizo los archivos, con qué licencia los obtuviste (CC0-1.0, CC-BY-4.0, propia…) y de dónde salieron. |
greengames importar descargas/kit --juego games/mi-juego --colision envolvente --origen "Kit de plataformas"
greengames importar heroe.fbx --juego games/mi-juego --alto 1.8 --colision capsula --autor "Ana" --licencia CC-BY-4.0
greengames importar roca.glb --estilo --colores 8Los formatos que acepta y lo que hace con cada uno: Importar assets. El formato de lo que escribe: Modelos, texturas y sonidos.
Salida en JSON#
Con --json, cada orden escribe un único objeto con una clave por juego. Sirve para scripts propios y para agentes.
validar --json da, por juego, { valido, errores, avisos }. Cada problema tiene siempre la misma forma. Salida real del juego con defectos de arriba, recortada a un error y al aviso:
{
"patio-de-monedas": {
"valido": false,
"errores": [
{
"nivel": "error",
"codigo": "GB321",
"archivo": "scripts/principal.ts",
"ruta": "linea 79",
"mensaje": "el script nombra la pieza \"puerta\", que no esta en ninguna escena",
"sugerencia": "Ids disponibles: suelo, meta-poste, meta-poste-2, meta-cinta, moneda, moneda-2, moneda-3, moneda-4, moneda-5, caja, caja-2, peligro."
}
],
"avisos": [
{
"nivel": "aviso",
"codigo": "GB207",
"archivo": "escenas/principal.json",
"ruta": "apariciones[0].posicion",
"mensaje": "no hay ninguna pieza solida bajo la aparicion \"inicio\" [0, 0, -40]: el jugador caeria al vacio",
"sugerencia": "Pon la aparicion sobre una plataforma: su \"posicion\" es donde quedan los pies."
}
]
}
}| Campo | Qué es |
|---|---|
nivel | error o aviso. |
codigo | Estable entre versiones: GB001, GB210... |
archivo | juego.json, escenas/principal.json, scripts/principal.ts, pruebas/bot.json. |
ruta | Lugar dentro del archivo: piezas[3].tamano o linea 40, columna 16. |
mensaje | Qué está mal. |
sugerencia | Opcional: cómo corregirlo. |
Con --idioma en cambian mensaje y sugerencia; codigo, archivo, ruta y nivel quedan igual.
probar --json da, por juego, { aprobada, corridas, grupos, bytesGbx }. Salida real de una corrida de patio-de-monedas, recortada a los campos que más se usan:
{
"patio-de-monedas": {
"aprobada": true,
"corridas": [
{
"guion": "principal",
"dificultad": "normal",
"semilla": 1,
"aprobada": true,
"grave": false,
"fallas": [],
"resultado": {
"termino": true,
"resultado": "victoria",
"puntaje": 2,
"segundos": 3.1166666666666667,
"errores": [],
"avisos": [],
"bot": { "pasos": 3, "hechos": 2, "atascado": null },
"registro": []
}
}
],
"grupos": [
{ "guion": "principal", "dificultad": "normal", "aprobadas": 1, "total": 1, "necesarias": 1, "aprobado": true }
],
"bytesGbx": 24718
}
}fallas es la lista de motivos, en texto, por los que una corrida no aprobó: el error del script con su línea, el límite excedido o dónde se atascó el bot. grave es true cuando hubo errores del juego o límites excedidos: esas corridas hacen fallar la prueba aunque --ganar perdone otras.
empaquetar --json da, por juego, { empaquetado, archivo, bytes, avisos }, o { empaquetado: false, ... } con el informe del validador.
Como biblioteca#
Lo que hace la herramienta se puede llamar desde tu propio código de Node (22.18 o superior), dentro del repositorio:
import { revisarJuego, probarPaquete } from '@greengames/toolkit';
const { informe, lectura } = await revisarJuego('games/mi-juego'); // informe: { valido, errores, avisos }
if (informe.valido) {
const prueba = await probarPaquete(lectura.paquete); // { aprobada, corridas, grupos, bytesGbx }
}| Función | De dónde | Qué hace |
|---|---|---|
revisarJuego(dir) | @greengames/toolkit | Lee la carpeta y la valida, tipos incluidos. Devuelve el informe y el paquete armado. |
leerJuego(dir) | @greengames/toolkit | Solo lee la carpeta y une el script. Devuelve el Paquete. |
probarPaquete(paquete, opciones) | @greengames/toolkit | La misma prueba que greengames probar. |
validarPaquete(paquete, { idioma }) | @greengames/sdk/contrato | El validador, sin tipos. Corre también en el navegador. |
empaquetarGbx(paquete), desempaquetarGbx(bytes) | @greengames/sdk/contrato | Pasan de Paquete a los bytes del .ggx y de vuelta. |
armarPaquete(archivos) | @greengames/sdk/contrato | Arma un Paquete desde un mapa ruta -> contenido, sin herramientas. Exige que el script sea un solo archivo .js. |
Estos módulos tampoco están publicados en un registro: se importan dentro del repositorio.
Sigue con#
- Pruebas con el bot: escribir el guion que
probarnecesita. - El validador: todos los códigos y cómo arreglar cada uno.
- Buenas prácticas: la lista que un juego debe cumplir antes de empaquetarlo.