🎭 Lua — не игровой движок
Когда говорят, что в игре используется Lua, это не значит, что Lua сам рисует графику, считает столкновения и отправляет данные на видеокарту.
Обычно большая часть движка написана на C или C++, а Lua встраивается внутрь как язык для игровой логики.
Игровой движок
├── графика
├── физика
├── звук
├── сеть
├── анимации
└── Lua-скрипты
├── NPC
├── квесты
├── оружие
├── способности
└── игровые событияДвижок говорит: «Я умею создавать объекты, рассчитывать физику и показывать игроку мир».
Lua отвечает: «Отлично. Тогда когда игрок подойдёт к сундуку, открой его, выдай золото и включи подозрительно эпичную музыку».
🤝 Идеальное разделение обязанностей.
🧠 Как Lua вообще получает доступ к игре
Сам по себе Lua не знает, что такое игрок, NPC, оружие или дверь. Эти возможности ему предоставляет игровой движок.
Допустим, внутри движка есть функция:
void SpawnEnemy(float x, float y) {
// создание противника
}Разработчики могут зарегистрировать её в Lua, после чего скрипт сможет написать:
spawnEnemy(100, 250)Внутри произойдёт примерно следующее:
Lua-скрипт
↓
spawnEnemy(100, 250)
↓
API игрового движка
↓
код C/C++
↓
создание противника
↓
игровой мирИменно поэтому Lua в разных играх выглядит немного по-разному. Сам язык один, а API движков отличается.
Где-то здоровье меняют так:
player:SetHealth(100)Где-то так:
player.health = 100А в другой игре вообще:
Game.SetPlayerHealth(playerId, 100)Lua тот же. Возможности, которые ему дал движок, разные.
🔥 Скрипты чаще всего реагируют на события
Игровой код постоянно реагирует на происходящее:
игрок нажал кнопку;
персонаж вошёл в зону;
пуля попала во врага;
NPC заметил игрока;
таймер закончился;
предмет подобрали;
квест завершился.
Например, у нас есть лава:
function onCollision(object)
if object.type == "player" then
object.health = object.health - 20
end
endВажно: Lua сам не вычисляет столкновение.
Физический движок замечает контакт и вызывает нужный Lua-скрипт:
Физический движок
↓
обнаружил столкновение
↓
вызвал onCollision()
↓
Lua уменьшил здоровье
↓
игра обновила состояние игрокаТакой подход называют event-driven — событийным программированием.
⚡ Что происходит каждый кадр
У игры есть главный цикл. Очень сильно упрощённо он выглядит так:
while игра запущена:
прочитать ввод
обновить физику
обновить игровую логику
выполнить нужные скрипты
обновить анимации
нарисовать кадрЕсли игра работает в 60 FPS, этот цикл выполняется примерно 60 раз в секунду.
Некоторые Lua-скрипты тоже могут получать обновление каждый кадр:
function update(dt)
enemy.x = enemy.x + speed * dt
enddt — время между текущим и предыдущим кадром.
Благодаря ему скорость объекта не должна внезапно зависеть от мощности компьютера.
Иначе покупка новой видеокарты могла бы случайно разблокировать персонажу турборежим. 🏎️
👾 Как на Lua делают поведение NPC
Даже простой противник уже может управляться скриптом:
function update()
local distance = getDistance(enemy, player)
if distance < 10 then
enemy:attack(player)
else
enemy:patrol()
end
endЕсли игрок близко — атакуем. Если далеко — продолжаем патруль.
Более сложный NPC может иметь состояния:
PATROL
↓
PLAYER_DETECTED
↓
CHASE
↓
ATTACK
↓
SEARCH
↓
PATROLНа Lua это можно представить примерно так:
state = "patrol"
function update()
if state == "patrol" and canSeePlayer() then
state = "chase"
elseif state == "chase" and closeToPlayer() then
state = "attack"
end
endПоэтому, когда NPC полкарты преследует именно вас, не принимайте близко к сердцу.
У него просто:
state = "attack"😔
📦 Почему таблицы Lua так удобны для игр
Одна из главных структур данных Lua — table.
Она может работать как массив:
inventory = {
"sword",
"potion",
"key"
}Как словарь:
player = {
health = 100,
mana = 50,
level = 7
}И даже как что-то похожее на объект:
enemy = {
health = 100,
damage = 15
}
function enemy:attack()
print("Enemy attacks!")
endПоэтому на Lua удобно хранить игровые параметры.
weapons = {
pistol = {
damage = 20,
fireRate = 0.4
},
shotgun = {
damage = 70,
fireRate = 1.2
},
sniper = {
damage = 150,
fireRate = 2
}
}Нужно ослабить дробовик? Поменяли число.
Нужно усилить снайперку? Поменяли ещё одно.
Не обязательно лезть глубоко в код движка.
🪄 Почему скрипты удобно менять во время разработки
Одно из преимуществ Lua — возможность быстро менять игровую логику.
Некоторые движки поддерживают hot reload: разработчик редактирует скрипт, сохраняет файл, а игра подхватывает изменения без полной пересборки проекта.
damage = 20Меняем на:
damage = 50Сохраняем — проверяем.
Разработчик через два часа:
damage = 999999«Это не чит. Я баланс тестирую».
⏳ Что делать с задержками, диалогами и кат-сценами
Представьте NPC, который должен сказать фразу, немного подождать, открыть дверь и уйти.
В игровом коде нельзя просто остановить всю программу на две секунды. Иначе вместе с NPC замрёт весь игровой мир.
Для таких сценариев используют:
таймеры;
callbacks;
планировщики задач;
асинхронные механики движка;
coroutines.
В Lua есть корутины — функции, выполнение которых можно временно приостановить, а затем продолжить.
Это удобно для:
диалогов;
кат-сцен;
анимационных последовательностей;
миссий;
скриптовых событий.
🛡️ Может ли Lua-скрипт получить доступ ко всему компьютеру
Если игра поддерживает пользовательские скрипты или моды, давать им полный доступ к системе было бы очень плохой идеей.
Поэтому разработчики часто запускают Lua внутри sandbox — ограниченной среды.
Опасные функции могут быть отключены, а вместо них скрипт получает только игровое API:
Game.Spawn()
Game.PlaySound()
Player.GetPosition()Так Lua может управлять игровым миром, но не получает полный доступ к операционной системе.
🌐 А что происходит в мультиплеере
Вот тут появляется легендарный вопрос:
«А на сервере ты это проверил?»
Допустим, клиентский скрипт отправляет:
player.money = 999999999Если сервер просто поверит клиенту, внутриигровая экономика закончится ещё до открытия магазина.
Поэтому важные действия должны подтверждаться сервером.
Игрок может сказать:
«Я хочу купить меч».
Но сервер проверяет:
if player.money >= sword.price then
buySword(player)
endТо есть клиент просит, а сервер решает, можно ли выполнять действие.
🎲 Где находятся Lua-скрипты в игре
Зависит от конкретного движка.
Это могут быть обычные файлы:
scripts/enemies/zombie.lua
scripts/quests/intro.lua
scripts/weapons/rifle.luaА могут быть скрипты, привязанные прямо к объектам внутри редактора.
Zombie
├── model
├── animations
├── sounds
└── zombie.luaКогда движок создаёт зомби, он подключает его скрипт поведения.
Без него это была бы просто красивая модель, которая стоит посреди карты и размышляет о смысле существования.
🧩 Lua связывает игровые системы между собой
Это, пожалуй, главное.
Представьте способность «огненный шар»:
function castFireball(player)
playAnimation(player, "cast")
local fireball = spawnProjectile()
fireball:onHit(function(enemy)
enemy:takeDamage(50)
playEffect("explosion")
playSound("boom")
end)
endОдин небольшой скрипт связывает:
анимацию;
физику;
урон;
частицы;
звук;
игровую механику.
Lua не обязан самостоятельно реализовывать все эти системы.
Он управляет ими через API игрового движка.
🎼 Движок — оркестр.
🎩 Lua — дирижёр, который решает, когда должен произойти BOOM.
🧪 Мини-проект: комната с противниками
Представим комнату:
игрок входит;
дверь закрывается;
появляются три противника;
игрок побеждает их;
кнопка становится активной;
нажатие кнопки открывает дверь.
На Lua логика могла бы выглядеть так:
local enemiesAlive = 3
function onPlayerEnter()
door:close()
spawnEnemy()
spawnEnemy()
spawnEnemy()
end
function onEnemyDeath()
enemiesAlive = enemiesAlive - 1
if enemiesAlive == 0 then
button:enable()
end
end
function onButtonPressed()
door:open()
endВот это и есть игровой скриптинг в чистом виде.
Вы не пишете физический движок двери. Не программируете рендер врага. Не рассчитываете каждый пиксель.
Вы описываете что должно произойти и при каких условиях.
🚀 Где попробовать Lua самому
Есть классическая ловушка любого новичка:
Читаешь статью: «Да, всё понятно».
Открываешь редактор:|
Мозг: «Мы вообще знакомы?»
Поэтому программирование лучше всего закреплять практикой.
В Кодике можно изучать программирование через короткие уроки и сразу решать задачи в редакторе. Это помогает не просто читать про переменные, условия, функции и таблицы, а использовать их в коде своими руками.
Если хочется разобраться в Lua, игровой логике или просто прокачать базовые навыки программирования, такой формат намного полезнее бесконечного просмотра теории.
А ещё у Кодика есть Telegram-сообщество, где выходят полезные посты про программирование, технологии, языки и разработку. Удобный способ регулярно повторять материал: увидел пример, вспомнил концепцию, сохранил что-то полезное и пошёл дальше. 📱
