Volver al inicio

Análogo de LuaJIT Scratch: 12k scripts a 30 FPS

El artículo desglosa el desarrollo de un intérprete visual de alto rendimiento en LuaJIT y LÖVE2D capaz de ejecutar 12289 scripts en hardware de baja potencia. Describe arquitectura modular, estructura de bloques, interpretación recursiva y compilación EXE. Código de ejemplo preservado para análisis por desarrolladores intermedios/senior.

12 mil scripts de Scratch en LuaJIT: desglose de código
Advertisement 728x90

Intérprete Visual de Alto Rendimiento con LuaJIT y LÖVE2D: 12,000 Scripts en Hardware de Bajo Costo

Un recién llegado ha implementado un editor de programación visual por bloques capaz de ejecutar 12,289 scripts con bloques IF, ROTATE y WHILE TRUE en un solo núcleo de un portátil de bajo rendimiento a una tasa estable de 30 FPS. El proyecto está construido sobre LuaJIT con la biblioteca LÖVE2D, garantizando compatibilidad multiplataforma: Windows, macOS, Linux, Android, iOS. El intérprete está separado del editor, y ya funciona una compilación EXE independiente.

La elección de LuaJIT se debe a su bytecode y compilación JIT para una alta velocidad de ejecución. Se utilizó LÖVE2D por su sintaxis simple y la experiencia del autor con Roblox Studio. VS Code sirvió como editor principal.

Arquitectura del Proyecto

El proyecto se divide en módulos para escalabilidad:

Google AdInline article slot
  • Main.lua — el editor principal.
  • Micropaint.lua — el editor de sprites.
  • GameLoader.lua — el cargador de juegos.
  • BlockList.lua — el registro de bloques (518 líneas, con una función de renderizado).

La interfaz gráfica se implementa manualmente mediante love.graphics sin bibliotecas externas. Ejemplo de renderizado de botón:

--BOTÓN: Cargar
love.graphics.rectangle("line", 230, 10, 120, 40)
love.graphics.print("Cargar", 240, 10)
--BOTÓN: Crear
love.graphics.rectangle("line", 360, 10, 100, 40)
love.graphics.print("Crear", 370, 10)

Manejo de clics con comentarios detallados:

--BOTÓN Crear
if x > 360 and x < 460 and y > 10 and y < 50 then
    createNewGame()
end
--BOTÓN CARGAR
if x > 230 and x < 350 and y > 10 and y < 50 then
    if gload() == "suc" then
        GameLoaded = true
        love.window.showMessageBox("¡LISTO!","Juego llamado "..game.name.." cargado")
    else
        love.window.showMessageBox("ERROR","Ocurrió un error durante la carga")
    end
end

Los comentarios simplifican el mantenimiento, aunque las bibliotecas de GUI habrían hecho el código más limpio.

Google AdInline article slot

Estructura y Renderizado de Bloques

Los bloques se describen en tablas con metadatos. Ejemplo de bloque de número aleatorio:

t[#t+1] = {
    type = 3, --TIPO DE BLOQUE 1 - REGULAR 2 - EJECUCIÓN 3 - DATOS
    isStarter = false, --¿ES ESTE UN BLOQUE DE INICIO DE SCRIPT???
    DisplayName = "Valor Aleatorio (desde, hasta)", -- NOMBRE VISIBLE
    name = "random", -- NOMBRE DEL SISTEMA
    args = 2, -- NÚMERO DE ARGUMENTOS
    containBlocksIn = false, -- CONTIENE BLOQUES DENTRO
    color = {0, 0, 0}
}

Los bloques admiten anidamiento: los argumentos pueden ser otros bloques. Renderizado mediante RenderBlock() con un intento de recursividad (parcialmente implementado). Los cuadrados blancos son campos de entrada.

El intérprete se ejecuta en un solo hilo por simplicidad. Los bloques se dividen en tipos: regular, ejecución, datos. La ejecución de argumentos es recursiva, con resultados en args[i].data.

Google AdInline article slot

Interpretación de Bloques

El código del intérprete verifica name y calcula el valor:

if name == "getX" then --Obtener X
        return sprite.pos.x
    elseif name == "getY" then --Obtener Y
        return sprite.pos.y
    elseif name == "random" then --Aleatorio
        return love.math.random(tonumber(args[1].data),tonumber(args[2].data))
    elseif name == "getrotation" then --Rotación
        return sprite.rotation
    elseif name == "getcostume" then --Disfraz
        return sprite.costumeNumber
    elseif name == "getsize" then --Tamaño
        return sprite.size
    elseif name == "mathplus" then --Suma
        return tonumber(args[1].data) + tonumber(args[2].data)
    elseif name == "mathminus" then --Resta
        return tonumber(args[1].data) - tonumber(args[2].data)
    elseif name == "mathmultiply" then --Multiplicación
        return tonumber(args[1].data) * tonumber(args[2].data)
    elseif name == "mathdivide" then --División
        return tonumber(args[1].data) / tonumber(args[2].data)
end

Los arrays de Lua se indexan desde 1. LÖVE2D no admite ruso de forma nativa — se usaron fuentes del sistema (Comic Sans y otras) mediante love.graphics.newFont.

Compilación y Despliegue

Compilación EXE:

  • Love Fusion empaqueta DLLs y archivos en un EXE.
  • Enigma Virtual Box combina todo en un solo ejecutable.

Se planea soporte para APK/HTML. Demostración: 12,300 hilos con IF/ROTATE/WHILE TRUE a 30 FPS.

Puntos Clave:

  • LuaJIT + LÖVE2D permiten 12k+ scripts en una CPU débil.
  • Arquitectura modular: intérprete separado del editor.
  • GUI manual via love.graphics, bloques anidados con ejecución recursiva.
  • Compilación multiplataforma a EXE/APK/HTML.
  • Los bloques tienen tipos (datos/ejecución), argumentos calculados recursivamente.

— Editorial Team

Advertisement 728x90

Leer después