Pełnomocnik kolekcji to komponent używany do wczytywania i zwalniania nowy “Światów” gry dynamicznie bazując na zawartości pliku kolekcji. Mogą być użyte do przełączania się między światami, poziomami w grze, ekranami menu, wczytywaniem i zwalnianiem fabularnych “scen”, mini-gier i wiele więcej.
Defold organizuje wszystkie obiekty gry w kolekcje. Kolekcja może zawierać obiekty gry i inne kolekcje (podkolekcje). Pełnomocnik kolekcji pozwala na rozdzielenie zawartości Twojej gry w oddzielne kolekcje i wczytywanie dynamiczne zasobów w skryptach.
Pełnomocnicy kolekcji różnią się od fabryk kolekcji. Fabryki kolekcji tworzy instancje obiektów zawartych w kolekcji do obecnego świata gry. Pełnomocnicy kolekcji tworzą natomiast nowe, osobne światy gry w trakcie działania programu, więc są używane w innych przypadkach.
Dodaj komponent typu Pełnomocnik kolekcji (Collection proxy) do obiektu gry klikając prawy przycisk myszki na obiekcie gry i wybierając Add Component ▸ Collection Proxy z menu kontekstowego.
Ustaw właściwość Collection do pliku kolekcji, którą chcesz przez danego pełnomocnika dynamicznie wczytywać w trakcie działania programu. Referencja do tego pliku jest statyczna, więc upewnij się, że wszystkie potrzebne w kolekcji zasoby będą znajdować się w ostatcznej wersji Twojej gry.
(Możesz wyłączyć część zawartości Twojej gry ze zbudowanej aplikacji, a potem ściągnąć ją dynamicznie zaznaczając opcję Exclude i używając funkcjonalności Live update.)
Podczas startu aplikacji silnik Defold wczytuje i tworzy instancje wszystkich obiektów gry z głównej kolekcji (bootstrap collection). Następnie inicjalizuje i aktywuje te obiekty i ich komponenty. Można określić, która z kolekcji jest kolekcją startową w ustawieniach projektu. Domyślnie i zgodnie z konwencją kolekcja ta nazwana jest “main.collection” (z ang. główna.kolekcja).
Dla obiektów gry i ich komponentów silnik Defold alokuje pamięć potrzebną do stworzenia całego “świata gry”, w którym instancje obiektów z głównej kolekcji są tworzone. Tworzony jest również osobny świat fizyki do obsługi kolizji i symulacji fizyki.
Ponieważ komponenty typu skrypt muszą być w stanie adresować każdy obiekt w grze, nawet z innej kolekcji, każdy otrzymuje unikalne imię - takie jakie określisz we właściwości Name w pliku kolekcji:
Jeśli kolekcja, która jest załadowana posiada komponenty typu Pełnomocnik kolekcji, kolekcje do których te komponenty się odnoszą NIE są wtedy wczytywane automatycznie. Musisz jawnie kontrolować wczytywanie zasobów tych kolekcji w kodzie.
Dynamiczne wczytywanie kolekcji przez Pełnomocnika kolekcji jest osiągane przez wysłanie wiadomości "load"
do komponentu pełnomocnika ze skryptu:
-- Tell the proxy "myproxy" to start loading.
msg.post("#myproxy", "load")
Pełnomocnik kolekcji nakaże silnikowi zaalokować pamięć na nowy świat gry, jak i również świat fizyki. Następnie światy mogą być utworzone, a w nich instancje wszystkich obiektów danej kolekcji “mylevel.collection”.
Nowy świat gry dostaje swoje imię określone we właściwości Name pliku wczytanej kolekcji, w tym przykładzie to “mylevel”. Nazwa ta musi być unikalna. Domyślna nazwa nowo utworzonej kolekcji to “default” - warto więc ją zmienić od razu. Jeśli właściwość Name podana w pliku kolekcji jest już aktualnie używana w dowolnym innym załadowanym świecie, silnik Defold zasygnalizuje kolizę nazw:
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.
Kiedy silnik kończy wczytywanie kolekcji, pełnomocnik kolekcji wysyła wiadomość o treści "proxy_loaded"
z powrotem do tego skryptu, który wysłał do niego polecenie wczytania kolekcji "load"
. Skrypt może teraż zainicjalizować i aktywować kolekcję w odpowiedzi na tę wiadomość:
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()
każdego skryptu zostają wywołane w tym momencie."enable"
Właściwość Name ustawiona w pliku kolekcji jest używana do adresowania każdego z obiektów gry i komponentów wczytanego świata gry. Jeśli przykładowo utworzysz obiekt zajmujący się wczytywaniem poziomów (ang. loader), będziesz mieć możliwość komunikowania się z nim z poziomu każdej wczytanej kolekcji:
-- tell the loader to load the next level:
msg.post("main:/loader#script", "load_level", { level_id = 2 })
Aby zwolnić pamięć po wczytanej kolekcji możesz wysłać pełnomocnikowi tej kolekcji wiadomości odpowiadające kolejnym krokom:
-- unload the level
msg.post("#myproxy", "disable")
msg.post("#myproxy", "final")
msg.post("#myproxy", "unload")
"disable"
"final"
final()
każdego skryptu zostają wywołane w tym momencie."unload"
"proxy_unloaded"
. Świat gry zostaje całkowicie usunięty z pamięci.Jeśli nie zależy Ci na szczegółowej kontroli, możesz po prostu wysłać wiadomość "unload"
bezpośrednio, bez uprzedniego dezaktywowania i finalizowania obiektów. Pełnomocnik kolekcji zajmie się wtedy automatycznie dezaktywacją i finalizowaniem zanim ostatecznie zwolni pamięć i usunie świat gry.
Pełnomocnik kolekcji po zwolnieniu zwróci wiadomość "proxy_unloaded"
z powrotem do tego skryptu, który wysłał do niego polecenie zwolnienia kolekcji "unload"
:
function on_message(self, message_id, message, sender)
if message_id == hash("proxy_unloaded") then
-- Ok, the world is unloaded...
...
end
end
Częstotliwość aktualizacji kolekcji (updates) może być kontrolowana przez pełnomocnika poprzez zmianę kroku czasowego (time step). Oznacza to, że nawet jeśli gra działa w stałych 60 klatkach na sekundę, pełnomocnik może aktualizować swoją kolekcję z większym lub mniejszym tempie, co wpływa na działanie symulacji fizyki i zmienną dt
przekazywaną do funkcji update()
. Możesz również ustawić tryb aktualizacji (update mode), który pozwala na kontrolowanie czy skalowanie powinno być przeprowadzone dyskretnie (co ma sens tylko przy współczynniku mniejszym od 1.0) lub ciągle.
Możesz kontrolować współczynnik skalowania kroku czasowego (factor) i tryb skalowania (mode) poprzez wiadomość set_time_step
:
-- update loaded world at one-fifth-speed.
msg.post("#myproxy", "set_time_step", {factor = 0.2, mode = 1}
Aby zobaczyć, czy krok czasowy faktycznie uległ zmianie, możemy stworzyć obiekt z poniższym skryptem, w którym będziemy wypisywać wartość zmiennej dt
:
function update(self, dt)
print("update() with timestep (dt) " .. dt)
end
Z krokiem czasowym 0.2, otrzymujemy we get the following result in the console:
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
Jak widać funkcja update()
jest nadal wywoływana 60 razy na sekundę, ale wartość dt
zmienia się. Widzimy, że jedynie co piąte wywołanie, czyli 1/5 (0.2) wszystkich wywołań funkcji update()
ma wartość dt
odpowiadającą 1/60 (dla 60 FPS)— w pozostałych przypadkach - 0. Symulacja fizyki również będzie odświeżana w zależności od danego dt
, więc postępy będą widoczne tylko w co 5 klatce.
Więcej szczegółów znajdziesz tutaj: set_time_step
.
Did you spot an error or do you have a suggestion? Please let us know on GitHub!
GITHUB