Retour à l'accueil

Analogue Scratch pour LuaJIT : 12k scripts à 30 FPS

L'article analyse le développement d'un interpréteur visuel haute performance sur LuaJIT et LÖVE2D capable d'exécuter 12289 scripts sur matériel faible. Décrit l'architecture modulaire, la structure des blocs, l'interprétation récursive et le build EXE. Code exemple préservé pour analyse par développeurs middle/senior.

12 mille scripts Scratch sur LuaJIT : analyse du code
Advertisement 728x90

Interpréteur Visuel Haute Performance avec LuaJIT et LÖVE2D : 12 000 Scripts sur Matériel Bas de Gamme

Un développeur a créé un éditeur de programmation visuelle par blocs capable d'exécuter 12 289 scripts avec des blocs SI, ROTATION et TANT QUE VRAI sur un seul cœur d'un ordinateur portable bas de gamme, à une fréquence stable de 30 images par seconde. Le projet est basé sur LuaJIT avec la bibliothèque LÖVE2D, garantissant une compatibilité multiplateforme : Windows, macOS, Linux, Android, iOS. L'interpréteur est séparé de l'éditeur, et une version autonome EXE est déjà fonctionnelle.

Le choix de LuaJIT est dû à son bytecode et à sa compilation JIT pour une vitesse d'exécution élevée. LÖVE2D a été utilisé pour sa syntaxe simple et l'expérience de l'auteur avec Roblox Studio. VS Code a servi d'éditeur principal.

Architecture du Projet

Le projet est divisé en modules pour une meilleure évolutivité :

Google AdInline article slot
  • Main.lua — l'éditeur principal.
  • Micropaint.lua — l'éditeur de sprites.
  • GameLoader.lua — le chargeur de jeu.
  • BlockList.lua — le registre des blocs (518 lignes, avec une fonction de rendu).

L'interface graphique est implémentée manuellement via love.graphics sans bibliothèques externes. Exemple de rendu de bouton :

--BOUTON : Charger
love.graphics.rectangle("line", 230, 10, 120, 40)
love.graphics.print("Charger", 240, 10)
--BOUTON : Créer
love.graphics.rectangle("line", 360, 10, 100, 40)
love.graphics.print("Créer", 370, 10)

Gestion des clics avec commentaires détaillés :

--BOUTON Créer
if x > 360 and x < 460 and y > 10 and y < 50 then
    createNewGame()
end
--BOUTON CHARGER
if x > 230 and x < 350 and y > 10 and y < 50 then
    if gload() == "suc" then
        GameLoaded = true
        love.window.showMessageBox("PRÊT !","Jeu nommé "..game.name.." chargé")
    else
        love.window.showMessageBox("ERREUR","Une erreur est survenue lors du chargement")
    end
end

Les commentaires simplifient la maintenance, bien que des bibliothèques d'interface graphique auraient rendu le code plus propre.

Google AdInline article slot

Structure et Rendu des Blocs

Les blocs sont décrits dans des tables avec des métadonnées. Exemple de bloc nombre aléatoire :

t[#t+1] = {
    type = 3, --TYPE DE BLOC 1 - RÉGULIER 2 - EXÉCUTION 3 - DONNÉES
    isStarter = false, --EST-CE UN BLOC DE DÉPART DE SCRIPT ???
    DisplayName = "Valeur Aléatoire (de, à)", -- NOM D'AFFICHAGE
    name = "random", -- NOM SYSTÈME
    args = 2, -- NOMBRE D'ARGUMENTS
    containBlocksIn = false, -- CONTIENT DES BLOCS À L'INTÉRIEUR
    color = {0, 0, 0}
}

Les blocs supportent l'imbrication : les arguments peuvent être d'autres blocs. Rendu via RenderBlock() avec une tentative de récursivité (partiellement implémentée). Les carrés blancs sont des champs de saisie.

L'interpréteur fonctionne en un seul thread pour la simplicité. Les blocs sont divisés en types : régulier, exécution, données. L'exécution des arguments est récursive, avec les résultats dans args[i].data.

Google AdInline article slot

Interprétation des Blocs

Le code de l'interpréteur vérifie name et calcule la valeur :

if name == "getX" then --Obtenir X
        return sprite.pos.x
    elseif name == "getY" then --Obtenir Y
        return sprite.pos.y
    elseif name == "random" then --Aléatoire
        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 --Taille
        return sprite.size
    elseif name == "mathplus" then --Plus
        return tonumber(args[1].data) + tonumber(args[2].data)
    elseif name == "mathminus" then --Moins
        return tonumber(args[1].data) - tonumber(args[2].data)
    elseif name == "mathmultiply" then --Multiplier
        return tonumber(args[1].data) * tonumber(args[2].data)
    elseif name == "mathdivide" then --Diviser
        return tonumber(args[1].data) / tonumber(args[2].data)
end

Les tableaux Lua sont indexés à partir de 1. LÖVE2D ne supporte pas le russe par défaut — les polices système (Comic Sans et autres) ont été utilisées via love.graphics.newFont.

Construction et Déploiement

Construction EXE :

  • Love Fusion intègre les DLL et fichiers dans un EXE.
  • Enigma Virtual Box combine tout en un seul exécutable.

Le support APK/HTML est prévu. Démonstration : 12 300 threads avec SI/ROTATION/TANT QUE VRAI à 30 images par seconde.

Points Clés :

  • LuaJIT + LÖVE2D permettent 12k+ scripts sur un CPU faible.
  • Architecture modulaire : interpréteur séparé de l'éditeur.
  • Interface graphique manuelle via love.graphics, blocs imbriqués avec exécution récursive.
  • Construction multiplateforme vers EXE/APK/HTML.
  • Les blocs sont typés (données/exécution), les arguments calculés récursivement.

— Editorial Team

Advertisement 728x90

Lire ensuite