Zurück zur Startseite

LuaJIT Scratch-Analog: 12k Skripte bei 30 FPS

Artikel analysiert die Entwicklung eines hochperformanten visuellen Interpreters auf LuaJIT und LÖVE2D, der 12289 Skripte auf schwacher Hardware ausführen kann. Beschreibt modulare Architektur, Blockstruktur, rekursive Interpretation und EXE-Build. Beispielcode für die Analyse durch Middle/Senior-Entwickler erhalten.

12 tausend Scratch-Skripte auf LuaJIT: Code-Analyse
Advertisement 728x90

Hochleistungs-Visual-Interpreter mit LuaJIT und LÖVE2D: 12.000 Skripte auf Low-End-Hardware

Ein Newcomer hat einen visuellen Blockprogrammier-Editor implementiert, der 12.289 Skripte mit IF-, ROTATE- und WHILE-TRUE-Blöcken auf einem einzelnen Kern eines Low-End-Laptops bei stabilen 30 FPS ausführen kann. Das Projekt basiert auf LuaJIT mit der LÖVE2D-Bibliothek und gewährleistet plattformübergreifende Kompatibilität: Windows, macOS, Linux, Android, iOS. Der Interpreter ist vom Editor getrennt, und eine eigenständige EXE-Version ist bereits funktionsfähig.

Die Wahl von LuaJIT ist auf dessen Bytecode und JIT-Kompilierung für hohe Ausführungsgeschwindigkeit zurückzuführen. LÖVE2D wurde aufgrund seiner einfachen Syntax und der Erfahrung des Autors mit Roblox Studio verwendet. VS Code diente als primärer Editor.

Projektarchitektur

Das Projekt ist für Skalierbarkeit in Module unterteilt:

Google AdInline article slot
  • Main.lua — der Haupteditor.
  • Micropaint.lua — der Sprite-Editor.
  • GameLoader.lua — der Spiel-Lader.
  • BlockList.lua — die Block-Registry (518 Zeilen, mit einer Rendering-Funktion).

Die GUI wird manuell über love.graphics ohne externe Bibliotheken implementiert. Beispiel für Button-Rendering:

--BUTTON: Load
love.graphics.rectangle("line", 230, 10, 120, 40)
love.graphics.print("Load", 240, 10)
--BUTTON: Create
love.graphics.rectangle("line", 360, 10, 100, 40)
love.graphics.print("Create", 370, 10)

Klick-Handling mit detaillierten Kommentaren:

--BUTTON Create
if x > 360 and x < 460 and y > 10 and y < 50 then
    createNewGame()
end
--BUTTON LOAD
if x > 230 and x < 350 and y > 10 and y < 50 then
    if gload() == "success" then
        GameLoaded = true
        love.window.showMessageBox("READY!","Game named "..game.name.." loaded")
    else
        love.window.showMessageBox("ERROR","An error occurred during loading")
    end
end

Kommentare vereinfachen die Wartung, obwohl GUI-Bibliotheken den Code sauberer gemacht hätten.

Google AdInline article slot

Blockstruktur und Rendering

Blöcke werden in Tabellen mit Metadaten beschrieben. Beispiel für einen Zufallszahlen-Block:

t[#t+1] = {
    type = 3, --BLOCK TYPE 1 - REGULAR 2 - EXECUTION 3 - DATA
    isStarter = false, --IS THIS A SCRIPT START BLOCK???
    DisplayName = "Random Value (from, to)", -- DISPLAY NAME
    name = "random", -- SYSTEM NAME
    args = 2, -- NUMBER OF ARGUMENTS
    containBlocksIn = false, -- CONTAINS BLOCKS INSIDE
    color = {0, 0, 0}
}

Blöcke unterstützen Verschachtelung: Argumente können andere Blöcke sein. Rendering über RenderBlock() mit einem Versuch der Rekursion (teilweise implementiert). Weiße Quadrate sind Eingabefelder.

Der Interpreter läuft aus Einfachheitsgründen in einem einzelnen Thread. Blöcke sind in Typen unterteilt: regulär, Ausführung, Daten. Die Argumentausführung ist rekursiv, mit Ergebnissen in args[i].data.

Google AdInline article slot

Blockinterpretation

Der Interpreter-Code prüft name und berechnet den Wert:

if name == "getX" then --Get X
        return sprite.pos.x
    elseif name == "getY" then --Get Y
        return sprite.pos.y
    elseif name == "random" then --Random
        return love.math.random(tonumber(args[1].data),tonumber(args[2].data))
    elseif name == "getrotation" then --Rotation
        return sprite.rotation
    elseif name == "getcostume" then --Costume
        return sprite.costumeNumber
    elseif name == "getsize" then --Size
        return sprite.size
    elseif name == "mathplus" then --Plus
        return tonumber(args[1].data) + tonumber(args[2].data)
    elseif name == "mathminus" then --Minus
        return tonumber(args[1].data) - tonumber(args[2].data)
    elseif name == "mathmultiply" then --Multiply
        return tonumber(args[1].data) * tonumber(args[2].data)
    elseif name == "mathdivide" then --Divide
        return tonumber(args[1].data) / tonumber(args[2].data)
end

Lua-Arrays sind ab 1 indiziert. LÖVE2D unterstützt Russisch nicht standardmäßig — Systemschriftarten (Comic Sans und andere) wurden über love.graphics.newFont verwendet.

Build und Deployment

EXE-Build:

  • Love Fusion packt DLLs und Dateien in eine EXE.
  • Enigma Virtual Box kombiniert alles zu einer einzigen ausführbaren Datei.

APK/HTML-Unterstützung ist geplant. Demonstration: 12.300 Threads mit IF/ROTATE/WHILE TRUE bei 30 FPS.

Wichtige Punkte:

  • LuaJIT + LÖVE2D ermöglichen 12k+ Skripte auf einer schwachen CPU.
  • Modulare Architektur: Interpreter getrennt vom Editor.
  • Manuelle GUI über love.graphics, verschachtelte Blöcke mit rekursiver Ausführung.
  • Plattformübergreifender Build zu EXE/APK/HTML.
  • Blöcke sind typisiert (Daten/Ausführung), Argumente rekursiv berechnet.

— Editorial Team

Advertisement 728x90

Weiterlesen