This translation is community contributed and may not be up to date. We only maintain the English version of the documentation. Read this tutorial in English
Если вы новичок в Defold, это руководство поможет освоиться в редакторе. Заодно вы познакомитесь с основными идеями и самыми распространенными строительными блоками движка: игровыми объектами, коллекциями, скриптами и спрайтами.
Мы начнем с пустого проекта и шаг за шагом соберем очень маленькое, но уже работающее приложение. После этого у вас должно появиться хорошее представление о том, как устроен Defold, и вы сможете переходить к более крупным руководствам или сразу к мануалам.
На протяжении всего руководства подробные пояснения по концепциям и действиям отмечены вот так. Если вам кажется, что эти блоки слишком подробные, их можно пропускать.

Сначала создайте новый проект и откройте его в редакторе. Если дважды щелкнуть файл main/main.collection, он откроется в редакторе:

Редактор состоит из нескольких основных областей:
print() и pprint(). Если игра не запускается, в первую очередь стоит смотреть сюда.Шаблон “Empty Project” на самом деле полностью пуст. Но даже в нем можно выбрать Project ▸ Build, чтобы собрать проект и запустить игру.

Черный экран вряд ли впечатляет, но это уже работающее приложение Defold, и его легко превратить во что-то интереснее. Этим и займемся.
Редактор Defold работает с файлами. Двойным щелчком по файлу в Assets pane вы открываете его в подходящем редакторе и можете менять его содержимое.
Когда вы закончили редактирование файла, его нужно сохранить. Выберите File ▸ Save. Несохраненные изменения редактор помечает звездочкой ‘*’ в заголовке вкладки.

Сначала создадим новую коллекцию. Коллекция — это контейнер для игровых объектов, которые вы размещаете и настраиваете. Чаще всего коллекции используют для сборки уровней, но они полезны везде, где нужно переиспользовать группу объектов. Можно думать о коллекции как о разновидности prefab.
Выберите папку main в Assets pane, затем щелкните правой кнопкой и выберите New ▸ Collection File. То же самое доступно через File ▸ New ▸ Collection File.

Назовите файл car.collection и откройте его. В этой пустой коллекции мы соберем машину из нескольких игровых объектов. Игровой объект — это контейнер для компонентов: спрайтов, звуков, логики, скриптов и так далее. Внутри игры каждый объект имеет свой уникальный id. Объекты могут обмениваться сообщениями, но до этого мы доберемся позже.
Игровой объект можно создать прямо внутри коллекции, как мы и сделаем. Такой объект существует в единственном экземпляре. Его можно копировать, но каждая копия будет самостоятельной: изменение одной не повлияет на остальные. Поэтому такие объекты удобно использовать там, где не нужно много повторяющихся экземпляров.
А вот игровой объект, сохраненный в отдельном файле, работает как прототип. Когда вы размещаете его экземпляры в коллекции, они создаются по ссылке на этот прототип. Если вы меняете прототип, все экземпляры обновляются автоматически.

Выберите корневой узел “Collection” в Outline, щелкните правой кнопкой и выберите Add Game Object. В коллекции появится новый объект с id “go”. Выберите его и в Properties поменяйте id на “car”.
Пока объект “car” ничего не делает: у него нет графики и нет логики. Чтобы добавить визуальную часть, нам нужен компонент sprite.
Компоненты расширяют игровые объекты визуальным представлением (графика, звук) и функциональностью (factory, collision, scripted behavior). Компонент не существует сам по себе — он всегда находится внутри игрового объекта. Чаще всего компоненты создаются прямо в том же файле, что и объект. Но если компонент нужно переиспользовать, его можно хранить в отдельном файле и подключать по ссылке. Некоторые типы компонентов, например Lua-скрипты, всегда задаются через отдельные файлы.
Важно помнить, что напрямую вы работаете не с компонентами, а с игровыми объектами, которые эти компоненты содержат.

Выберите игровой объект “car”, щелкните правой кнопкой и выберите Add Component, затем выберите Sprite и нажмите Ok. Если отметить sprite в Outline, станет видно, что ему нужно задать несколько свойств:
Изображения для игры:

Добавьте эти изображения в atlas:
![]()
![]()
Теперь добавьте в коллекцию еще два игровых объекта. Назовите их “left_wheel” и “right_wheel” и добавьте в каждый sprite-компонент, использующий изображение колеса из sprites.atlas. Затем перетащите оба объекта на “car”, чтобы они стали дочерними.
Дочерние игровые объекты следуют за своим родителем, когда тот двигается. При этом их можно двигать и отдельно, но движение всегда считается относительно родителя. Для колес это как раз то, что нужно: они будут двигаться вместе с машиной, а мы сможем слегка поворачивать их влево и вправо при рулении.
Расположите колеса, выбрав их и активировав Scene ▸ Move Tool. Переместите объекты в нужные места с помощью стрелок или центрального зеленого квадрата. Последний шаг — убедиться, что колеса рисуются под машиной. Для этого задайте им Z-позицию -0.5. Визуальные элементы рисуются от дальних к ближним по значению Z. У машины Z по умолчанию 0, поэтому -0.5 для колес гарантирует, что они окажутся позади.

Отметьте “main” в Assets pane, щелкните правой кнопкой и выберите New ▸ Script File. Назовите новый файл car.script, затем добавьте его к игровому объекту “car”, отметив “car” в Outline, щелкнув правой кнопкой и выбрав Add Component File. Выберите car.script и нажмите OK. Сохраните файл коллекции.
Дважды щелкните car.script, чтобы открыть его.
В Defold есть несколько lifecycle-функций для игровой логики. Подробнее о них можно прочитать в руководстве по Script.
Для начала удалите функции final, on_message и on_reload: в этом руководстве они не понадобятся.
Теперь добавьте перед init такой код:
-- Constants
local turn_speed = 0.1 -- Slerp factor
local max_steer_angle_left = vmath.quat_rotation_z(math.pi / 6) -- 30 degrees
local max_steer_angle_right = vmath.quat_rotation_z(-math.pi / 6) -- -30 degrees
local steer_angle_zero = vmath.quat_rotation_z(0) -- Zero degrees
local wheels_vector = vmath.vector3(0, 72, 0) -- Vector from center of back and front wheel pairs
local acceleration = 100 -- The acceleration of the car
-- prehash the inputs
local left = hash("left")
local right = hash("right")
local accelerate = hash("accelerate")
local brake = hash("brake")
Изменения здесь довольно простые: мы просто добавили в скрипт набор constants, которые потом будем использовать для управления машиной.
Обратите внимание, что хэши действий ввода заранее сохраняются в переменные. Это хорошая практика: код становится и читабельнее, и немного эффективнее.
Теперь измените функцию init, чтобы она содержала следующее:
function init(self)
-- Send a message to the render script (see builtins/render/default.render_script) to set the clear color.
-- This changes the background color of the game. The vector4 contains color information
-- by channel from 0-1: Red = 0.2. Green = 0.2, Blue = 0.2 and Alpha = 1.0
msg.post("@render:", "clear_color", { color = vmath.vector4(0.2, 0.2, 0.2, 1.0) } ) --<1>
-- Acquire input focus so we can react to input
msg.post(".", "acquire_input_focus") -- <2>
-- Some variables
self.steer_angle = vmath.quat() -- <3>
self.direction = vmath.quat()
-- Velocity and acceleration are car relative (not rotated)
self.velocity = vmath.vector3()
self.acceleration = vmath.vector3()
-- Input vector. This is modified later in the on_input function
-- to store the input.
self.input = vmath.vector3()
end
Что именно здесь происходит:
acquire_input_focus сообщаем, что этот объект должен получать ввод.Теперь измените update() на такой вариант:
function update(self, dt)
-- Set acceleration to the y input
self.acceleration.y = self.input.y * acceleration -- <1>
-- Calculate the new positions of front and back wheels
local front_vel = vmath.rotate(self.steer_angle, self.velocity)
local new_front_pos = vmath.rotate(self.direction, wheels_vector + front_vel)
local new_back_pos = vmath.rotate(self.direction, self.velocity) -- <2>
-- Calculate the car's new direction
local new_dir = vmath.normalize(new_front_pos - new_back_pos)
self.direction = vmath.quat_rotation_z(math.atan2(new_dir.y, new_dir.x) - math.pi / 2) -- <3>
-- Calculate new velocity based on current acceleration
self.velocity = self.velocity + self.acceleration * dt -- <4>
-- Update position based on current velocity and direction
local pos = go.get_position()
pos = pos + vmath.rotate(self.direction, self.velocity)
go.set_position(pos) -- <5>
-- Interpolate the wheels using vmath.slerp
if self.input.x > 0 then -- <6>
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_right)
elseif self.input.x < 0 then
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_left)
else
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, steer_angle_zero)
end
-- Update the wheel rotation
go.set_rotation(self.steer_angle, "left_wheel") -- <7>
go.set_rotation(self.steer_angle, "right_wheel")
-- Set the game object's rotation to the direction
go.set_rotation(self.direction)
-- reset acceleration and input
self.acceleration = vmath.vector3() -- <8>
self.input = vmath.vector3()
end
Здесь происходит следующее:
y берется из текущего ввода.vmath.slerp, чтобы они не дергались мгновенно.Осталось научить машину реагировать на ввод. Измените on_input() так:
function on_input(self, action_id, action)
-- set the input vector to correspond to the key press
if action_id == left then
self.input.x = -1
elseif action_id == right then
self.input.x = 1
elseif action_id == accelerate then
self.input.y = 1
elseif action_id == brake then
self.input.y = -1
end
end
Функция простая: она принимает действие ввода и записывает его в вектор self.input.
Не забудьте сохранить изменения.
Действия ввода пока не настроены, поэтому откройте файл /input/game.input_bindings и добавьте key_trigger для действий “accelerate”, “brake”, “left” и “right”. В примере используются клавиши-стрелки (KEY_LEFT, KEY_RIGHT, KEY_UP и KEY_DOWN):

Теперь машина готова к поездке. Мы создали ее внутри “car.collection”, но в игре она пока не существует. Это потому, что при запуске движок сейчас загружает “main.collection”. Чтобы исправить это, нужно просто добавить car.collection в main.collection. Откройте main.collection, выберите корневой узел “Collection” в Outline, щелкните правой кнопкой и выберите Add Collection From File, выберите car.collection и нажмите OK. Теперь содержимое car.collection будет размещено в main.collection как новые экземпляры. Если изменить содержимое car.collection, каждый экземпляр коллекции будет автоматически обновляться при сборке игры.

Теперь выберите Project ▸ Build и прокатитесь на своей машине. Вы заметите, что управлять ею уже можно, но есть проблема: если отпустить клавиши, машина не останавливается. Исправим это.
Когда объект движется в реальном мире, на него действует сила сопротивления, которая замедляет его. Эта сила примерно пропорциональна квадрату скорости и может быть описана формулой D = k * |V| * V, где k — константа, V — скорость, а |V| — ее величина. Добавим это в код.
В секции констант вверху скрипта добавьте:
local drag = 1.1 --the drag constant <1>
Затем в update, чуть выше строки, где пересчитывается скорость, добавьте следующее:
function update(self, dt)
...
-- Calculate new velocity based on current acceleration
self.velocity = self.velocity + self.acceleration * dt
...
end
function update(self, dt)
...
-- Speed is the magnitude of the velocity
local speed = vmath.length_sqr(self.velocity)
-- Apply drag
self.acceleration = self.acceleration - speed * self.velocity * drag
-- Stop if we are already slow enough
if speed < 0.5 then self.velocity = vmath.vector3(0) end
...
end
После всех шагов ваш car.script должен выглядеть так:
local turn_speed = 0.1 -- Slerp factor
local max_steer_angle_left = vmath.quat_rotation_z(math.pi / 6) -- 30 degrees
local max_steer_angle_right = vmath.quat_rotation_z(-math.pi / 6) -- -30 degrees
local steer_angle_zero = vmath.quat_rotation_z(0) -- Zero degrees
local wheels_vector = vmath.vector3(0, 72, 0) -- Vector from center of back and front wheel pairs
local acceleration = 100 -- The acceleration of the car
local drag = 1.1 -- the drag constant
function init(self)
-- Send a message to the render script (see builtins/render/default.render_script) to set the clear color.
-- This changes the background color of the game. The vector4 contains color information
-- by channel from 0-1: Red = 0.2. Green = 0.2, Blue = 0.2 and Alpha = 1.0
msg.post("@render:", "clear_color", { color = vmath.vector4(0.2, 0.2, 0.2, 1.0) } )
-- Acquire input focus so we can react to input
msg.post(".", "acquire_input_focus")
-- Some variables
self.steer_angle = vmath.quat()
self.direction = vmath.quat()
-- Velocity and acceleration are car relative (not rotated)
self.velocity = vmath.vector3()
self.acceleration = vmath.vector3()
-- Input vector. This is modified later in the on_input function
-- to store the input.
self.input = vmath.vector3()
end
function update(self, dt)
-- Set acceleration to the y input
self.acceleration.y = self.input.y * acceleration
-- Calculate the new positions of front and back wheels
local front_vel = vmath.rotate(self.steer_angle, self.velocity)
local new_front_pos = vmath.rotate(self.direction, wheels_vector + front_vel)
local new_back_pos = vmath.rotate(self.direction, self.velocity)
-- Calculate the car's new direction
local new_dir = vmath.normalize(new_front_pos - new_back_pos)
self.direction = vmath.quat_rotation_z(math.atan2(new_dir.y, new_dir.x) - math.pi / 2)
-- Speed is the magnitude of the velocity
local speed = vmath.length(self.velocity)
-- Apply drag
self.acceleration = self.acceleration - speed * self.velocity * drag
-- Stop if we are already slow enough
if speed < 0.5 then self.velocity = vmath.vector3() end
-- Calculate new velocity based on current acceleration
self.velocity = self.velocity + self.acceleration * dt
-- Update position based on current velocity and direction
local pos = go.get_position()
pos = pos + vmath.rotate(self.direction, self.velocity)
go.set_position(pos)
-- Interpolate the wheels using vmath.slerp
if self.input.x > 0 then
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_right)
elseif self.input.x < 0 then
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, max_steer_angle_left)
else
self.steer_angle = vmath.slerp(turn_speed, self.steer_angle, steer_angle_zero)
end
-- Update the wheel rotation
go.set_rotation(self.steer_angle, "left_wheel")
go.set_rotation(self.steer_angle, "right_wheel")
-- Set the game object's rotation to the direction
go.set_rotation(self.direction)
-- reset acceleration and input
self.acceleration = vmath.vector3()
self.input = vmath.vector3()
end
function on_input(self, action_id, action)
-- set the input vector to correspond to the key press
if action_id == hash("left") then
self.input.x = -1
elseif action_id == hash("right") then
self.input.x = 1
elseif action_id == hash("accelerate") then
self.input.y = 1
elseif action_id == hash("brake") then
self.input.y = -1
end
end
Теперь выберите Project ▸ Build в главном меню и прокатитесь на своей новой машине.
На этом вводное руководство заканчивается. Вот несколько упражнений, которые можно попробовать самостоятельно:
properties, чтобы их можно было менять для разных экземпляров машины.Теперь можно смело нырять глубже в Defold. У нас подготовлено много мануалов и руководств, а если вы застрянете, вас всегда ждут на форуме.
Приятной работы в Defold!