Ja bym bardzo prosił, aby mojej platformówki nie dawać jako przykładu tworzenia gier, dlatego że jest ona jedynie eksperymentem (jest wysoce specyficzna), nie zawierająca czegokolwiek związanego z poprawnym dziś gamedevem. A że sam tworzeniem ”prawdziwych” gier się nie zajmuję, to każdy aspekt tego projektu po prostu wymyśliłem. Wiem co nieco na temat tworzenia gier na Famicoma, ale taka wiedza dziś nie jest zbyt przydatna. ;)
Tak więc mogę napisać o tym, w jaki sposób zaimplementowałem kolizje bohatera z platformami w mojej demówce, jednak głównie jako ciekawostkę, bo zapewne istnieją lepsze rozwiązania. Zresztą wszystko i tak zależy od konkretnych wymagań.
W mojej demówce, bohater jest prostokątnym klockiem. Rozmiar hitboksu klocka to zawsze 14x16
pikseli – bez względu na to czy ciało bohatera jest rozciągnięte, czy nie (rozciąga się głównie podczas opadania). Owe rozciągnięcie jest jedynie efektem wizualnym, bo może takim być – w końcu kolizja z platformą trwa tak krótko, że rozciągnięte ciało bohatera nachodzące na platformę jest niezauważalne.
Platformy zbudowane są z kafli o rozmiarach 16x16
pikseli. Hitboks bohatera mieści się ładnie wewnątrz kafli, dlatego też określiłem, że do sprawdzania kolizji używane będą wierzchołki. Hitboks bohatera opisuje struktura zawierająca współrzędne wszystkich czterech wierzchołków, natomiast hitboksy danej warstwy poziomu to po prostu dwuwymiarowa macierz wartości logicznych.
Aby przeprowadzić ruchu bohatera, wykonuje się przesunięcie współrzędnych wierzchołków o zadane wartości. Dla ruchu poziomego jest to stała wartość równa 2
, a dla pionowego, zależna od prędkości wznoszenia/opadania (od 1
do 16
). Aby wykryć kolizję, najpierw określa się kierunek ruchu – lewo lub prawo oraz góra lub dół – i wybiera dwie pary wierzchołków, jedna para dla ruchu poziomego i jedna dla pionowego. Jeśli bohater ma być przesunięty np. w prawo, to bierze się dwa prawe wierzchołki, jeśli w dół, to dwa dolne. Następnie dodaje się offsety do tych współrzędnych i dzieli się je przez rozmiar kafli – w ten sposób uzyskujemy relatywne indeksy komórek w macierzy hitboksów warstwy poziomu.
Ostatnim krokiem jest sprawdzenie, czy komórki w macierzy hitboksów spod obliczonych relatywnych indeksów zawierają True
(blok platformy) lub False
(pusta przestrzeń). Jeśli False
, to bohater może się przesunąć (brak kolizji), więc aktualizuje się jego pozycję, a jeśli True
, to należy wyrównać pozycję do danego kafla (nastąpiła kolizja). Gra oprócz statycznych platform nie posiada żadnych innych obiektów, z którymi bohater może się zderzyć, więc kolizje z platformami to jedyne co należało oprogramować.
Oczywiście zabezpieczeń jest więcej. Należy też sprawdzać kolizje ze ścianami ekranu (dla ruchu poziomego), tak aby bohater nie mógł wyjść poza planszę, a także z granicami planszy (dla ruchu pionowego), aby bohater z niej nie wypadł (wtedy podczas testowania kolizji wykroczonoby poza zakres macierzy hitboksów). Dlatego każdy poziom określają dwa obszary:
- widoczna przestrzeń (
Area
) – do której wyrównywana jest kamera oraz ciało bohatera w ruchu poziomym,
- granice planszy (
Bounds
) – do których wyrównywane jest ciało bohatera w ruchu pionowym (tylko po wpadnięciu w przepaść).
Różnica tych obszarów to tzw. margines, gdyby to kogokolwiek obchodziło. Bohater może się poruszać po marginesie jedynie w ruchu pionowym. Śmierć bohatera następuje w momencie dotknięcia granicy planszy (Bounds
), co zostało wykorzystane do stworzenia efektu niewielkiej przerwy, trwającej od zniknięcia bohatera pod/nad ekranem do jego zabicia, a tym samym do zaciemnienia ekranu i resetu. Owa przerwa to ruch pionowy właśnie po marginesie, a jej długość jest uzależniona od prędkości opadania (im wyższa prędkość, tym szybciej bohater dotknie granicy, więc i krótsza przerwa).
Rozmiar kafli warstw poziomów oraz rozmiar bohatera są dobrane w taki sposób, aby powyższe obliczenia nigdy się nie myliły. Maksymalna prędkość opadania określona jest jako 16
pikseli na klatkę i jest równa rozmiarowi pojedynczego kafla. Jest ona konieczna, bo gdyby nie była określona, to podczas opadania np. 30
pikseli na klatkę, bohater mógłby przelecieć przez kafel poziomu (przesunięcie współrzędnych wierzchołków byłoby większe niż rozmiar kafla). To tak gwoli ścisłości.
Ok, w dużym skrócie byłoby tyle.
Jeśli kogoś potrzebuje szczegółów to z chęcią je podam, choć tak jak wspomniałem – ten projekt jest jedynie eksperymentem. Jednym z jego założeń był opis ruchu na podstawie liczb całkowitych**1** oraz liczenie klatek zamiast czasu na podstawie delty, czyli podobnie jak miało to miejsce w grach na Famicoma. O ile było to fajne wyzwanie, to taka technika nie jest używana we współczesnych grach, więc nie polecam jej stosować.
[1] Wyjątkiem jest ruch świetlików po okręgu, do czego użyłem liczb zmiennoprzecinkowych. Co prawda floaty nie są konieczne (bo mamy np. odmianę Midpoint circle algorithm bazującą na intach), jednak użycie floatów było po prostu krótsze w implementacji.
czyKoliduja
, jeśli chcesz się trzymać polskiego nazewnictwa.