[Unity] Zapisywanie i wczytywanie skomplikowanego stanu gry


#1

Witam,
od pewnego czasu hobbistycznie staram się stworzyć coś ciekawego w Unity. Przepatrzyłem większość postów i odpowiedzi związanych z persystencją danych, jednak nigdzie nie znalazłem wystarczającej odpowiedzi.

Gra jest grą 3D HDRP i posiada obecnie kilka poziomów (scen). Każda scena zawiera zestaw obiektów, których stan należy zapisać. Są to m. in.: stan animacji (wraz ze zmiennymi animacji), stan zmiennych komponentów-skryptów, położenie, rotacja obiektu, stan cutscenek zrobionych za pomocą timeline itd. W każdej scenie obiektów jest około kilkudziesięciu. Ręczne wskazywanie danych do serializacji i deserializacji trochę mija się z celem, szczególnie że gra nie jest jeszcze skończona, więc się powiększy.

Wymyśliłem, żeby do każdego typu komponentu napisać odpowiedni skrypt, który będzie serializował i deserializował odpowiednie dane. Dla przykładu skrypt związany z persystencją animacji przy zapisie pobierałby z animatora stan animacji, zmiennych itd, następnie przy wczytywaniu te dane by ustawiał. Do tego stworzyłbym takiego managera zapisów, gdzie znajdowałbym wszystkie obiekty z tymi skryptami (np. na zasadzie abstrakcji i interfejsów). Czy jest to dobre rozwiązanie? Może jest jakieś lepsze? W jaki sposób powinienem zapisać timeline, tak aby nie uruchamiał się za każdym wczytaniem?

Druga sprawa to wczytywanie zapisów/kontynuacja gry. Po kolei: powinienem wczytać scenę, wczytać dane z pliku zapisu a następnie te dane ustawić na obiektach sceny? Czy to nie będzie dawało efektu takiego, że gracz przez pewien krótki czas będzie widział na początku stan całkowicie nowego poziomu? Jak to rozwiązać? Obecnie jeśli po wczytaniu poziomu próbuję ustawić te dane z pliku zapisu, to poziom jest ładowany, dane są ustawiane, a następnie są nadpisywane przez domyślne ustawienia komponentów dla nowego poziomu.

Ktoś może zna temat i może udzielić jakichś wskazówek? Z góry dziękuję


#2

Tak czysto ideowo - nie spotkałem gry w której zachowywało by się wszystko, a już na pewno nie klatka animacji itd. Raczej dąży się do tego aby save/load był jak najkrótszy, a zapisywanie zbyt wielu danych w tym nie pomaga i raczej dąży się do odtworzenia stanu przez obliczenia. Raczej rzadko gracz przejmuje się tym, że postać miała np. nogę inaczej. I chyba nigdy nie widziałem, aby umożliwić save w połowie cutscenki ( raczej odgrywa się je od punktu kontrolnego ).

Druga sprawa - ekran ładowania. A jak masz szybki load to fade in/fade out. I tak, wczytać scenę i ustawić wg. zapisanych danych.

A samo zapisywanie i wczytywanie najlepiej oprzeć na interfejsach, np. masz klasę Saver która przy zapisie/wczytaniu wyszukuje klasy implementujące ISaveable, a następnie na każdym obiekcie wywołuje ISaveable.Save() lub ISaveableLoad(). W sensie implementacja interfejsu wskazuje, że klasa poglega pod save/load, natomiast to co się dzieje z klasą przy save/load jest już implementowane w samej klasie.


#3

Unity to jednocześnie zbawienie i przekleństwo.

Zbawienie, bo możesz w edytorze zrobić scenę, poustawiać obiekty (prefabs) i zrobić level w try-miga.

Przekleństwo, że możesz się zapędzić, skorzystać z tego rodzaju “prostot”, a później przychodzą konsekwencje i dają Ci kopa w dupsko. :smiley:


#4

Ale że w jaki sposób ?


#5

+1 do wypowiedzi W_P
nie chcesz serializować wszystkiego, raz że to będzie za dużo,
dwa że nie odtworzysz każdego particla idealnie i będziesz mieć różne bugi z tego tytułu

chcesz odtworzyć i zapisać stan istotnych z punktu widzenia gameplayu obiektów
a resztę odtworzyć potencjalnie inaczej ale tak żeby nie popsuć rozgrywki
i to nie tylko na obecnej scenie ale każdej do której gracz ma dostęp w przyszłości
dlatego większość gier bardzo ogranicza moment save - żeby przejmować się mniejszą liczbą rzeczy

a to że deserializacja zajmuje czas to od tego jest loading screen
poza tym jak nie oddasz procesora to się nic nie wyrenderuje…
bardziej w drugą stronę jest problem żeby jakiś loading bar czy chociaż animację pokazać
że gra się nie zawiesiła…

przy okazji pamiętaj że obiekty na scenie w edytorze i te z sejwa muszą się jakoś do siebie odnieść
żebyś nie miał potem 2 kopii - jednej wczytanej ze sceny a drugiej z save
to może generować sporo issues przy różnych sytuacjach typu spawnowanie przeciwników

ale co do zasady tak - jak dajesz save to coś iteruje po obiektach i zrzuca ich stan
a przy load wczytujesz scenę, potem iterujesz po sejwie i odpalasz jakiś kawałek kodu który ustawi stan za pomocą tego co znalazł w save

inicjalizacja faktycznie jest problematyczna bo jest upierdliwe zarządzanie kolejnością wykonania na starcie
nie wiem jak w unity ale być może będzie trzeba poczekać klatkę i dodać jakiś customowy loading screen żeby twój kod odpalił się jak już wszystkie obiekty są zainicjalizowane
tak żeby nie było sytuacji że jakiś obiekt np jeszcze nie istnieje…
unity niestety nie ma otwartego kodu
więc tu może ktoś kto ma więcej doświadczenia ma tipa gdzie taki kod najlepiej wpiąć

ja bym pewnie dał loading screen na hudzie, dograł scenę addytywnie i poczekał klatkę z ustawianiem stanu, ale może da się jakoś czyściej