Компонент Collection Proxy используется для динамической загрузки и выгрузки новых игровых “миров”, основываясь на содержимом файла коллекции. Они могут использоваться для реализации переключения между игровыми уровнями, экранами графического интерфейса, загрузки и выгрузки повествовательных “сцен” на протяжении уровня, загрузки/выгрузки мини-игр и многого другого.
Defold организует все игровые объекты в коллекции. Коллекция может содержать игровые объекты и другие коллекции (то есть субколлекции). Прокси-коллекции позволяют разделить содержимое на отдельные коллекции и затем динамически управлять загрузкой и выгрузкой этих коллекций с помощью скриптов.
Прокси-коллекции отличаются от фабрик коллекций. Фабрика коллекций внедряет содержимое коллекции в текущий игровой мир. Прокси-коллекции создают новый игровой мир во время выполнения и, следовательно, имеют другие сценарии использования.
Добавьте прокси-коллекцию к игровому объекту, кликнув ПКМ на игровом объекте и выбрав Add Component ▸ Collection Proxy из контекстного меню.
В свойстве Collection укажите ссылку на коллекцию, которую необходимо динамически загрузить в среду выполнения позднее. Ссылка является статической и гарантирует, что все содержимое коллекции, на которую ссылаются, в конечном счете окажется в игре.

(Содержимое можно исключить из сборки и загрузить вместо него код, отметив опцию Exclude и используя Live-обновление.)
Когда движок Defold запускается, он загружает и инстанцирует все игровые объекты из загрузочной (bootstrap) коллекции в среду выполнения. Затем он инициализирует и активирует игровые объекты и их компоненты. Какая загрузочная коллекция должна использоваться движком, указывается в настройках проекта. По договоренности этот файл коллекции обычно называется “main.collection”.

Для размещения игровых объектов и их компонентов движок выделяет память, необходимую для всего “игрового мира”, в который инстанцируется содержимое загрузочной коллекции. Кроме того, создается отдельный физический мир для любых объектов столкновений и физических симуляций.
Поскольку скрипты должны иметь возможность обращаться ко всем объектам в игре, даже извне загрузочного мира, им присваивается уникальное имя: свойство Name, которое задается в файле коллекции:

Если загружаемая коллекция содержит прокси-коллекции, коллекции, на которые они ссылаются, автоматически не загружаются. Необходимо контролировать загрузку этих ресурсов с помощью скриптов.
Динамическая загрузка коллекции посредством прокси осуществляется путем отправки сообщения "load" компоненту прокси из скрипта:
-- Сказать прокси "myproxy", чтобы он начал загрузку.
msg.post("#myproxy", "load")

Прокси укажет движку выделить место для нового мира. Также будет создана отдельная среда выполнения физики и инстанцируются все игровые объекты в коллекции “mylevel.collection”.
Новый мир получает свое имя из свойства Name в файле коллекции, в данном примере оно установлено в “mylevel”. Имя должно быть уникальным. Если Name, заданное в файле коллекции, уже используется для загруженного мира, движок сообщит об ошибке конфликта имен:
ERROR:GAMEOBJECT: The collection 'default' could not be created since there is already a socket with the same name.
WARNING:RESOURCE: Unable to create resource: build/default/mylevel.collectionc
ERROR:GAMESYS: The collection /mylevel.collectionc could not be loaded.
Когда движок закончит загрузку коллекции, прокси-коллекция отправит сообщение "proxy_loaded" обратно скрипту, который отправил сообщение "load". Затем скрипт может инициализировать и активировать коллекцию в качестве реакции на это сообщение:
function on_message(self, message_id, message, sender)
if message_id == hash("proxy_loaded") then
-- Новый мир загружен. Инициировать и активировать его.
msg.post(sender, "init")
msg.post(sender, "enable")
...
end
end
"load""proxy_loaded", когда все будет готово."async_load""proxy_loaded", когда все будет готово."init""enable""enable".Имя Name, заданное в свойствах файла коллекции, используется для обращения к игровым объектам и компонентам в загруженном мире. Если, например, создать объект-загрузчик в коллекции начальной загрузки, может понадобиться взаимодействовать с ним из любой загруженной коллекции:
-- сообщить загрузчику о необходимости загрузить следующий уровень:
msg.post("main:/loader#script", "load_level", { level_id = 2 })

И если необходимо взаимодействовать с игровым объектом в загруженной коллекции из загрузчика, можно отправить сообщение, используя полный URL объекта:
msg.post("mylevel:/myobject", "hello")
Невозможно напрямую обращаться к игровым объектам в загруженной коллекции извне этой коллекции:
local position = go.get_position("mylevel:/myobject")
-- loader.script:42: function called can only access instances within the same collection.
Чтобы выгрузить загруженную коллекцию, необходимо отправить сообщения, соответствующие обратным этапам загрузки:
-- unload the level
msg.post("#myproxy", "disable")
msg.post("#myproxy", "final")
msg.post("#myproxy", "unload")
"disable""final"final() всех скриптов."unload"Если более тонкий контроль не требуется, можно отправить сообщение "unload" напрямую, без предварительной деактивации и финализации коллекции. Тогда прокси автоматически отключит и завершит коллекцию перед ее выгрузкой.
Когда Collection Proxy закончит выгрузку коллекции, он отправит сообщение "proxy_unloaded" обратно в скрипт, который отправил сообщение "unload":
function on_message(self, message_id, message, sender)
if message_id == hash("proxy_unloaded") then
-- Ok, мир выгружен...
...
end
end
Обновления прокси-коллекции можно масштабировать, изменяя временной шаг. Это означает, что даже если игра имеет постоянную частоту 60 FPS, прокси может обновляться с большей или меньшей скоростью, влияя на такие аспекты, как:
dt, переданное в update()Можно также установить режим обновления, который позволяет контролировать, должно ли масштабирование выполняться дискретно (что имеет смысл только при коэффициенте масштабирования менее 1.0) или непрерывно.
Управление коэффициентом масштабирования и режимом масштабирования осуществляется путем отправки прокси сообщения set_time_step:
-- обновлять загруженный мир на 1/5 скорости.
msg.post("#myproxy", "set_time_step", {factor = 0.2, mode = 1}
Чтобы увидеть, что происходит при изменении временного шага, мы можем создать объект со следующим кодом в скрипте и поместить его в коллекцию, временной шаг которой мы изменяем:
function update(self, dt)
print("update() with timestep (dt) " .. dt)
end
При шаге по времени 0.2 мы получаем следующий результат в консоли:
INFO:DLIB: SSDP started (ssdp://192.168.0.102:54967, http://0.0.0.0:62162)
INFO:ENGINE: Defold Engine 1.2.37 (6b3ae27)
INFO:ENGINE: Loading data from: build/default
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0.016666667535901
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0
DEBUG:SCRIPT: update() with timestep (dt) 0.016666667535901
update() по-прежнему вызывается 60 раз в секунду, но значение dt меняется. Мы видим, что только 1/5 (0.2) вызовов update() будет иметь dt равное 1/60 (что соответствует 60 FPS) — остальные равны нулю. Все физические симуляции также будут обновляться в соответствии с этим dt и продвигаться только в 1/5 кадров.
Функциональность временного шага коллекции можно использовать для приостановки игры, например, при отображении всплывающего окна или когда окно потеряло фокус. Используйте msg.post("#myproxy", "set_time_step", {factor = 0, mode = 0}) для паузы и msg.post("#myproxy", "set_time_step", {factor = 1, mode = 1}) для возобновления.
За подробностями обращайтесь к set_time_step.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB