Manuals
Manuals




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

Проксі колекції

Компонент (component) проксі колекції (collection proxy) використовується для динамічного завантаження й вивантаження нових ігрових «світів» на основі вмісту файлу колекції (collection). За допомогою проксі колекцій можна реалізувати перемикання між ігровими рівнями й екранами GUI, завантаження та вивантаження сюжетних «сцен» у межах рівня, завантаження й вивантаження мініігор тощо.

Defold організовує всі ігрові об’єкти (game object) у колекції. Колекція може містити ігрові об’єкти та інші колекції (тобто підколекції). Проксі колекцій дають змогу розділити вміст на окремі колекції, а потім динамічно керувати їхнім завантаженням і вивантаженням за допомогою скриптів.

Проксі колекцій відрізняються від компонентів фабрики колекцій (collection factory). Фабрика колекцій створює екземпляри (instance) вмісту колекції в поточному ігровому світі. Проксі колекцій створюють новий ігровий світ під час виконання, тому мають інші сфери застосування.

Створення компонента проксі колекції

  1. Додайте компонент проксі колекції до ігрового об’єкта: клацніть правою кнопкою миші ігровий об’єкт і виберіть Add Component ▸ Collection Proxy у контекстному меню.

  2. Установіть властивість Collection так, щоб вона посилалася на колекцію, яку ви хочете згодом динамічно завантажити в середовище виконання. Це статична залежність на етапі збирання: колекція, на яку вказує посилання, та її залежності компілюються. Якщо прапорець Exclude не встановлено, вони включаються до основного пакета. Коли прапорець Exclude встановлено, ресурси, на які посилаються лише виключені проксі, можна не включати до основного пакета для Live Update, а незавантажений проксі можна перенаправити на іншу скомпільовану колекцію під час виконання, як описано нижче.

додавання компонента проксі

(Ви можете виключити вміст зі збірки й натомість завантажити його за допомогою коду, встановивши прапорець Exclude та скориставшись функцією Live Update.)

Початкове завантаження

Під час запуску рушій Defold завантажує та створює в середовищі виконання екземпляри всіх ігрових об’єктів зі стартової колекції (bootstrap collection). Потім він ініціалізує й вмикає ігрові об’єкти та їхні компоненти. Стартова колекція, яку має використовувати рушій, задається в налаштуваннях проєкту. За усталеною домовленістю цей файл колекції зазвичай має назву main.collection.

початкове завантаження

Щоб розмістити ігрові об’єкти та їхні компоненти, рушій виділяє пам’ять, потрібну для всього «ігрового світу», у якому створюються екземпляри вмісту стартової колекції. Також створюється окремий фізичний світ для всіх об’єктів колізій і фізичної симуляції.

Оскільки компоненти-скрипти повинні мати змогу адресувати всі об’єкти в грі, навіть з-поза світу стартової колекції, цьому світу надається унікальне ім’я — значення властивості Name, яку ви задаєте у файлі колекції:

початкове завантаження

Якщо завантажена колекція містить компоненти проксі колекцій, колекції, на які вони посилаються, не завантажуються автоматично. Ви маєте керувати завантаженням цих ресурсів за допомогою скриптів.

Завантаження колекції

Щоб динамічно завантажити колекцію через проксі, надішліть зі скрипту повідомлення "load" компоненту проксі:

-- Tell the proxy "myproxy" to start loading.
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
        -- New world is loaded. Init and enable it.
        msg.post(sender, "init")
        msg.post(sender, "enable")
        ...
    end
end
"load"
Це повідомлення вказує компоненту проксі колекції розпочати завантаження своєї колекції в новий світ. Після завершення проксі надішле у відповідь повідомлення "proxy_loaded".
"async_load"
Це повідомлення вказує компоненту проксі колекції розпочати фонове завантаження своєї колекції в новий світ. Після завершення проксі надішле у відповідь повідомлення "proxy_loaded".
"init"
Це повідомлення вказує компоненту проксі колекції ініціалізувати всі створені ігрові об’єкти та компоненти. На цьому етапі викликаються всі функції init() скриптів.
"enable"
Це повідомлення вказує компоненту проксі колекції ввімкнути всі ігрові об’єкти та компоненти. Наприклад, усі компоненти-спрайти починають відображатися після ввімкнення.

Зміна колекції виключеного проксі

collectionproxy.set_collection() може перенаправити виключений незавантажений проксі на скомпільовану колекцію, що корисно після монтування пакета Live Update. У проксі має бути встановлено прапорець Exclude, і він не має бути завантаженим або перебувати в процесі завантаження. Шлях має закінчуватися на .collectionc. Під час завантаження проксі колекція та всі її залежності мають бути доступні системі ресурсів.

Перевірте повернене значення перед завантаженням проксі. Ініціалізуйте й увімкніть новий світ лише після отримання proxy_loaded:

local function load_mounted_level()
    local ok, result = collectionproxy.set_collection(
        "#level_proxy",
        "/level_pack/level_3.collectionc"
    )

    if ok then
        msg.post("#level_proxy", "load")
    else
        print("Unable to change proxy collection", result)
    end
end

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

Щоб відновити колекцію, призначену в редакторі, викличте collectionproxy.set_collection("#level_proxy", nil), коли проксі не завантажений і не перебуває в процесі завантаження. Про завантаження й монтування вмісту дивіться посібник зі скриптів Live Update, а про коди помилок collectionproxy.RESULT_* — довідник API.

Адресація в новому світі

Значення Name, задане у властивостях файлу колекції, використовується для адресації ігрових об’єктів і компонентів у завантаженому світі. Якщо ви, наприклад, створите об’єкт-завантажувач у стартовій колекції, вам може знадобитися взаємодіяти з ним із будь-якої завантаженої колекції:

-- tell the loader to load the next level:
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" безпосередньо, не вимикаючи й не фіналізуючи колекцію заздалегідь. Тоді проксі автоматично вимкне та фіналізує колекцію перед її вивантаженням.

Коли проксі колекції завершить вивантаження колекції, він надішле повідомлення "proxy_unloaded" у відповідь скрипту, що надіслав повідомлення "unload":

function on_message(self, message_id, message, sender)
    if message_id == hash("proxy_unloaded") then
        -- Ok, the world is unloaded...
        ...
    end
end

Крок часу

Швидкість оновлення проксі колекції можна масштабувати, змінюючи крок часу. Це означає, що навіть якщо гра працює зі сталою частотою 60 FPS, проксі може оновлюватися швидше або повільніше, впливаючи на такі речі:

Ви також можете задати режим оновлення, щоб керувати тим, чи масштабування виконується дискретно (що має сенс лише за коефіцієнта масштабування меншого за 1.0) або безперервно.

Щоб керувати коефіцієнтом і режимом масштабування, надішліть проксі повідомлення set_time_step:

-- update loaded world at one-fifth-speed.
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: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 і просуватимуться лише в кожному п’ятому кадрі.

Ви можете скористатися кроком часу колекції, щоб призупинити гру, наприклад, під час показу спливного вікна або коли вікно втратило фокус. Використовуйте msg.post("#myproxy", "set_time_step", {factor = 0, mode = 0}), щоб призупинити гру, і msg.post("#myproxy", "set_time_step", {factor = 1, mode = 1}), щоб продовжити.

Докладніше дивіться set_time_step.

Застереження й поширені проблеми

Фізика
За допомогою проксі колекцій у рушій можна завантажити кілька колекцій верхнього рівня, або ігрових світів. При цьому важливо знати, що кожна колекція верхнього рівня є окремим фізичним світом. Фізичні взаємодії (колізії, тригери, трасування променів) відбуваються лише між об’єктами, що належать одному світу. Тож навіть якщо об’єкти колізій із двох світів візуально накладаються один на одного, фізична взаємодія між ними неможлива.
Пам’ять
Кожна завантажена колекція створює новий ігровий світ, який потребує відносно великого обсягу пам’яті. Якщо ви одночасно завантажуєте десятки колекцій через проксі, варто переглянути архітектуру гри. Для створення багатьох екземплярів ієрархій ігрових об’єктів краще підходять фабрики колекцій.
Введення
Якщо у вашій завантаженій колекції є об’єкти, яким потрібні дії введення, переконайтеся, що ігровий об’єкт із проксі колекції отримує введення. Коли ігровий об’єкт отримує повідомлення введення, вони передаються його компонентам, тобто проксі колекцій. Дії введення надсилаються через проксі в завантажену колекцію.