Tworzę platformówkę 2D z kilkoma przyjaciółmi z Uni. Oparliśmy go na XNA Platformer Starter Kit, który używa plików .txt do przechowywania mapy kafelków. Chociaż jest to proste, nie zapewnia nam wystarczającej kontroli i elastyczności przy projektowaniu na poziomie. Kilka przykładów: w przypadku wielu warstw treści wymaganych jest wiele plików, każdy obiekt jest przymocowany do siatki, nie pozwala na obracanie obiektów, ograniczoną liczbę znaków itp. Robię więc badania, jak przechowywać dane poziomu i plik mapy.
Dotyczy to tylko przechowywania w systemie plików map kafelków, a nie struktury danych, która ma być używana przez grę podczas jej działania. Mapa kafelków jest ładowana do tablicy 2D, więc pytanie dotyczy tego, z którego źródła należy wypełnić tablicę.
Uzasadnienie dla DB: Z mojej perspektywy widzę mniej nadmiarowości danych przy użyciu bazy danych do przechowywania danych kafelków. Płytki w tej samej pozycji x, y o tej samej charakterystyce można ponownie wykorzystywać z poziomu na poziom. Wydaje się, że napisanie metody pobierania wszystkich kafelków używanych na danym poziomie z bazy danych byłoby dość proste.
Uzasadnienie dla JSON / XML: Pliki edytowalne wizualnie, zmiany można śledzić przez SVN o wiele łatwiej. Ale jest powtarzana treść.
Czy mają jakieś wady (czasy ładowania, czasy dostępu, pamięć itp.) W porównaniu do innych? A co jest powszechnie stosowane w branży?
Obecnie plik wygląda następująco:
....................
....................
....................
....................
....................
....................
....................
.........GGG........
.........###........
....................
....GGG.......GGG...
....###.......###...
....................
.1................X.
####################
1 - Punkt początkowy gracza, X - Wyjście z poziomu,. - Puste miejsce, # - Platforma, G - Klejnot
Odpowiedzi:
Więc czytając zaktualizowane pytanie, wydaje się, że najbardziej martwisz się o „nadmiarowe dane” na dysku, z pewnymi drugorzędnymi obawami dotyczącymi czasów ładowania i wykorzystania przez przemysł.
Po pierwsze, dlaczego martwisz się nadmiarowymi danymi? Nawet w przypadku rozdętego formatu, takiego jak XML, implementacja kompresji i obniżenie rozmiaru poziomów byłoby dość proste. Bardziej prawdopodobne jest, że zajmiesz miejsce teksturami lub dźwiękami niż dane poziomu.
Po drugie, format binarny najprawdopodobniej załaduje się szybciej niż format tekstowy, który trzeba przeanalizować. Podwójnie, jeśli możesz po prostu upuścić go w pamięci i mieć tam swoje struktury danych. Istnieją jednak pewne wady tego. Po pierwsze, pliki binarne są prawie niemożliwe do debugowania (szczególnie dla osób tworzących treści). Nie możesz ich różnicować ani wersji. Ale będą mniejsze i ładują się szybciej.
To, co robią niektóre silniki (i co uważam za idealną sytuację), to wdrażanie dwóch ścieżek ładowania. Do programowania używasz pewnego rodzaju formatu tekstowego. Tak naprawdę nie ma znaczenia, jakiego konkretnego formatu używasz, o ile używasz solidnej biblioteki. W celu wydania przełączasz się na ładowanie wersji binarnej (mniejsze, szybsze ładowanie, być może pozbawione jednostek debugowania). Twoje narzędzia do edycji poziomów wypluwają oba. To o wiele więcej pracy niż tylko jeden format pliku, ale możesz w pewnym sensie uzyskać to, co najlepsze z obu światów.
Biorąc to wszystko pod uwagę, myślę, że trochę podskakujesz.
Pierwszym krokiem do rozwiązania tych problemów jest zawsze dokładne opisanie problemu. Jeśli wiesz, jakich danych potrzebujesz, to wiesz, jakie dane musisz przechowywać. Stamtąd jest to sprawdzalne pytanie optymalizacyjne.
Jeśli masz przypadek użycia do przetestowania, możesz przetestować kilka różnych metod przechowywania / ładowania danych, aby zmierzyć to, co uważasz za ważne (czas ładowania, użycie pamięci, rozmiar dysku itp.). Nie wiedząc o tym, trudno być bardziej szczegółowym.
źródło
Polecam użyć niestandardowego formatu pliku binarnego. W mojej grze mam tylko
SaveMap
metodę, która przechodzi i zapisuje każde pole za pomocąBinaryWriter
. Pozwala to również wybrać kompresję, jeśli chcesz, i daje większą kontrolę nad rozmiarem pliku. tzn. Zapiszshort
zamiast zamiast,int
jeśli wiesz, że nie będzie większy niż 32767. W dużej pętli zapisanie czegoś jakoshort
zamiastint
może oznaczać znacznie mniejszy rozmiar pliku.Ponadto, jeśli pójdziesz tą drogą, polecam pierwszą zmienną w pliku, która będzie numerem wersji.
Rozważmy na przykład klasę mapy (bardzo uproszczoną):
Powiedzmy, że chcesz dodać nową właściwość do mapy, powiedzmy
List
oPlatform
obiektach. Wybrałem to, ponieważ jest to trochę bardziej zaangażowane.Po pierwsze, zwiększasz
MapVersion
i dodajeszList
:Następnie zaktualizuj metodę zapisu:
Następnie, i tam właśnie naprawdę widzisz korzyści, zaktualizuj metodę ładowania:
Jak widać,
LoadMaps
metoda może nie tylko załadować najnowszą wersję mapy, ale także starsze wersje! Kiedy ładuje starsze mapy, masz kontrolę nad wartościami domyślnymi, których używa.źródło
Krótka historia
Dobrą alternatywą dla tego problemu jest przechowywanie poziomów w bitmapie, po jednym pikselu na płytkę. Korzystanie z RGBA pozwala na łatwe przechowywanie czterech różnych wymiarów (warstw, identyfikatorów, obrotu, odcienia itp.) Na jednym obrazie.
Długa historia
To pytanie przypomniało mi, kiedy Notch transmitował na żywo swój wpis o Ludum Dare kilka miesięcy temu, i chciałbym się podzielić na wypadek, gdybyś nie wiedział, co zrobił. Myślałem, że to naprawdę interesujące.
Zasadniczo używał map bitowych do przechowywania swoich poziomów. Każdy piksel w mapie bitowej odpowiadał jednemu „kafelkowi” na świecie (w jego przypadku tak naprawdę nie był to kafelek, ponieważ była to gra z prażonymi rodami, ale wystarczająco blisko). Przykład jednego z jego poziomów:
Jeśli więc używasz RGBA (8 bitów na kanał), możesz użyć każdego kanału jako innej warstwy i maksymalnie 256 rodzajów kafelków dla każdego z nich. Czy to wystarczy? Lub na przykład jeden z kanałów może utrzymywać obrót kafelka, jak wspomniano. Poza tym tworzenie edytora poziomów do pracy z tym formatem również powinno być dość trywialne.
A co najważniejsze, nie potrzebujesz nawet żadnego niestandardowego procesora treści, ponieważ możesz po prostu załadować go do XNA jak każdy inny Texture2D i odczytać piksele z powrotem w pętli.
IIRC użył czystej czerwonej kropki na mapie, aby zasygnalizować wrogowi, i w zależności od wartości kanału alfa dla tego piksela określałby typ wroga (np. Wartość alfa 255 może być nietoperzem, a 254 to zombie lub coś innego).
Innym pomysłem byłoby podzielenie obiektów gry na te, które są ustalone w siatce, i te, które mogą „drobno” poruszać się między płytkami. Zachowaj ustalone kafelki w mapie bitowej, a obiekty dynamiczne na liście.
Może nawet istnieć sposób na zmultipleksowanie jeszcze większej ilości informacji w tych 4 kanałach. Jeśli ktoś ma na to jakiś pomysł, daj mi znać. :)
źródło
Prawdopodobnie mógłbyś uciec z prawie wszystkim. Niestety współczesne komputery są tak szybkie, że ta przypuszczalnie stosunkowo niewielka ilość danych nie zrobiłaby zauważalnej różnicy czasu, nawet jeśli jest przechowywana w rozdętym i źle skonstruowanym formacie danych, który wymaga dużej ilości przetwarzania do odczytania.
Jeśli chcesz zrobić „właściwą” rzecz, tworzysz format binarny, jak sugerował Drackir, ale jeśli dokonujesz innego wyboru z jakiegoś głupiego powodu, prawdopodobnie nie wróci i nie ugryzie (przynajmniej nie pod względem wydajności).
źródło
Zobacz to pytanie: „Binarny XML” dla danych gry? Zdecydowałbym się również na JSON, YAML, a nawet XML, ale ma to dość duże obciążenie. Jeśli chodzi o SQLite, nigdy nie użyłbym tego do przechowywania poziomów. Relacyjna baza danych jest przydatna, jeśli spodziewasz się przechowywać dużo powiązanych danych (np. Zawiera linki relacyjne / klucze obce) i jeśli oczekujesz zadawania dużej liczby różnych zapytań, przechowywanie mapowania nie wydaje się robić tego rodzaju i dodałoby niepotrzebnej złożoności.
źródło
Podstawowym problemem tego pytania jest to, że łączy w sobie dwie koncepcje, które nie mają ze sobą nic wspólnego:
Możesz przechowywać swoje pliki jako zwykły tekst w dowolnym dowolnym formacie, JSON, skrypcie Lua, XML, dowolnym formacie binarnym itp. I żaden z nich nie będzie wymagał , aby reprezentacja tych danych w pamięci miała jakąkolwiek konkretną formę.
Zadaniem twojego kodu ładującego poziom jest konwersja paradygmatu przechowywania plików na reprezentację w pamięci. Na przykład mówisz: „Widzę mniej nadmiarowości danych przy użyciu bazy danych do przechowywania danych kafelków”. Jeśli chcesz mniej nadmiarowości, powinien to sprawdzić Twój kod ładujący poziom. To jest coś, z czym powinna sobie poradzić Twoja reprezentacja w pamięci.
To nie jest coś, czym powinien zajmować się Twój format pliku. I oto dlaczego: te pliki muszą skądś pochodzić.
Albo będziesz pisać je ręcznie, albo będziesz używał jakiegoś narzędzia, które tworzy i edytuje dane na poziomie. Jeśli piszesz je ręcznie, najważniejszą rzeczą, której potrzebujesz, jest format, który jest łatwy do odczytania i modyfikacji. Nadmiarowość danych nie jest czymś Twój Format nawet musi rozważyć, ponieważ będzie spędzać większość swojej czasie edycji plików. Czy chcesz ręcznie korzystać z wszelkich wymyślonych mechanizmów, aby zapewnić nadmiarowość danych? Czy nie byłoby lepiej wykorzystać swój czas, aby twój program ładujący zajął się tym?
Jeśli masz narzędzie, które je tworzy, faktyczny format ma znaczenie (co najwyżej) ze względu na czytelność. Możesz umieścić wszystko w tych plikach. Jeśli chcesz pominąć zbędne dane w pliku, po prostu zaprojektuj format, który może to zrobić, i spraw, aby Twoje narzędzie prawidłowo korzystało z tej możliwości. Twój format tilemap może obejmować kompresję RLE (kodowanie w czasie wykonywania), kompresję duplikacji i dowolne techniki kompresji, ponieważ jest to twój format tilemap.
Problem rozwiązany.
Relacyjne bazy danych (RDB) istnieją, aby rozwiązać problem wykonywania złożonych wyszukiwań w dużych zestawach danych zawierających wiele pól danych. Jedynym rodzajem wyszukiwania, jakie przeprowadzasz na mapie kafelków, jest „zdobądź kafelek w pozycji X, Y”. Każda tablica może to obsłużyć. Używanie relacyjnej bazy danych do przechowywania i utrzymywania danych mapy warstw byłoby ekstremalnie przesadne i nie warte niczego.
Nawet jeśli wbudujesz kompresję w swoją reprezentację w pamięci, nadal będziesz w stanie łatwo pokonać RDB pod względem wydajności i zajmowanego miejsca w pamięci. Tak, będziesz musiał wdrożyć tę kompresję. Ale jeśli rozmiar danych w pamięci stanowi taki problem, że należy rozważyć RDB, prawdopodobnie prawdopodobnie chcesz wdrożyć określone rodzaje kompresji. Będziesz szybszy niż RDB i jednocześnie mniej pamięci.
źródło
W przypadku naprawdę prostych danych prawdopodobnie wybrałbym trasę XML, jeśli znasz już ładowanie i parsowanie XML.
źródło