This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this manual in English
Компонент Script позволяет создавать игровую логику, используя язык программирования Lua.
В Defold существует три типа Lua-скриптов, и для каждого из них доступны разные библиотеки Defold.
Defold выполняет Lua-скрипты как часть жизненного цикла движка и раскрывает этот жизненный цикл через набор предопределенных функций обратного вызова. При добавлении компонента скрипта к игровому объекту скрипт становится частью жизненного цикла игрового объекта и его компонента(ов). Скрипт оценивается в Lua-контексте при его загрузке, затем движок выполняет следующие функции и передает ссылку на текущий экземпляр компонента скрипта в качестве параметра. Эту ссылку self
можно использовать для хранения состояния в экземпляре компонента.
self
— это объект типа userdata, который действует как Lua-таблица, однако его нельзя итерировать с помощью pairs()
или ipairs()
и нельзя распечатить с помощью pprint()
.
init(self)
: Вызывается при инициализации компонента.
function init(self)
-- Эти переменные доступны в течение всего времени существования экземпляра компонента
self.my_var = "something"
self.age = 0
end
final(self)
: Вызывается при удалении компонента. Это полезно для очистки, например, если были порождены игровые объекты, которые должны быть удалены при удалении компонента.
function final(self)
if self.my_var == "something" then
-- провести чистку
end
end
update(self, dt)
: Вызывается один раз в каждом кадре. dt
содержит дельту времени с момента последнего кадра.
function update(self, dt)
self.age = self.age + dt -- increase age with the timestep
end
fixed_update(self, dt)
: Обновление, не зависящее от частоты кадров. dt
содержит дельту времени с момента последнего обновления. Эта функция вызывается, когда включен параметр engine.fixed_update_frequency
(!= 0). Полезна, когда вы хотите управлять физическими объектами с регулярными интервалами для достижения стабильной симуляции физики при включенном параметре physics.use_fixed_timestep
в game.project.
function fixed_update(self, dt)
msg.post("#co", "apply_force", {force = vmath.vector3(1, 0, 0), position = go.get_world_position()})
end
on_message(self, message_id, message, sender)
: При отправке сообщений компоненту Script через msg.post()
движок вызывает эту функцию компонента-приемника. За подробностями обращайтесь к руководству по передаче сообщений.
```lua
function on_message(self, message_id, message, sender)
if message_id == hash("increase_score") then
self.total_score = self.total_score + message.score
end
end
```
on_input(self, action_id, action)
: Если этот компонент получил фокус ввода (см. acquire_input_focus
), движок вызывает эту функцию когда ввод зарегистрирован. За подробностями обращайтесь к руководству по вводу.
```lua
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
print("Touch", action.x, action.y)
end
end
```
on_reload(self)
: Эта функция вызывается при перезагрузке скрипта с помощью горячей перезагрузки в редакторе (Edit ▸ Reload Resource). Очень удобно для отладки, тестирования и оптимизации. За подробностями обращайтесь к руководству по горячей перезагрузке.
function on_reload(self)
print(self.age) -- вывести возраст данного игрового объекта
end
Игровой объект с компонентом Script реализует некоторую логику. Часто эта логика зависит от какого-либо внешнего фактора. Вражеский AI может реагировать на то, что игрок находится в определенном радиусе от него; дверь может отомкнуться и открыться в результате взаимодействия с игроком и т.д. и т.п.
Функция update()
позволяет реализовать комплексное поведение, определяемое как механизм состояний, запускаемый каждый кадр — иногда это вполне адекватный подход. Однако каждый вызов update()
сопряжен с определенными затратами. Если функция в действительности не нужна, ее следует удалить и вместо этого попытаться построить логику реактивно. Выгоднее пассивно ждать какого-либо сообщения с последующей реакцией, чем активно исследовать игровой мир в поисках данных для ответа. Более того, проектирование реактивным способом также часто приводит к более чистому и надежному дизайну и реализации.
Давайте рассмотрим конкретный пример. Предположим, вы хотите, чтобы скрипт отправил сообщение через 2 секунды после того, как он был инициирован. Затем он должен дождаться определенного ответного сообщения и после получения ответа отправить еще одно сообщение через 5 секунд. Нереактивный код для этого будет выглядеть примерно так:
function init(self)
-- Счетчик для учета времени.
self.counter = 0
-- Это необходимо, чтобы следить за состоянием.
self.state = "first"
end
function update(self, dt)
self.counter = self.counter + dt
if self.counter >= 2.0 and self.state == "first" then
-- отправить сообщение через 2 секунды
msg.post("some_object", "some_message")
self.state = "waiting"
end
if self.counter >= 5.0 and self.state == "second" then
-- отправить сообщение через 5 секунд после получения "ответа"
msg.post("another_object", "another_message")
-- Nil the state so we don’t reach this state block again.
self.state = nil
end
end
function on_message(self, message_id, message, sender)
if message_id == hash("response") then
-- “first” state done. enter next
self.state = "second"
-- zero the counter
self.counter = 0
end
end
Даже в этом довольно простом случае мы получаем довольно запутанную логику. Это можно улучшить с помощью горутин в модуле (см. ниже), но давайте вместо этого попробуем сделать это реактивным и использовать встроенный механизм синхронизации.
local function send_first()
msg.post("some_object", "some_message")
end
function init(self)
-- Подождать 2 секунды, затем вызвать send_first()
timer.delay(2, false, send_first)
end
local function send_second()
msg.post("another_object", "another_message")
end
function on_message(self, message_id, message, sender)
if message_id == hash("response") then
-- Подождать 5 секунды, затем вызвать send_second()
timer.delay(5, false, send_second)
end
end
Так чище и проще для понимания. Мы избавляемся от внутренних переменных состояния, которые часто трудно проследить через логику, и которые могут привести к трудноуловимым ошибкам. Мы также полностью избавляемся от функции update()
. Это освобождает движок от необходимости вызывать скрипт 60 раз в секунду, даже если он простаивает.
Можно использовать Lua-препроцессор и специальную разметку для условного включения кода в зависимости от варианта сборки. Пример:
-- Используйте одно из следующих ключевых слов: RELEASE, DEBUG или HEADLESS
--#IF DEBUG
local lives_num = 999
--#ELSE
local lives_num = 3
--#ENDIF
Препроцессор доступен как расширение сборки. Подробнее о его установке и использовании см. на странице расширения на GitHub.
Редактор Defold поддерживает редактирование Lua-скриптов с подсветкой синтаксиса и автодополнением. Чтобы автоматически подставить имена функций Defold, нажмите Ctrl+Space — появится список функций, соответствующих набираемому тексту.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB