Una silueta pensante exhalando formas geométricas de colores que se transforman en pixeles y entran a la pantalla de una consola retro, donde un personaje pixel art camina sobre el nivel

Antes de escribir una sola línea de Chivo-8 tuve que decidir en qué lenguaje se programarían los cartuchos. No era una decisión técnica menor — era, básicamente, decidir qué se siente hacer un juego en esta consola. Terminé en Lua, y no fue el primer lenguaje que consideré.


Lua nació en 1993 en la Pontificia Universidad Católica de Río de Janeiro (dentro de Tecgraf), creado por Roberto Ierusalimschy, Luiz Henrique de Figueiredo y Waldemar Celes — su nombre significa simplemente "luna" en portugués. No nació para hacer videojuegos — nació para que aplicaciones industriales brasileñas pudieran extenderse sin recompilar todo el programa cada vez. La apuesta de sus creadores fue radical para la época: hacer un lenguaje tan pequeño y tan fácil de incrustar que cualquier programa pudiera cargarlo adentro, dejando que la aplicación anfitriona decidiera el resto.

Esa decisión de diseño —ser chico, ser un huésped educado, no un lenguaje que quiere controlar todo el programa— es exactamente lo que después lo volvió irresistible para videojuegos.

Ese salto del mundo industrial a los videojuegos no tardó tanto como parece: uno de los primeros juegos importantes en usar Lua fue Grim Fandango (1998), de LucasArts. Su programador líder, Bret Mogilefsky, escribió a los creadores de Lua en 1997 — terminó escribiendo buena parte del juego en el lenguaje. Los propios autores de Lua señalan ese caso como uno de los que lo puso en el radar de la industria.


Hoy Lua (o algún derivado suyo) corre adentro de World of Warcraft (interfaz de usuario y addons), Roblox —cuyo lenguaje Luau nació como una evolución de Lua 5.1, prueba de que Lua tuvo tanta influencia que terminó generando su propio descendiente—, frameworks como LÖVE2D, y las dos fantasy consoles más conocidas: PICO-8 y TIC-80 (aunque tampoco corren Lua estándar al pie de la letra: PICO-8, por ejemplo, usa su propia sintaxis extendida y números de punto fijo, sin la librería estándar completa — más adelante en Chivo-8 tendré que ser igual de explícito sobre qué es exactamente lo que corre). La razón se repite en todos los casos: un juego (o una app) ya está escrito en el lenguaje anfitrión — C, C++, JavaScript — y necesita darle a alguien más (un modder, un jugador, en este caso tú) una forma simple de programar lógica sin tocar el motor por dentro, con el anfitrión controlando qué queda expuesto y qué no. Lua encaja en ese hueco casi mejor que cualquier otro lenguaje que existe.

En Chivo-8 esa idea es literal: el navegador entero no queda expuesto al cartucho — la consola solo le entrega las funciones que decide exponerle (spr(), btn(), cls(), las que ves más abajo).

Para Chivo-8, que corre en el navegador, esto trajo un problema práctico: Lua no corre nativamente en JavaScript. La solución que usé se llama Fengari — una máquina virtual de Lua reescrita completa en JavaScript, que además usa el recolector de basura de JS en vez de implementar uno propio, lo que simplifica bastante pasar referencias entre los dos mundos (aunque no elimina por completo la posibilidad de fugas — sigues pudiendo dejar referencias vivas por accidente en cualquiera de los dos lados). En la práctica, eso significa que un cartucho escrito en Lua se ejecuta de verdad, no simulado ni traducido a mano: Fengari implementa la semántica de Lua 5.3, con algunas diferencias documentadas (enteros de 32 bits, algunas funciones de io no disponibles en navegador, matices en require) frente al Lua "de escritorio".


Un cartucho mínimo en Chivo-8 se ve, en espíritu, así:

function _init()
  x = 64
  y = 64
end

function _update()
  if btn(0) then x = x - 1 end
  if btn(1) then x = x + 1 end
end

function _draw()
  cls()
  spr(1, x, y)
end

_init corre una vez. _update y _draw son los callbacks con los que el cartucho participa en el game loop del que hablé en el post anterior — se llaman una y otra vez, separando lógica de dibujo. Si programaste algo alguna vez en cualquier lenguaje, esto ya se lee casi solo.


Escritorio dividido en dos: a la izquierda un caos de paneles de código y ventanas de IDE en tonos oscuros, a la derecha una consola portátil cálida y sencilla mostrando un personaje pixel art caminando entre árboles

La alternativa que más consideré fue JavaScript puro, ya que la consola corre en el navegador de todos modos y me habría ahorrado la capa de Fengari. La descarté por una razón de diseño, no técnica: quería que programar un cartucho se sintiera contenido — un espacio chico, con menos sintaxis, menos formas de dispararte en el pie, menos distancia entre "se me ocurrió una idea" y "la veo en pantalla". JavaScript es, para mi gusto, un lenguaje enorme con décadas de capas encima. Lua sigue siendo, más de treinta años después, deliberadamente pequeño — sus propios creadores dicen que la simplicidad es probablemente su característica más importante, y que casi todo lo demás se deriva de ahí. No es casualidad que encaje con que TIC-80 lo mantenga como su lenguaje principal, aunque también soporte JavaScript, MoonScript, Wren y varios más.


No hace falta saber programar "de verdad" para escribir tu primer cartucho. Hace falta, como mucho, entender qué es una variable y qué es una función — el resto lo vas descubriendo empujando x un pixel a la vez.