Компонент Factory используется для динамического порождения игровых объектов из пула объектов в запущенной игре.
При добавлении фабрики к игровому объекту в свойстве Prototype указывается, какой файл игрового объекта должна использовать фабрика в качестве прототипа для всех новых создаваемых ею игровых объектов.
Чтобы инициировать создание игрового объекта, вызовите factory.create()
:
-- factory.script
local p = go.get_position()
p.y = vmath.lerp(math.random(), min_y, max_y)
local component = "#star_factory"
factory.create(component, p)
factory.create()
принимает 5 параметров:
url
[position]
vector3
. Если позицию не указать, игровой объект будет порожден в позиции фабрики.[rotation]
quat
.[properties]
[scale]
number
(больше 0), задающего равномерное масштабирование по всем осям. Кроме того, можно указать vector3
, где каждый компонент задает масштабирование по соответствующей оси.Например:
-- factory.script
local p = go.get_position()
p.y = vmath.lerp(math.random(), min_y, max_y)
local component = "#star_factory"
-- Порождение без вращения, но с двукратным масштабом.
-- Установите стоимость звезды в 10.
factory.create(component, p, nil, { score = 10 }, 2.0) -- <1>
-- star.script
go.property("score", 1) -- <1>
local speed = -240
function update(self, dt)
local p = go.get_position()
p.x = p.x + speed * dt
if p.x < -32 then
go.delete()
end
go.set_position(p)
end
function on_message(self, message_id, message, sender)
if message_id == hash("collision_response") then
msg.post("main#gui", "add_score", {amount = self.score}) -- <2>
go.delete()
end
end
В настоящее время Defold не поддерживает неравномерное масштабирование форм столкновений. Если задать неравномерное значение масштаба, например, vmath.vector3(1.0, 2.0, 1.0)
, спрайт будет масштабироваться правильно, но формы столкновений — нет.
Механизм адресации Defold позволяет получить доступ к каждому объекту и компоненту в запущенной игре. В руководстве по адрессации подробно описано, как работает эта система. Можно использовать тот же механизм адресации для порожденных игровых объектов и их компонентов. Зачастую достаточно использовать идентификатор порожденного объекта, например, при отправке сообщения:
local function create_hunter(target_id)
local id = factory.create("#hunterfactory")
msg.post(id, "hunt", { target = target_id })
return id
end
Передача сообщения самому игровому объекту вместо конкретного компонента фактически отправит сообщение всем компонентам. Обычно это не является проблемой, но об этом следует помнить, если объект имеет много компонентов.
Но что, если необходимо получить доступ к определенному компоненту в порожденном игровом объекте, например, чтобы отключить объект столкновения или изменить изображение спрайта? Решение заключается в построении URL из идентификатора игрового объекта и идентификатора компонента.
local function create_guard(unarmed)
local id = factory.create("#guardfactory")
if unarmed then
local weapon_sprite_url = msg.url(nil, id, "weapon")
msg.post(weapon_sprite_url, "disable")
local body_sprite_url = msg.url(nil, id, "body")
sprite.play_flipbook(body_sprite_url, hash("red_guard"))
end
end
При вызове factory.create()
возвращается id нового игрового объекта, что позволяет сохранить его для дальнейшего использования. Одно из распространенных применений — порождать объекты и добавлять их идентификаторы в таблицу, чтобы потом удалить их все, например, при сбросе компоновки уровня:
-- spawner.script
self.spawned_coins = {}
...
-- Spawn a coin and store it in the "coins" table.
local id = factory.create("#coinfactory", coin_position)
table.insert(self.spawned_coins, id)
А затем:
-- coin.script
-- Удалить все порожденные монеты.
for _, coin_id in ipairs(self.spawned_coins) do
go.delete(coin_id)
end
-- или, как вариант
go.delete(self.spawned_coins)
Также часто бывает необходимо, чтобы порожденный объект знал об объекте, породившем его. В качестве примера можно привести какой-нибудь автономный объект, который может быть порожден только по одному за раз. Тогда порожденный объект должен сообщить породителю, когда он будет удален или деактивирован, чтобы можно было породить другой:
-- spawner.script
-- Spawn a drone and set its parent to the url of this script component
self.spawned_drone = factory.create("#dronefactory", drone_position, nil, { parent = msg.url() })
...
function on_message(self, message_id, message, sender)
if message_id == hash("drone_dead") then
self.spawed_drone = nil
end
end
Логика порожденного объекта:
-- drone.script
go.property("parent", msg.url())
...
function final(self)
-- I'm dead.
msg.post(self.parent, "drone_dead")
end
Отметив в свойствах фабрики Load Dynamically, движок откладывает загрузку ресурсов, связанных с фабрикой.
Если эта опция не отмечена, движок загружает ресурсы прототипа при загрузке компонента Factory, чтобы они были сразу готовы к порождению.
Если опция отмечена, есть два варианта использования:
factory.create()
, когда нужно породить объекты. При этом ресурсы будут загружены синхронно, что может вызвать заминку, а затем будут порождены новые экземпляры.
function init(self)
-- No factory resources are loaded when the factory’s parent
-- collection is loaded. Calling create without having called
-- load will create the resources synchronously.
self.go_id = factory.create("#factory")
end
function final(self)
-- Delete game objects. Will decref resources.
-- In this case resources are deleted since the factory component
-- holds no reference.
go.delete(self.go_id)
-- Calling unload will do nothing since factory holds no references
factory.unload("#factory")
end
factory.load()
для явной асинхронной загрузки ресурсов. Когда ресурсы будут готовы к порождению, будет получен обратный вызов.
function load_complete(self, url, result)
-- Loading is complete, resources are ready to spawn
self.go_id = factory.create(url)
end
function init(self)
-- No factory resources are loaded when the factory’s parent
-- collection is loaded. Calling load will load the resources.
factory.load("#factory", load_complete)
end
function final(self)
-- Delete game object. Will decref resources.
-- In this case resources aren’t deleted since the factory component
-- still holds a reference.
go.delete(self.go_id)
-- Calling unload will decref resources held by the factory component,
-- resulting in resources being destroyed.
factory.unload("#factory")
end
Настройка проекта Max Instances в разделе настроек, связанных с коллекциями ограничивает общее количество экземпляров игровых объектов, которые могут существовать в игровом пространстве (main.collection, загружаемая при запуске, или любое пространство, загруженное через прокси-коллекцию). Все игровые объекты, существующие в пространстве, подсчитываются с учетом этого ограничения, и не имеет значения, размещены ли они вручную в редакторе или порождены во время выполнения скрипта.
Так, если значение Max Instances равно 1024 и в основной коллекции имеется 24 вручную размещенных игровых объекта, можно породить еще 1000 игровых объектов. Как только игровой объект удаляется, можно породить еще один экземпляр.
Может показаться хорошей идеей сохранять порожденные игровые объекты в пуле и использовать их повторно. Однако движок уже выполняет объединение объектов в пул под капотом, поэтому дополнительные операции лишь замедлят работу. Быстрее и чище удалять игровые объекты и порождать новые.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB