Saltar al contenido
Docs

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.

sh
node packages/toolkit/bin/greengames.js validar mi-juego
npm run greengames -- validar mi-juego     # lo mismo

En 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:

FormaEjemploQué toma
Un sluggreengames validar mi-juegoLa carpeta games/mi-juego del proyecto.
Una rutagreengames validar /ruta/a/mi-juegoEsa carpeta, esté donde esté.
--todosgreengames validar --todosTodos 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#

OrdenQué 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 limitesMuestra los límites vigentes.
greengames importar <archivo | carpeta>... [--juego <carpeta>]Convierte modelos, texturas, sonidos y paquetes de otras herramientas.

Opciones comunes:

OpciónÓrdenesQué hace
--jsontodas menos crear y previsualizarEscribe un único objeto JSON en vez de texto.
--sin-tiposvalidar, probar, empaquetarNo revisa los tipos del script con TypeScript. Es más rápido y revisa menos.
--idioma envalidarMensajes 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#

sh
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.

--tipoQué crea
tercera-personaEl juego de siempre: juntar seis monedas contra el reloj, con la cámara detrás del avatar.
primera-personaUn juego que se ve por los ojos del avatar. Hecho con piezas.
plataformas-2dUn nivel de lado, con vista lateral, mapa de baldosas y láminas.
cenitalUn juego visto desde arriba, con mapa de baldosas de suelo y láminas.
isometricoUn juego en vista isométrica. Hecho con piezas.
hibridoTercera persona con una zona que se juega de lado sobre un mapa de baldosas y otra isométrica.
multijugadorUna carrera de monedas para 1 a 8 jugadores, que declara multijugador en el manifiesto.
mundo-abiertoUn 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:

greengames crear x --tipo nada
No existe el tipo de juego "nada". Tipos: tercera-persona, cenital, hibrido, isometrico, multijugador, mundo-abierto, plataformas-2d, primera-persona

Salida real, corrida en un proyecto de prueba:

Salida de greengames crear
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.md

El 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#

sh
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:

greengames validar patio-de-monedas
patio-de-monedas: valido

Y 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.

greengames validar patio-de-monedas (con defectos)
  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#

sh
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ónQué 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.
--registroMuestra 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:

greengames probar 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:

greengames probar patio-de-monedas --dificultad normal (guion con un destino imposible)
  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ó:

greengames probar patio-de-monedas --dificultad normal --semilla 1 (guion que espera perder)
  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, probar imprime 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:

greengames probar arena-de-monedas --jugadores 4 --dificultad normal
  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#

sh
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.

greengames empaquetar patio-de-monedas -o patio-de-monedas.ggx
patio-de-monedas: patio-de-monedas.ggx (24.2 KB)

Con errores no escribe nada: imprime el informe del validador y termina con

consola
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#

sh
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 N usa 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.0 acepta 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ámetroEjemploQué hace
autoauto=1Empieza 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.
pilotopiloto=botEl guion del juego maneja al jugador.
dificultaddificultad=facilDificultad de la partida.
semillasemilla=7Semilla de la partida, para repetirla igual.
modomodo=sala-localPartida con bots.
idiomaidioma=enIdioma del reproductor y de los textos del juego.
localesauto=1&locales=2Con 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.
recargarecarga=0Desactiva la recarga automática siempre.

capturar#

sh
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ónQué hace
--segundos 3,15,40En qué segundos de partida capturar.
--tamano 1280x720Tamaño de la ventana. Por defecto 1280 x 720.
--movilCaptura como un teléfono vertical (390 x 844).
--locales NCaptura con N personas en el mismo equipo (pantalla dividida o compartida).
--sin-finNo espera al panel de fin.
-o dirCarpeta 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#

sh
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:

greengames limites
paqueteBytes                 5242880
paqueteDescomprimidoBytes    20971520
scriptBytes                  524288
recursoBytes                 2097152
escenas                      16
piezasPorEscena              2000
piezasVivas                  2500
piezasDinamicas              150

La tabla completa, explicada: Límites.

importar#

sh
greengames importar <archivo | carpeta>... [--juego <carpeta del juego>] [opciones]
greengames importar --ayuda

Convierte 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ónQué hace
--juego <carpeta>La carpeta del juego donde escribir.
--escenasEscribe también las escenas convertidas.
--jsonEl 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-frenteEje vertical del archivo, y media vuelta para los modelos que miran hacia -Z.
--colision <tipo>caja (por defecto), capsula, envolvente o ninguna.
--autor, --licencia, --origenQuién hizo los archivos, con qué licencia los obtuviste (CC0-1.0, CC-BY-4.0, propia…) y de dónde salieron.
sh
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 8

Los 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:

greengames validar patio-de-monedas --json (recortada)
{
  "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."
      }
    ]
  }
}
CampoQué es
nivelerror o aviso.
codigoEstable entre versiones: GB001, GB210...
archivojuego.json, escenas/principal.json, scripts/principal.ts, pruebas/bot.json.
rutaLugar dentro del archivo: piezas[3].tamano o linea 40, columna 16.
mensajeQué está mal.
sugerenciaOpcional: 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:

greengames probar patio-de-monedas --dificultad normal --semilla 1 --json (recortada)
{
  "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:

ts
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ónDe dóndeQué hace
revisarJuego(dir)@greengames/toolkitLee la carpeta y la valida, tipos incluidos. Devuelve el informe y el paquete armado.
leerJuego(dir)@greengames/toolkitSolo lee la carpeta y une el script. Devuelve el Paquete.
probarPaquete(paquete, opciones)@greengames/toolkitLa misma prueba que greengames probar.
validarPaquete(paquete, { idioma })@greengames/sdk/contratoEl validador, sin tipos. Corre también en el navegador.
empaquetarGbx(paquete), desempaquetarGbx(bytes)@greengames/sdk/contratoPasan de Paquete a los bytes del .ggx y de vuelta.
armarPaquete(archivos)@greengames/sdk/contratoArma 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#