„Strefowanie” obszarów na dużej mapie kafelków, a także lochy

10

Moja gra ma mapę taką jak Minecraft, w sposób pseudoinfinite i losowo generowany. I duży. Powiedzmy, że użytkownik zbadał strefę 1000 x 1000 (tutaj 2D), czyli 1 000 000 płytek.

Oczywiście nie będę w stanie zapisać tego wszystkiego w pamięci. Nie chcę też po prostu ignorować wszystkiego z 10 kafelków lub w dowolnym promieniu - oba nic się nie zaktualizują (wszyscy NPC, może płytki reaktywne) i musiałbym pracować z niezręcznymi pozycjami, takimi jak 1382,12918.

Więc jeśli miałbym podzielić go na kawałki lub strefy lub cokolwiek, powiedzmy, płytki 64x64, musiałbym zapisać każdą płytkę i pozycję obiektu w następujący sposób:

Kawałek a, b. Pozycja x, y.

Ale co jeśli chciałbym lochy i tym podobne na mojej mapie? Tak więc pojedyncza płytka, która może prowadzić do 40-poziomowego lochu, każdy o powierzchni 40x40. Nie mogę dokładnie przechowywać tych na tej samej mapie.

I jest strona pamięci; ile mógłbym rozsądnie jednocześnie zapisać w pamięci? Mógłbym dość łatwo układać płytki według ID i mieć tam normalną tablicę 2D. Czy też istnieje bardziej skuteczne rozwiązanie niż posiadanie pliku danych z typami kafelków ? Więc dla 64x64 .. będzie to tylko 20K lub cokolwiek innego. Chciałbym załadować jak najwięcej otoczenia, aby uzyskać najbardziej realistyczną aktualizację. Ale nie wiem, jakie ograniczenia mogę przekroczyć, nie pogrążając się w pamięci.

Kaczka komunistyczna
źródło

Odpowiedzi:

9

Chociaż zgadzam się z sentymentem: „nie przejmuj się, chyba że jest to udowodniony problem”, myślę, że warto o tym pomyśleć od samego początku: retro-montaż rozwiązania jest o wiele bardziej bolesny. I tak, tylko aktualizowanie „pobliskich” kafelków jest dobrym rozwiązaniem. Jednak wydajne przechowywanie i adresowanie przedmiotów w świecie gry jest bardzo ważne ze względu na wydajność.

To, o czym tak naprawdę myślisz, to rzadki zestaw danych: coś, w którym potencjalne indeksy są duże (lub nieograniczone), ale w rzeczywistości wykorzystywany jest tylko niewielki odsetek. Kluczową kwestią jest to, że nie wiesz dokładnie, która proporcja zostanie wykorzystana.

Standardowym rozwiązaniem rzadkiego problemu z zestawem danych jest oddzielenie indeksu / adresowalności od faktycznego przechowywania danych. Więc jeśli obiekt kafelkowy jest drogi, przechowuj go w zwartej formie (np. W płaskiej tablicy). Ale pozwól mu być indeksowany przez tańszy obiekt. W najprostszej postaci może to być macierz 2D (lub 3D), którą można łatwo indeksować według współrzędnych, ale każdy element w macierzy jest po prostu indeksem. Następnie używasz tego indeksu do wyszukiwania rzeczywistej zawartości kafelków w osobnej, zwartej tablicy. Jeśli zawartość kafelka jeszcze nie istnieje, dodaj je na końcu tablicy i zapisz indeks w macierzy 3D.

Rozwiązanie staje się bardziej złożone, jeśli chcesz wesprzeć usuwanie zawartości (ponieważ prowadzi to do fragmentacji tablicy zawartości), a jeśli zawartość kafelków jest tania, to dodatkowa waga indeksu (indeksy 32-bitowe lub 64-bitowe) prawdopodobnie przytłoczy oszczędności wynikające z nieprzechowywania każdej potencjalnej płytki. Jest to również dodatkowe wyszukiwanie, które obniży wydajność pamięci podręcznej.

Możesz uzyskać jeszcze większą wydajność przechowywania, wprowadzając dodatkowe warstwy pośrednie. Załóżmy, że organizujesz kafelki w kawałki, a ich fragmenty mają ziarnistość 64 x 64 x 64. Biorąc pod uwagę płytkę o wartości 125, 1, 132, wiesz, że należy ona do fragmentu (1,0,2). Masz więc świat, który składa się ze zwartej tablicy porcji i macierzy indeksów porcji (-1, jeśli porcja nie istnieje). Zawartość każdego fragmentu (jeśli jest obecny) to macierz indeksów kafelków 64x64x64 (-1, jeśli kafelek jeszcze nie istnieje) oraz zwarta tablica używanych kafelków. W ten sposób nie wypalasz ogromnej liczby indeksów kafelków dla kawałków, które nigdy nie są używane. Postępując zgodnie z tym podejściem i wybierając rozsądne liczby dla ziarnistości fragmentu, możesz masowo skalować swój wszechświat i kontrolować wykorzystanie pamięci. Właściwie, jeśli zrobisz kawałki o wymiarach 32 x 32 x 32,

Możesz także wykonywać podstępne sztuczki, na przykład używać bitów wysokiego rzędu lub indeksów kafelków, aby oznaczać coś wyjątkowego. Jeśli więc wpis w macierzy kafelków ma ustawiony górny bit, wówczas 31 niższych bitów nie oznacza indeksu kafelków, a zamiast tego oznacza „indeks wypaczenia” lub coś podobnego, i można to sprawdzić na osobnej liście aby znaleźć współrzędne, do których prowadzi.

MrCranky
źródło
Ostrzegam każdego, kto natknie się na to pytanie w przyszłości: Chociaż nic w tym poście nie jest niepoprawne, jest to naprawdę okropne dla gry takiej jak Minecraft, która ma nieliczny świat. (I MrCranky mówi tyle.) Tylko swój świat korzyści od tego, czy posiada ono kilka latków i wiele nothings , a nie kilku latków i wiele pustych opakowań .
1
Zgadzam się szczerze, że koszty związane z utrzymaniem rzadkiego rozwiązania do przechowywania zestawu danych są dość wysokie, więc wybrałbyś to tylko wtedy, gdy równowaga była wyraźnie wypaczona w kierunku rzadkości. Kluczowymi czynnikami są tutaj: koszt elementu danych i prawdopodobieństwo, że element danych nie zostanie wykorzystany. Minecraft ma ogromny, ogromny potencjał danych (pewna duża liczba płytek w każdym kierunku). Rzeczywista proporcja potencjalnego użytego świata gry jest niewielka. Nawet jeśli poruszasz się po całym świecie, twierdzisz, że jest to duża część, ale wciąż stanowi niewielką część potencjalnych elementów danych.
MrCranky,
1
Co odróżnia Minecraft od innych, to fakt, że koszt jednostkowy jest niewielki - może to jeden bajt pamięci? Jedna wartość wskazuje „nic tam nie ma”, reszta to typy bloków, które można łatwo zmieścić w bajcie. Więc nie miałbyś indeksu dla pojedynczego bloku, to po prostu szalone, indeks jest znacznie większy niż tylko przechowywanie rzeczywistej wartości bloku. Ale reguły dotyczące rzadkich zestawów danych nadal działają na wyższych poziomach. Każda porcja (np. 64 x 64 x 64) jest kosztowna w przechowywaniu (bloki 256 KB), więc jeśli możesz użyć pojedynczej małej wartości, aby wskazać jej brak lub jej adres, jeśli istnieje, możesz zaoszczędzić ogromne miejsce.
MrCranky,
6

Dlaczego nie możesz przechowywać miliona kafelków w pamięci? Nawet mój telefon ma 256 MB pamięci RAM; będzie milion pustych kafelków, 4-32 MB?


źródło
Po prostu wydawało się, że jest tak ogromna liczba do przechowywania. Poza tym ich aktualizacja zajmie dużo czasu.
Kaczka komunistyczna
2
O wiele łatwiej jest po prostu zignorować zestaw kafelków dla aktualizacji niż w przypadku niektórych rozwiązań stronicowania dysku. Po prostu je zignoruj. Nie aktualizuj płytek więcej niż X odległości od znanego aktora.
2
@TheCommunistDuck Jedyne, co się liczy, to całkowita ilość pamięci użytej do przechowywania kafelków, a nie liczba kafelków.
Justin
1
Uzgodniono z Kragenem i Joe. Nie martw się o to, co często brzmi - zrób matematykę i opracuj ją. To raczej nie będzie problem.
Kylotan