Jak zaprojektować mapę świata z podziałem na prowincje i regiony? Nie jest to trywialne zadanie, a w internecie jak na złość nie ma prawie wcale użytecznych materiałów. Kilka lat temu, gdy próbowałem taką mapę zaimplementować, poległem. Dopiero teraz, przy produkcji tolkienowskiego RTSa udało mi się wymyślić działający sposób. I w gruncie rzeczy, nie jest wcale takie trudne, gdy się na spokojnie usiądzie i przemyśli.

screen

Tak to wygląda u mnie. Nawet ładnie!

O co nam chodzi?

W skrócie: o mapę jak w grach Paradoxu, np. Europie Universalis. Nie wiem, czy jest jakaś specjalna, fachowa nazwa, ale w większości wypadków gdy znalazłem coś w internecie wszyscy nazywali to „mapą z EU2”. To będzie nasz punkt wyjścia.

Musimy mieć możliwość łatwej edycji mapy, najlepiej w jakimś edytorze. Musimy mieć możliwość kolorowania różnych prowincji i rozpoznawania, nad którą konkretnie użytkownik trzyma kursor i w którą kliknął. No i całość musi działać w miarę szybko – nie chcemy przecież ograniczać liczby regionów na mapie.

Jak?

Najpierw musimy sobie uświadomić, że to, co użytkownik widzi na ekranie to nie do końca to samo co dzieje się w aplikacji. Musimy oddzielić warstwę logiki (obsługi myszki gracza; atrybutów prowincji np. jej właściciel czy populacja) od warstwy prezencji (czyli kolorowa mapa). Nie możemy sobie pozwolić na aktualizowanie całego obrazu co klatkę, bo to zje wydajność. Zajmujemy się tylko pojedynczymi prowincjami/regionami.

Jak to działa

Może nie wygląda zachęcająco, ale w gruncie rzeczy właśnie tak wygląda u mnie cały proces tworzenia mapy.

Jak to działa? Na pierwszym obrazku mamy mapę, gdzie każda prowincja zapisana jest innym kolorem, a granice między nimi są czarne. Potem edytor map mieli to, oddziela obrazki każdego regionu i zapisuje w osobnym pliku, razem z informacją o pozycji każdego regionu, tak, żeby dało się potem to poskładać z powrotem. Na końcu łączymy mapę i możemy sprawdzić nad jaką prowincją user trzyma myszkę poprzez proste getPixel(x, y).blue.

Format zapisu mapy

Zapisana mapa składa się z dwóch elementów:

  1. plik graficzny, gdzie wszystkie regiony umieszczone są obok siebie
  2. plik tekstowy o formacie:
    ID_REGIONU POZYCJA_NA_OBRAZKU (X, Y, X2, Y2) POZYCJA_NA_MAPIE (X, Y, X2, Y2)Możemy też zapisać tutaj dodatkowe informacje z edytora map, np. u mnie to jest zaludnienie danego regionu czy surowce naturalne.

W grze wczytujemy oba pliki i tworzymy tablicę regionów. Każdy z nich powinien mieć swój obrazek (wczytany z pierwszego pliku na podstawie POZYCJA_NA_OBRAZKU) oraz pozycję na docelowej mapie świata (POZYCJA_NA_MAPIE). To daje nam już duże pole do manewru.

Teraz tworzymy bitmapę, kopię mapy świata, która służyć będzie do interakcji z użytkownikiem. Po prostu renderujemy wszystkie regiony kolorując je na RGB(0, 0, ID_REGIONU). Po co to? Gdy chcemy sprawdzić, nad jaką prowincją znajduje się kursor myszy, robimy to poprzez getPixel (mouseX, mouseY).blue.
Proste, prawda?

Wyświetlanie mapy

Nie wyświetlamy wszystkich prowincji co klatkę! Renderujemy je tylko w przypadku zmiany (np. zmienił się właściciel i chcemy zmienić kolor z niebieskiego na czerwony) do osobnej bitmapy (drugiej, pierwsza służy tylko do interakcji z graczem) i dopiero ją wysyłamy na ekran. To właśnie po to jest to cale rozdrobienie na osobne obrazki, żebyśmy potem mogli wyświetlać tylko pojedyncze prowincje. Te, które akurat potrzebujemy. Tutaj fajnie widać, że w EU4 rozwiązano to w całkiem podobny sposób.

 

Starałem się streścić pomysł jak najkrócej i jak najbardziej czytelnie. Gdyby mi się to jednak nie udało – czekam na komentarze 🙂

  • Did you like it?
  • Yes   No