Всем привет
Продолжаем серию разборов чужого кода с этой страницы: Публичный пример Defold .
Сегодня разбираем пример от britzl — Фабрики и свойства.
Попробуйте этот проект в действии: запустите пример .
Исходная папка с проектом: ссылка на github .
Исходная папка с публичными примера Defold на github: ссылка на github.
Обращение к новичкам:
Я предполагаю, что у вас уже установлен Defold, если нет, перейдите по этой ссылке: Добро пожаловать в Defold.
Также, будет плюсом, если вы хотя бы поверхностно знакомы со строительными блоками Defold. Если нет, ознакомиться с основными концепциями Defold можно на официальном сайте — перейдите по этой ссылке.6.
Пример того, как скачать и открыть готовый проект:
Скачиваем архив с примерами проектов из github
Распаковываем скачанный ZIP архив примеров в любую папку во вашему усмотрению.
Переходим в папку examples
.
Ищем проект play_animation
.
Открываем game.project
.
Внимание: скриншоты представлены ниже, это пример скачивания и открытия проекта, в этом примере мы рассматриваем п
Preformatted text
роект с названием play_animation, потому название папок проекта будет отличаться.


Этот пример демонстрирует эффективный способ передачи параметров, включая строки, при создании объектов через фабрики в Defold.
factory_and_properties.collection
factory_and_properties.collection
содержит в себе только 2 игровых объекта: bg
и go
.
bg
служит в качество игрового объекта под фон.
go
хранит в себе фабрику и скрипт, с помощью этих двух компонентов будут создаваться игровые объекты и выполнять логику примера.
factory_and_properties.atlas
factory_and_properties.atlas
содержит в себе одно изображение и одну анимацию.
Этот “хранилище изображений” нужно для того, чтобы мы могли использовать изображения в игре.
thing_with_properties.go
thing_with_properties.go
— этот файл является прототипом игрового персонажа. Фабрика будет ссылаться на этот шаблон создания игрового объекта.
Рассмотрим lookup.lua:
lookup.lua
— Lua-модуль, реализующий таблицу соответствия (lookup table). Он позволяет сохранять, получать и удалять значения по ключам. Defold не позволяет передавать строки через go.property
, но с помощью таблицы соответствия можно обойти это ограничение:
- В момент создания объекта мы сохраняем строку по хэшу.
- После создания строки объект знает только хэш, но может восстановить строку через
lookup.get(...)
.
local M = {}
Создаётся таблица M
, в которой будут храниться все функции модуля (его публичный интерфейс). Интерфейс — это с помощью чего мы сможем и будем взаимодействовать с нашей таблицей.
local values = {}
Создаётся внутренняя таблица values
, где:
Возьмём в качестве примера пару-строк: hash("player1")
и "Player One"
- ключи — это значения, передаваемые как
key
(в нашем случае:hash("player1")
) - значения — это реальные строки или другие данные, которые ты хочешь сохранить (в нашем случае:
"Player One"
)
Эта таблица не доступна снаружи, только внутри модуля.
function M.add(key, value)
assert(key, "You must provide a key")
assert(value, "You must provide a value")
values[key] = value
end
- Добавляет или обновляет запись в
values
assert
проверяет, что ключ и значение действительно переданы (иначе будет ошибка)
Как пример:M.add(hash("player1"), "Player One")
. Добавили хэш-строкуhash("player1")
и соответствующую ей строку"Player One"
.
function M.remove(key)
assert(key, "You must provide a key")
values[key] = nil
end
- Удаляет значение по ключу
Например:M.remove(hash("player1"))
. Ни хэш-строки по ключу “player”, ни строки соответствующей этому ключу не будет.
function M.get(key)
assert(key, "You must provide a key")
return values[key]
end
- Возвращает значение по ключу
Например:
local name = M.get(hash("player1"))
print(name) --> "Player One"
Модуль возвращает таблицу M, чтобы другие скрипты могли его использовать:
return M
Рассмотрим thing_with_properties.script:
-- properties can be of type number|hash|url|vector3|vector4|quaternion
go.property("move_to", vmath.vector3())
go.property("move_speed", 2)
go.property("cool", false)
go.property("key", hash(""))
go.property("url", msg.url())
go.property("color", vmath.vector4(1, 1, 1, 1))
go.property("rotation", vmath.quat())
Здесь объявляются пользовательские свойства, которые можно передать через factory.create(...)
.
Поддерживаемые типы:
number
,hash
,url
,vector3
,vector4
,quaternion
Примеры:
move_to
: целевая позиция для движения (в этом коде не используется, но подготовлено)move_speed
: скорость движенияcool
: флаг (передаётся случайно)key
: хэш от строки имени (используется для получения строки черезlookup
)url
: ссылка на объект-контроллер, чтобы отправить ему сообщениеcolor
: цвет объектаrotation
: финальный поворот объекта
local lookup = require "factory_and_properties.lookup"
Подключаем модуль с таблицей соответствия, разобранный ранее.
local width = tonumber(sys.get_config("display.width"))
local height = tonumber(sys.get_config("display.height"))
Получаем ширину и высоту экрана игры.
При инициализации:
print("My name is " .. lookup.get(self.key))
print(self.cool and "I'm cool" or "I'm not cool")
- Выводит строковое имя через
lookup.get(self.key)
- Показывает “I’m cool” или “I’m not cool” в зависимости от флага
cool
go.animate("#sprite", "tint", go.PLAYBACK_LOOP_PINGPONG, self.color, ..., 2)
- Анимирует цвет спрайта до указанного (
self.color
), и делает это пинг-понгом (туда-обратно)
go.animate(".", "rotation", ..., self.rotation, ..., 2)
- Анимирует вращение объекта до кватерниона
self.rotation
msg.post(self.url, "thing_created")
- Отправляет сообщение обратно создателю, например, чтобы сообщить: “Я создан!”
self.direction = vmath.vector3(math.random(-1,1), math.random(-1,1), 0)
- Задаёт случайное направление движения
Каждый кадр:
local distance = (self.direction * self.move_speed * dt)
local pos = go.get_position() + distance
- Вычисляется, куда сдвинуть объект с учётом времени и скорости
if pos.x < 0 or pos.x > width then
self.direction.x = -self.direction.x
end
if pos.y < 0 or pos.y > height then
self.direction.y = -self.direction.y
end
- Если объект выходит за пределы экрана — отражается от стенки
go.set_position(pos)
- Перемещает объект
Рассмотрим factory.script:
Этот скрипт — контроллер, который:
- создаёт 10 объектов через фабрику,
- задаёт им разные параметры (скорость, цвет, поворот, и т.п.),
- при нажатии клавиш
Up
иDown
увеличивает или уменьшает скорость движения всех этих объектов.
lookup = require "factory_and_properties.lookup"
Подключает модуль с таблицей соответствия (lookup.lua
), чтобы потом использовать строковые имена через хэши.
local function modify_speed_of_things(things, delta)
for _, id in pairs(things) do
local url = msg.url(nil, id, "script")
local move_speed = go.get(url, "move_speed")
go.set(url, "move_speed", move_speed + delta)
end
end
- Получает список объектов (
things
) (Он создаётся вinit()
) - У каждого получает текущую
move_speed
и изменяет её на+delta
или-delta
(получаем дельту изon_input
- Используется при нажатии клавиш (смотри
on_input
).
Важно: go.get
и go.set
работают по URL, указывающему на компонент скрипта.
local url = msg.url(nil, id, "script")
Создаёт адрес игрового объекта, по которому можно отправить сообщение или получить данные.
local move_speed = go.get(url, "move_speed")
Получаем скорость игрового объекта, используя полученный раннее адрес.
go.set(url, "move_speed", move_speed + delta)
Изменяем свойство-скорость игровому объекту, используя всё тот же адрес.
local function random_position()
return vmath.vector3(
math.random(1, sys.get_config("display.width")),
math.random(1, sys.get_config("display.height")),
0)
end
Возвращает случайную позицию на экране (с учётом размеров экрана).
Во время инициализации игрового объекта:
- Инициализирует список объектов:
self.things = {}
- Создаёт 10 объектов через
factory.create
- Каждому объекту передаёт свойства:
move_to
: куда двигатьсяmove_speed
: начальная скоростьcool
: случайный флаг true/falsekey
: хэш от строки имени, добавляется вlookup
color
,rotation
: внешний видurl
: URL этого управляющего объекта (может быть нужен для сообщений)
Затем:
- Добавляет каждый объект в
self.things
, чтобы позже менять им свойства - Активирует фокус ввода:
acquire_input_focus()
self.things = {}
Создаётся пустой список things, куда будут сохраняться id сгенерированных объектов.
math.randomseed(os.time())
Это нужно, чтобы значения math.random() были действительно случайными каждый запуск.
for i = 1, 10 do
Внутри цикла происходит создание каждого отдельного объекта:
local name = “Foo” .. i
local key = hash(name)
Создание уникального имени и хеша. Например, “Foo1”, “Foo2” и т.д. Затем создаётся хеш этой строки — он нужен как идентификатор.
lookup.add(key, name)
Добавляем в наш список пару: “хеш/строка”.
local id = factory.create("#factory", random_position(), nil, {
move_to = random_position(),
move_speed = math.random(100, 300),
cool = math.random(1,2) == 1 and true or false,
key = key,
color = vmath.vector4(...),
rotation = vmath.quat_rotation_z(...),
url = msg.url()
})
"#factory"
— это ссылка на компонент фабрики в текущем объекте.
random_position()
— задаёт случайную позицию.
move_to
— позиция, куда объект будет двигаться.
move_speed
— скорость движения.
cool
— случайный булев флаг.
key
— уникальный идентификатор.
color
— случайный цвет.
rotation
— случайный поворот.
url
— URL текущего объекта (используется для связи с ним).
Эти свойства передаются новому объекту, и он может использовать их в своём init()
через go.property()
.
function final(self)
msg.post(".", "release_input_focus")
end
Отменяет фокус ввода при удалении игрового объекта.
msg.post("@render:", "draw_text", { text = "Up/Down to modify move_speed", position = vmath.vector3(20, 20, 0) })
Каждый кадр рисует текст на экране.
function on_message(self, message_id, message, sender)
print(message_id, sender)
end
Печатает входящие сообщения в консоль, не используется для логики.
if action_id == hash("up") and action.released then
modify_speed_of_things(self.things, 100)
elseif action_id == hash("down") and action.released then
modify_speed_of_things(self.things, -100)
end
- При отпускании клавиш
Up
иDown
увеличивает или уменьшает скорость всех объектов на ±100. - Использует ранее определённую функцию
modify_speed_of_things
.
Надеюсь, кому-нибудь этот материал будет полезен.
Всем спасибо за внимание
Другие разборы:
- Разбор проектов на Defold 1.Tilemap Collisions [tilemap]
- Разбор проектов на Defold 2. Параллакс [parallax]
- Разбор проектов на Defold 3. Движение игровых объектов [animation, movement, input]
- Разбор проектов на Defold 4. Воспроизвести анимацию [animation, movement, input]
- Разбор проектов на Defold 5. Меню и игра. Прокси-коллекции [proxy-collection, gameloop, collection, gui]
- Разбор проектов на Defold 6. Пауза [пауза]
- Разбор проектов на Defold 7. Простая кнопка [простая кнопка]