Podczas ładowania bardzo dużych map metoda ładowania wyrzuca wyjątek z pamięci, gdy tworzone jest nowe wystąpienie kafelka mapy. Chciałbym, aby cała mapa była przetwarzana przynajmniej w aplikacji serwera (i na kliencie, jeśli to możliwe). Jak mam rozwiązać ten problem?
UPD: pytanie brzmi, jak sprawić, by gra przestała się zawieszać, gdy jest jeszcze wolna pamięć do użycia. Jeśli chodzi o dzielenie mapy na części, jest to dobre podejście, ale nie w moim przypadku.
UPD2: Na początku przypisywałem teksturę każdemu nowemu wystąpieniu klasy kafelków i to wymagało tyle pamięci (także czasu ładowania). Teraz zajmuje około cztery razy mniej miejsca. Dzięki wszystkim mogę teraz uruchamiać ogromne mapy, nie myśląc o tym, aby je teraz rozbić na części.
UPD3: Po przepisaniu kodu, aby sprawdzić, czy tablice właściwości kafelków działają szybciej lub zużywają mniej pamięci (niż te właściwości w odpowiednich kafelkach jako właściwości obiektów), odkryłem, że nie tylko zajęło mi to dużo czasu, aby to wypróbować, ale nie przyniósł żadnej poprawy wydajności i sprawił, że gra była wyjątkowo trudna do debugowania.
źródło
Odpowiedzi:
To zdecydowanie sugeruje, że nie reprezentujesz poprawnie swoich kafelków, ponieważ oznaczałoby to, że każdy kafelek ma rozmiar ~ 80 bajtów.
Musisz zrozumieć, że należy rozdzielić koncepcję gry kafelka od kafelka wizualnego, który widzi użytkownik. Te dwie koncepcje to nie to samo .
Weźmy na przykład Terraria. Najmniejszy świat Terraria zajmuje 4200 x 1200 płytek, czyli 5 milionów płytek. Ile pamięci zajmuje reprezentowanie tego świata?
Każda płytka ma warstwę pierwszoplanową, warstwę tła (ściany tła), „warstwę drutu”, do której prowadzą przewody, i „warstwę mebli”, do której prowadzą elementy mebli. Ile pamięci zajmuje każdy kafelek? Znowu mówimy tylko koncepcyjnie, a nie wizualnie.
Kafelek pierwszego planu można łatwo przechowywać w nieoznaczonym skrócie. Nie ma więcej niż 65536 rodzajów kafelków pierwszego planu, więc nie ma sensu używać więcej pamięci niż to. Kafelki tła mogą z łatwością zawierać bajt bez znaku, ponieważ istnieje mniej niż 256 różnych rodzajów kafelków tła. Warstwa drutu jest czysto binarna: albo płytka ma drut, albo nie. To jeden bit na płytkę. A warstwa mebli może znów być bajtem niepodpisanym, w zależności od liczby możliwych różnych mebli.
Całkowity rozmiar pamięci na kafelek: 2 bajty + 1 bajt + 1 bit + 1 bajt: 4 bajty + 1 bit. Tak więc całkowity rozmiar małej mapy Terraria wynosi 20790000 bajtów lub ~ 20 MB. (uwaga: obliczenia te oparte są na Terrarii 1.1. Od tego czasu gra znacznie się rozwinęła, ale nawet współczesna Terraria może zmieścić się w 8 bajtach na lokalizację kafelków, czyli ~ 40 MB. Nadal jest to całkiem znośne).
Nigdy nie należy przechowywać tej reprezentacji jako tablic klas C #. Powinny to być tablice liczb całkowitych lub coś podobnego. AC # struct też by działało.
Teraz, gdy nadchodzi czas na narysowanie części mapy (zwróć uwagę na nacisk), Terraria musi przekształcić te płytki koncepcyjne w rzeczywiste płytki. Każda płytka musi faktycznie wybrać obraz pierwszego planu, obraz tła, opcjonalny obraz mebli i mieć obraz z drutu. W tym miejscu pojawia się XNA z różnymi arkuszami sprite i tym podobne.
Musisz przekonwertować widoczną część mapy pojęciowej na rzeczywiste płytki arkuszy sprite XNA. Nie powinieneś próbować konwertować całej rzeczy naraz . Każdy przechowywany kafelek powinien być po prostu indeksem z napisem „Jestem kafelkiem typu X”, gdzie X jest liczbą całkowitą. Używasz tego indeksu liczb całkowitych, aby pobrać duszka, którego używasz do jego wyświetlenia. I używasz arkuszy sprite XNA, aby uczynić to szybszym niż tylko rysowanie pojedynczych quadów.
Teraz widoczny obszar kafelków musi zostać podzielony na różne części, abyś nie budował ciągle arkuszy duszka przy każdym ruchu kamery. Więc możesz mieć 64x64 kawałki świata jako arkusze sprite. Niezależnie od tego, które fragmenty świata 64x64 są widoczne z aktualnej pozycji kamery gracza, są to losowane fragmenty. Wszelkie inne fragmenty nie mają nawet arkuszy sprite; jeśli fragment spadnie z ekranu, wyrzucasz ten arkusz (uwaga: tak naprawdę go nie usuwasz; trzymasz go w pobliżu i określasz ponownie jako nowy fragment, który może być później widoczny).
Twój serwer nie musi wiedzieć ani dbać o wizualną reprezentację kafelków. Jedyne, na czym trzeba się przejmować, to reprezentacja konceptualna. Użytkownik dodaje tutaj kafelek, więc zmienia on indeks kafelków.
źródło
Podziel teren na regiony lub fragmenty. Następnie ładuj tylko te części, które są widoczne dla graczy, i te, które nie są. Możesz myśleć o tym jak o przenośniku taśmowym, w którym ładujesz kawałki na jednym końcu i rozładowujesz je na drugim, gdy gracz się porusza. Zawsze wyprzedzając gracza.
Możesz także użyć sztuczek takich jak instancja. Gdzie, jeśli wszystkie kafelki jednego typu mają tylko jedną instancję w pamięci i są rysowane wielokrotnie we wszystkich lokalizacjach, w których jest to potrzebne.
Możesz znaleźć więcej takich pomysłów tutaj:
Jak to zrobili: Miliony płytek w Terraria
„Strefowanie” obszarów na dużej mapie kafelków, a także lochy
źródło
Odpowiedź Byte56 jest dobra i zacząłem pisać to jako komentarz do tego, ale stało się to zbyt długo i zawiera kilka wskazówek, które mogą być bardziej pomocne w odpowiedzi.
Podejście to jest absolutnie tak samo dobre dla serwera, jak i dla klienta. W rzeczywistości jest to prawdopodobnie bardziej odpowiednie, biorąc pod uwagę, że serwer zostanie poproszony o zajęcie się znacznie większą liczbą obszarów niż klient. Serwer ma inne wyobrażenie o tym, co należy załadować niż klient (dba o wszystkie regiony, o które dbają wszyscy podłączeni klienci), ale jest równie zdolny do zarządzania działającym zestawem regionów.
Nawet jeśli potrafisz zmieścić w pamięci tak gigantyczną mapę (a najprawdopodobniej nie możesz), nie chcesz . Załadowanie danych, których nie trzeba od razu wykorzystywać, jest absolutnie zerowe, jest nieefektywne i powolne. Nawet jeśli umieścisz to w pamięci, najprawdopodobniej nie będziesz w stanie przetworzyć tego wszystkiego w rozsądnym czasie.
Podejrzewam, że sprzeciwiłeś się rozładowaniu niektórych regionów, ponieważ twoja aktualizacja świata jest iteracyjna po wszystkich kafelkach i ich przetwarzaniu? Jest to z pewnością ważne, ale nie wyklucza załadowania tylko niektórych części. Zamiast tego po załadowaniu regionu zastosuj wszelkie pominięte aktualizacje, aby zaktualizować go. Jest to również znacznie bardziej wydajne pod względem przetwarzania (koncentracja dużego wysiłku na małym obszarze pamięci). Jeśli wystąpią jakiekolwiek efekty przetwarzania, które mogą przekroczyć granice regionu (np. Przepływ lawy lub przepływ wody, który przeniesie się do innego regionu), to połącz te dwa regiony razem, aby po załadowaniu jednego z nich tak samo było z drugim. Idealnie byłoby, gdyby takie sytuacje zostały zminimalizowane, w przeciwnym razie szybko znajdziesz się ponownie w przypadku „wszystkie regiony muszą być ładowane przez cały czas”.
źródło
Wydajnym sposobem, który wykorzystałem w grze XNA było:
Możesz także użyć w ten sposób pojedynczej dużej mapy, jeśli nie jest ona TAK duża i użyj przedstawionej logiki.
Oczywiście rozmiar twoich tekstur musi być zrównoważony, a rozdzielczość płaci w tym świetną cenę. Spróbuj pracować w niższej rozdzielczości i, jeśli potrzebujesz, zrób pakiet wysokiej rozdzielczości do załadowania, jeśli ustawienie konfiguracji jest ustawione na.
Za każdym razem będziesz musiał zapętlić tylko odpowiednie części tej mapy i renderować tylko to, co jest potrzebne. W ten sposób znacznie poprawisz wydajność.
Serwer może szybciej przetwarzać ogromną mapę, ponieważ nie musi renderować (jednej z najwolniejszych operacji) gry, więc logika może polegać na wykorzystaniu większych fragmentów lub nawet całej mapy, ale przetworzyć tylko logikę wokół graczy, tak jak w dwukrotności pola widzenia gracza wokół gracza.
Rada tutaj jest taka, że pod żadnym pozorem nie jestem ekspertem od gier, a moja gra została stworzona dla końcowego projektu ukończenia studiów (który miałem najlepszy wynik), więc może nie mam racji w każdym punkcie, ale mam przeszukałem strony internetowe i strony takie jak http://www.gamasutra.com oraz witrynę twórców XNA creators.xna.com (w tej chwili http://create.msdn.com ) w celu zebrania wiedzy i umiejętności, a dla mnie dobrze .
źródło
Zarówno
źródło
texture2d
? Czy każdy kafelek ma unikatowy lub czy mają wspólne tekstury? Również, gdzie masz to powiedział? Na pewno nie umieściłeś tego w swoim pytaniu, gdzie powinny znajdować się informacje. Proszę lepiej wyjaśnić swoje szczególne okoliczności.W odpowiedzi na aktualizację nie można przydzielić tablic o
Int32.MaxValue
większym rozmiarze. Musisz podzielić to na kawałki. Możesz nawet umieścić go w klasie opakowania, która eksponuje elewację podobną do tablicy:źródło