Как оптимизировать код на Lua для мобильных игр: быстрые и умные приёмы
Разбираем, как выжать максимум из Lua в мобильных играх: повысить FPS, снизить нагрузку на процессор и экономить батарею. Приёмы для Defold, LÖVE2D, Solar2D и других движков — с примерами кода и советами из практики.
В мобильных играх всё решают кадры. Один микролаг — и игрок уже недовольно жмёт на крестик. На ПК можно позволить себе пару лишних операций, но смартфон такого не простит: здесь меньше мощности, меньше памяти, и батарея тает быстрее, чем кофе в кружке программиста.
Если вы пишете игру на Lua — будь то Defold, LÖVE2D, Solar2D или другой движок — у вас уже есть преимущество: язык лёгкий, быстрый и гибкий. Но это не значит, что можно расслабиться. Даже на Lua можно легко утопить FPS, если не думать об оптимизации.
В этой статье мы разберём приёмы, которые помогут выжать максимум из кода: повысить производительность, разгрузить процессор, снизить потребление энергии и сделать так, чтобы ваша игра летала на любых устройствах.
📢 Заходи к нам в Telegram-канал! Там уютно, по делу и без спама 😊

Почему это важно именно на мобильных? ⚡
Ограничения платформы
Меньше CPU/GPU и оперативной памяти.
Чувствительность к GC-паузам и «микрофризам».
Игрок видит просадку FPS и легко закрывает игру.
Задача разработчика
Минимизировать выделения памяти в кадре.
Сократить число draw calls и работу вне экрана.
Обновлять только то, что действительно нужно.
1) Измеряй, а не гадай 🧪
Оптимизация без профайлера — как ремонт с закрытыми глазами. Включай инструменты движка (Defold Profiler, графики FPS в LÖVE2D/Solar2D) и мерь «горячие» участки прямо в кадре.
-- Простой таймер под LÖVE2D / Lua
local t0 = os.clock()
-- горячий код
local dt = os.clock() - t0
if dt > 0.004 then
print(("Медленно: %.3f ms"):format(dt * 1000))
end
Лайфхак: держи индикатор FPS на экране во время разработки — начнёшь ловить лаги раньше игрока.
2) Убираем мусор: меньше аллокаций — меньше фризов 🧹
♻️ Пулы объектов: переиспользуй таблицы/снаряды/вектора.
📦 Предвыделяй массивы нужного размера заранее.
🔗 Не конкатенируй строки в цикле — копи в список и делай
table.concat
.
-- Пул снарядов
local pool = {}
local function acquire()
return table.remove(pool) or {x=0,y=0,active=true}
end
local function release(b)
b.active=false; b.x=0; b.y=0
pool[#pool+1] = b
end
Тюнингуй GC: collectgarbage("setpause",110)
, setstepmul
≈200. Делай полный сбор при смене сцен.
3) Локальные переменные — бесплатный турбобуст 🚀
-- Вместо глобальных обращений кэшируй ссылки
local sin, cos, sqrt = math.sin, math.cos, math.sqrt
local gfx = love.graphics -- или display/go/sprite под ваш движок
Глобали ищутся через хеш-таблицу. Экономия на одной операции мала, но в 60 FPS × сотни объектов это уже секунды.
4) Быстрый цикл: плотные массивы и предсказуемый обход 🔢
-- Хорошо: плотный числовой массив
local actors = {a1, a2, a3}
for i = 1, #actors do actors[i]:update(dt) end
Избегай «дыр» и смешанных таблиц. for i=1,#t
обычно быстрее и стабильнее, чем pairs
.
5) Не создавай мусор в горячем коде 🔥
Анти‑пример
-- каждый кадр создаёт новую функцию сравнения
table.sort(enemies, function(a,b) return a.hp > b.hp end)
Правильно
local sortByHP = function(a,b) return a.hp > b.hp end
table.sort(enemies, sortByHP)
6) Обновляй только нужное: ленивые тики и видимость 🎛️
Отсеивай объекты вне экрана (камера/окно).
AI и поиск пути — раз в 0.1–0.2 сек, не каждый кадр.
Физика — фиксированный шаг (30–60 Гц), рендер — каждый кадр.
7) Математика без лишнего: квадраты вместо корней 📐
local dx, dy = x1-x2, y1-y2
if dx*dx + dy*dy < r*r then
-- столкновение
end
Кэшируй константы (local PI2 = math.pi*2
), тригонометрические значения — по возможности предрассчитывай.
8) Тяжёлые задачи — вне геймлупа 🧵
Парсинг JSON, генерация уровней, загрузка звуков — запускай при загрузке сцены или в корутинах между кадрами. В кадре оставляй только самое критичное.
9) Рендер: батчи и атласы 🎨
Собери спрайты в атласы — меньше переключений текстур.
Группируй рисование: один материал/шейдер — одним блоком.
Не рисуй невидимое: клиппинг/фрустум‑куллинг для 2D — must have.
10) Быстрый диагноз — быстрые решения 🩹
Симптом | Вероятная причина | Что делать |
---|---|---|
Просадка при спавне волн | Массовые аллокации/создание объектов | Пулы, предзагрузка, распределить спавн по нескольким кадрам |
Периодические микрофризы | Сборка мусора | Снизить аллокации, тюнинг GC, ручной GC при смене сцен |
Высокий расход батареи | Лишние апдейты/таймеры | Редкие тики второстепенных систем, стоп апдейтов off‑screen |
Дёргается UI | Частая перерисовка текста/лейаута | Кэш битмапов/рендер‑таргетов, пересборка только по изменению |
Мини‑пример: «редкий» апдейт вспомогательной системы ⏱️
local acc = 0
function updateAmbient(dt)
acc = acc + dt
if acc >= 0.1 then -- 10 Гц вместо 60
acc = acc - 0.1
-- лёгкая логика фона / частицы / AI на дальних слоях
end
end
📚 Хочешь углубиться в тему?
В приложении Кодик ты найдёшь подробные уроки по этой теме, пошаговые упражнения, разбор ошибок и удобную практику прямо в телефоне или браузере.
А если хочешь быть в курсе новостей, новых фич и полезных материалов — подписывайся на наш Telegram-канал. Там уютно, по делу и с любовью к коду ❤️