Chcę stworzyć ogromną mapę świata; rozmiar co najmniej 8000 x 6000 pikseli. Podzieliłem go na siatkę 10 × 10 obrazów PNG o wymiarach 800 × 600 pikseli. Aby uniknąć ładowania wszystkiego do pamięci, obrazy powinny być ładowane i rozładowywane w zależności od pozycji gracza w siatce.
Na przykład tutaj jest gracz na pozycji (3,3)
:
Gdy przesuwa się w prawo (4,3)
, trzy obrazy po lewej stronie zostają cofnięte, a trzy obrazy po prawej są przydzielane:
Prawdopodobnie w każdej komórce siatki powinien znajdować się próg uruchamiający ładowanie i rozładowywanie. Ładowanie powinno prawdopodobnie nastąpić w osobnym wątku.
Jak zaprojektować taki system?
Odpowiedzi:
Tutaj jest kilka problemów do rozwiązania. Pierwszym jest ładowanie i rozładowywanie płytek. ContentManager domyślnie nie pozwala na rozładowanie określonych treści. Jednak niestandardowe wdrożenie tego:
Drugi problem dotyczy sposobu decydowania, które płytki należy załadować i rozładować. Poniższe rozwiązałoby ten problem:
Ostatnim problemem jest to, jak zdecydować, kiedy załadować i rozładować. To chyba najłatwiejsza część. Można to po prostu zrobić w metodzie Update () po określeniu pozycji ekranu gracza:
Oczywiście musisz również narysować kafelki, ale biorąc pod uwagę tablicę tileWidth, tileHeight i Tiles [], powinno to być trywialne.
źródło
To, co chcesz zrobić, jest dość powszechne. Aby uzyskać fajny samouczek na temat tej i innych popularnych technik, sprawdź serię silników kafelkowych .
Jeśli nie zrobiłeś czegoś takiego wcześniej, polecam obejrzenie serialu. Jeśli jednak chcesz, możesz uzyskać ostatni kod z samouczków. Jeśli zrobisz to później, sprawdź metodę losowania.
Krótko mówiąc, musisz znaleźć swoje minimalne i maksymalne punkty X / Y wokół gracza. Kiedy już to zrobisz, po prostu przejrzyj każdy z nich i narysuj ten kafelek.
Jak widać, wiele się dzieje. Potrzebujesz kamery, która utworzy matrycę, musisz przekonwertować bieżącą lokalizację pikseli na indeks kafelków, znajdziesz swoje punkty min / max (dodaję trochę do mojego MAXa, aby nieco wykraczał poza widoczny ekran) ), a następnie możesz go narysować.
Naprawdę sugeruję obejrzenie serii samouczków. Omawia twój obecny problem, jak stworzyć edytor kafelków, utrzymywać gracza w granicach, animację sprite, interakcję AI itp.
Na marginesie, TiledLib ma to wbudowane. Możesz także przestudiować ich kod.
źródło
Pracowałem nad czymś bardzo podobnym do mojego obecnego projektu. To krótkie podsumowanie tego, jak to robię, z kilkoma dodatkowymi notatkami na temat tego, jak ułatwić sobie życie.
Dla mnie pierwszym problemem było rozbicie świata na mniejsze części, które byłyby odpowiednie do załadunku i rozładunku w locie. Ponieważ używasz mapy opartej na kafelkach, ten krok staje się znacznie łatwiejszy. Zamiast uwzględniać pozycje każdego obiektu 3D na poziomie, Twój poziom jest już ładnie podzielony na kafelki. To pozwala po prostu rozbić świat na kawałki X na Y i załadować je.
Będziesz chciał to zrobić automatycznie, a nie ręcznie. Ponieważ używasz XNA, masz możliwość korzystania z potoku treści z niestandardowym eksporterem treści poziomu. Jeśli nie znasz sposobu na uruchomienie procesu eksportu bez ponownej kompilacji, szczerze odradzam. Chociaż kompilacja C # nie jest tak wolna jak zwykle C ++, nadal nie musisz ładować Visual Studio i rekompilować gry za każdym razem, gdy wprowadzasz drobne zmiany na mapie.
Inną ważną rzeczą tutaj jest upewnienie się, że używasz dobrej konwencji nazewnictwa dla plików zawierających części twojego poziomu. Chcesz wiedzieć, że chcesz załadować lub rozładować fragment C, a następnie wygenerować nazwę pliku, którą musisz załadować, aby to zrobić w czasie wykonywania. Na koniec pomyśl o drobiazgach, które mogą ci pomóc w dalszej drodze. Naprawdę miło jest móc zmienić wielkość porcji, ponownie wyeksportować, a następnie natychmiast zobaczyć jej wpływ na wydajność.
W czasie wykonywania jest to nadal dość proste. Będziesz potrzebował sposobu asynchronicznego ładowania i rozładowywania fragmentów, ale jest to w dużej mierze zależne od tego, jak działa Twoja gra lub silnik. Drugi obraz jest dokładnie poprawny - musisz określić, które części powinny zostać załadowane lub rozładowane, i złóż odpowiednie wnioski, aby tak było, jeśli nie jest jeszcze. W zależności od liczby załadowanych fragmentów naraz możesz to zrobić za każdym razem, gdy gracz przekroczy granicę z jednej porcji do drugiej. W końcu, tak czy inaczej, chcesz się upewnić, że wystarczająco dużo jest załadowane, że nawet w najgorszym (rozsądnym) czasie ładowania część jest nadal ładowana, zanim gracz ją zobaczy. Prawdopodobnie będziesz chciał dużo grać z tym numerem, dopóki nie uzyskasz równowagi między wydajnością a zużyciem pamięci.
Jeśli chodzi o rzeczywistą architekturę, będziesz chciał wyodrębnić proces faktycznego ładowania i rozładowywania danych z pamięci od procesu określania, co powinno zostać załadowane / rozładowane. Podczas pierwszej iteracji nie martwiłbym się nawet wydajnością ładowania / rozładowywania, a jedynie najprostszą rzeczą, która mogłaby ewentualnie działać, i zapewniał, że generujesz odpowiednie żądania w odpowiednim czasie. Następnie możesz spojrzeć na optymalizację sposobu ładowania, aby zminimalizować śmieci.
Napotkałem wiele dodatkowych problemów ze względu na silnik, którego używałem, ale jest to dość specyficzne dla implementacji. Jeśli masz jakieś pytania dotyczące tego, co zrobiłem, proszę o komentarz, a ja zrobię, co w mojej mocy, aby pomóc.
źródło