Я только начинаю писать код, он у меня не аккуратный и странный, проект писать на luaJIT love 2d, а заголовок правдивый, оно смогло запустить на одном ядре слабого ноутбука 12300 потоков в которых были блоки IF, ROTATE и WHILE TRUE и стабильные 30 fps. https://t.me/Turbo_Scratch/63 Там если что демонстрация и версия для windows если кому надо. Также github.

Движок я выбрал не случайно, мне понравился синтаксис lua, этот язык и библиотеку love 2d подсказала ии, на этой же библиотеке написан Pocket Up(аналог pocket code, тоже визуальное программирование), также этот язык использует байт-код и jit компиляцию что по словам ии должно сильно увеличивать скорость. Также love 2d собирается под многие платформы Windows, macos, вроде даже linux, android, ios. Читал офф сайт и нашёл упоминание игры Balatro, я эту игру до этого только раз слышал, но про что она не знаю.

Писал я всё в VS Code, это очень удобный редактор кода, я на нём и другие свои проекты писал. С lua я уже был знаком, так как я уже делал игру на roblox studio, и не так давно, осенью 25 года.

Я сразу хотел писать свой редактор так чтобы он работал на Windows, и собирал проекты в EXE(уже реализовано), apk и html. И чтобы отделить в будущем редактор и интерпретатор игры я разделил проект на части Main.lua - редактор, Micropaint.lua - редактор спрайтов, ну и по мелочи GameLoader.lua - загрузчик игр, BlockList.lua - список доступных блоков, не хотел его лепить в main.lua тк там 518 строк, + я туда ещё добавил функцию отрисовки блоков и удобнее когда функционал разделён на отдельные файлы.

Сначала я сделал простенькое окно загрузки и создания игры, GUI библиотеку не использовал и кодом рисовал все линии у кнопок.

--КНОПКА: Загрузить
love.graphics.rectangle("line", 230, 10, 120, 40)
love.graphics.print("Загрузить", 240, 10)
--КНОПКА: Создать
love.graphics.rectangle("line", 360, 10, 100, 40)
love.graphics.print("Создать", 370, 10)

Также я весь код связанный с кнопками старался комментировать, чтобы потом понять для чего какой код, если в отображении кнопки можно по print(“Создать”) понять что это за кнопка, то в коде обработки нажатий не понятно ничего

        --КНОПКА Создать
        if x > 360 and x < 460 and y > 10 and y < 50 then
            createNewGame()
        end
        --КНОПКА ЗАГРУЗИТЬ 
        if x > 230 and x < 350 and y > 10 and y < 50 then
            if gload[3](gload[2]()) == "suc" then
                GameLoaded = true 
                love.window.showMessageBox( "ГОТОВО!","Игра с именем "..game.name.." загружена")
            else
                love.window.showMessageBox( "ОШИБКА","Во время загрузки возникла ошибка")
            end
            
        end

Конечно всё равно по функциям понятно что за что отвечает, но с комментариями проще. А ещё проще было бы если бы я использовал готовую GUI библиотеку

Дальше я писал остальные функции выбора спрайтов, сами спрайты, на блоках подробно останавливаться не буду лишь могу показать пример как я в коде выглядит блок случайного числа

t[#t+1] = {
    type = 3, --ТИП БЛОКА 1 - ОБЫЧНЫЙ 2 - ИСПОЛНЕНИЯ 3 - ДАННЫХ
    isStarter = false, --ЭТО БЛОК НАЧАЛА СКРИПТА???
    DisplayName = "Случайное значение (от, до)", -- ОТОБРАЖАЕМОЕ ИМЯ
    name = "random", -- СИСТЕМНОЕ ИМЯ
    args = 2, -- КОЛИЧЕСТВО АРГУМЕНТОВ
    containBlocksIn = false, -- ИМЕЕТ БЛОКИ ВНУТРИ
    color = {0, 0, 0}
}

И получится из этого

(белые квадратики это поля для ввода чисел)

Также в блоки можно вставлять другие блоки, я пытался сделать это через рекурсию моей функции RenderBlock() которая отображает блок(я хотел чтобы она вызывала саму себя) но у меня это нормально не вышло

Если вдруг кто не понял оно выберет случайное число от (5*5) до (25+100)

Интерпретатор блоков писать было интереснее всего, я его сделал в одном потоке(чтобы было проще) ну и разделил там же блоки на 3 типа, они интерпретируются по разному

if name == "getX" then --Получить X
        return sprite.pos.x
    elseif name == "getY" then --Получить Y
        return sprite.pos.y
    elseif name == "random" then --Рандом
        return love.math.random(tonumber(args[1].data),tonumber(args[2].data))
    elseif name == "getrotation" then --Поворот
        return sprite.rotation
    elseif name == "getcostume" then --Костюм
        return sprite.costumeNumber
    elseif name == "getsize" then --Размер 
        return sprite.size
    elseif name == "mathplus" then --Плюс
        return tonumber(args[1].data) + tonumber(args[2].data)
    elseif name == "mathminus" then --Минус
        return tonumber(args[1].data) - tonumber(args[2].data)
    elseif name == "mathmultiply" then --Умножить
        return tonumber(args[1].data) * tonumber(args[2].data)
    elseif name == "mathdivide" then --Делить
        return tonumber(args[1].data) / tonumber(args[2].data)

Тут представлен код интерпретации блоков данных, name это системное имя, а args[*].data это аргументы, уже переведённые из блоков (тк в блок можно сувать другой блок, и те блоки уже были выполнены) в нормальный текст. А ещё в lua массивы начинаются с 1, а не с 0 как в других языках

По умолчанию LOVE не поддерживает русский язык, я взял из папки C:\windows\fonts шрифт comis sans и сделал его шрифтом по умолчанию, ну и я ещё взял другой шрифт, но как уже не вспомню, сделал его шрифтом для блоков

Когда пришло время собирать проект в EXE файл я решил сначала собрать файлы через Love Fusion (нашёл на github) который положил мне все dll файлы в одну папку, а файлы редактора запаковал в EXE файл, после через Enigma Virtual Box я собрал все dll и exe в один другой exe файл.