Jakiś czas temu dość obszernie omawiałem zarządzanie pamięcią w Unity, jeśli przegapiliście ten artykuł to warto go nadrobić. Jak zawsze przy takich tematach pojawiają się pewne wątpliwości, a część zagadnień jest pomijana, dlatego w tym DLC zajmiemy się tymi kwestiami.
Chyba najważniejsza rzeczą jaką poruszano w komentarzach to brak porównania wykorzystania zasobów przez Unity w wersji 2018.2 w pętli foreach ze starymi wersjami. Nikt takich testów do tej pory nie zrobił, dlatego zrobiłem je dla was. Wyniki zestawione w grafice poniżej – nie powtarzałem testów z głównego artykułu dla starych wersji.
Ponieważ autor pierwotnego zestawienia udostępnił kod, którym testował, wykorzystałem dokładnie ten sam skrypt. W zasadzie nie mamy zmian po za jednym typem jakim jest SortedDictionary. Autorom silnika udało się w znaczący sposób obniżyć wykorzystaną pamięć przy pierwszym obrocie pętli schodząc z 288 bajtów do 232. Niestety przy kolejnym obrocie pętli zamiast starych 192 mamy 200. O ile pętla zaoszczędziła nam sporo na początku, to później zajmuje trochę więcej.
Sztuczki optymalizacyjne
Durgi największy zbiór komentarzy dotyczył innych metod optymalizacji gry. Dokładniej pojawiły się dwie istotne.
Deklaracja WaitForSecond
Pierwsza sztuczka dotyczy Coroutines, a podrzucił go Wojtek Kłos. Każdorazowe wywołanie yield return new WaitForSecond(1) zmusza nas do alokacji pamięci. Możemy to bardzo sprytnie obejść, deklarując sobie oczekiwanie do zmiennej.
// Deklaracja var waiting = new WaitForSecond(1); // Wywołanie oczekiwania przez 1 sekundę: yield return waiting;
Object pooling
Object pooling to nic innego jak wykorzystywanie jednego obiektu wiele razy. Jeśli w skrypcie na przemian pojawia się Instantiate i Destroy, to mamy źródło potencjalnych lagów. Można to bardzo łatwo zoptymalizować korzystając z właśnie Object Poolingu. Polega to na tym, że zamiast za każdym razem tworzyć obiekt od nowa, chowamy go sobie i w razie potrzeby wykorzystujemy znów. Metoda świetnie się sprawdzi, jeśli w grze pojawiają się generowani przeciwnicy lub pociski. Przykładowo rzucamy na gracza falę 3 orków. Po tej fali ma go zaatakować druga. Gdy gracz zabije okra, nie wykonujemy na nim Destroy, a jedynie przesuwamy go w bezpieczne miejsce (w Gothicu była jaskinia na orki) czyli takie po za zasięgiem wzroku gracza. Gdy przyjdzie potrzeba rzucenia na gracza drugiej fali, to przenosimy sobie nasze truchła na spawn pointy, restartujemy im statystyki i rzucamy do boju.
Metoda na początku może wydawać się nie intuicyjna, ale jest bardzo szeroko wykorzystywana.
Inne problemy artkułu
W jednym miejscu wspomniałem JavaScript, co spotkało się z oburzeniem i fakt, że Unity w zasadzie zawsze korzystało z UnityScript, które na JS się opierało, stąd mój nieszczęsny skrót myślowy. Jednak jest również faktem, że w sierpniu zeszłego roku Unity zrezygnowało ze wsparcia tego języka, natomiast Unity dalej umożliwia pobranie starszych wersji silnika, gdzie język dalej może być wykorzystany. Natomiast sam rozdział skupiał się na Scripting Backend – stąd nie wiem dlaczego taki problem z tym nieszczęsnym JavaScriptem. Niemniej, skorygowałem treść oryginalnego artykułu.
Oczywiście zapewne są inne metody optymalizacyjne, które da się wykorzystać i jeśli jakieś sztuczki znacie, to podzielcie się nimi w komentarzach.