Tworzenie gier w JavaScript: kolizje – część pierwsza

Dziś omówię jeden z ważniejszych elementów, na które składa się tworzenie gier w JavaScript: kolizje. Po krytycznym przyjrzeniu się moim poprzednim projektom, doszedłem do wniosku, że wykrywanie kolizji było zdecydowanie najsłabszą ich stroną.Po prostu jakoś działały. Ale nie chcę zadowalać się efektem „jakoś”. Dlatego postanowiłem zgłębić temat. Trochę szperania w internecie, trochę eksperymentów i już wiem znacznie więcej 🙂 Swoją nowo zdobytą wiedzą podzielę się z wami w tym poście 🙂

Tworzenie gier w JavaScript: kolizje

Czym jest wykrywanie kolizji? Jest to technika programistyczna, która ma na celu sprawdzenie, czy obiekty w grze nie stykają się. Po wykryciu kolizji zazwyczaj coś się dzieje, statek obcych wybucha (kolizja z rakietą), postać nie może iść dalej (kolizja ze ścianą), postać zdobywa przedmiot (kolizja z przedmiotem). W skrócie, w grach jest to praktycznie niezbędne.

Na szczęście wykrywanie kolizji nie jest takie trudne jakie mogłoby się wydawać. Programista gier, powinien wiedzieć jakie są sposoby na wykrywaniu kolizji i kiedy z których korzystać.

Dziś opiszę najbardziej podstawowy sposób na wykrywanie kolizji. Jest to sprawdzanie granic tak zwanego prostokąta ograniczającego. Prostokąt ograniczający to taki umowny twór, który otacza obiekt w grze. Jego granice wyliczamy na własną rękę dla każdego obiekty. Jeżeli granice dwóch taki prostokątów przetną się, oznacza to, że między obiektami, które otaczają, nastąpiła kolizja.

Wszystko to brzmi bardzo ciekawie, ale póki co to czysta teoria. Spokojnie, jak zwykle, nadchodzę z przykładami. Tym razem pomoże mi mój dzielny pies Maja:

maja

Kliknij na zdjęcie aby uruchomić prosty program, w którym między dwoma obiektami może zajść kolizja. Poprowadź Maję do kota używając strzałek na klawiaturze. Jeśli go złapiesz, nastąpi kolizja i w konsoli wyświetli się odpowiedni komunikat.

Tworzenie gier w JavaScript: kolizje – kod

Prostokąt ograniczający bardzo łatwo zdefiniować używając informacji, które mamy już w naszych obiektach. pole x to lokacja jego lewej ściany a y górnej. Tak samo x + szerokość to lokacja prawej ściany prostokąta a y plus wysokość to lokacja jego dolnej ściany. Oczywistości.

Kod można obejrzeć przeglądając źródło strony linkowanej zdjęciem. Cały JavaScript jest zawarty w tagu script w pliku html. Obiekty przedstawiające Maję i kota wyglądają mniej więcej tak jak obiekty w poprzednich moich grach. Najważniejsze jest to że posiadają pola x, y, width oraz height. To wszystko czego potrzebuję do wykrywania kolizji. Kod który to robi wygląda tak:

Jest to metoda głównego obiektu gry. Jako argumenty przyjmuje ona dwa kolejne obiekty. Jeżeli obiekty stykają się metoda ta zwróci true, w innym wypadku, metoda zwróci false.

Wykorzystanie jest bardzo proste. Wrzucam tę metodę wraz z dwom interesującymi mnie obiektami w wyrażenie if. Kolizja spowoduje odpalenie kodu po warunku:

I tyle. To wystarczy do bardzo prostego wykrywania kolizji. Jednak mimo swej prostoty sprawdza się w wielu przypadkach, nawet w bardziej skomplikowanych grach.

Dla dociekliwych opiszę dokładnie funkcję checkCollisions. Szczególnie zwracany fragment może wydawać się dość skomplikowany. Logika wykluczająca i negacja wszystkiego powoduje, że jest dość ciężko sobie to zwizualizować. Jednak po rozłożeniu na czynniki pierwsze, łatwo zrozumieć co tam się dzieje.

Przede wszystkim, cały zwrot jest na negowany (symbol wykrzyknika). Czyli aby nastąpiła kolizja, to co jest w nawiasie musi równać się false. Są to cztery wyrażenia połączone OR’em (dwa pipe’y). Czyli aby całe wyrażenie było równe false, wszystko musi być równe false (wystarczy jedno true w grupie zwrotów połączanej operatorem OR i całe wyrażenie równa się true).

Przejdę kolejno przez każdy element wyrażenia, tłumacząc co oznacza, sytuacja kiedy wartość równa jest false. Oto schemat takiej sytuacji:

kolizjaPrzyklad

Na oko zaznaczyłem prostokąty graniczne. Wyraźnie widać kolizję 🙂 Kot to entity1 a Maja to entity2.

entity1.x + entity1.width < entity2.x – false w tym wyrażeniu oznacza, że prawa krawędź Kota znajduje się na prawo od lewej krawędzi Mai – widać to na obrazku 🙂 To dobry znak, ale kot może znajdować się daleko na prawo, więc nie zakładam jeszcze kolizji.

entity2.x + entity2.width < entity1.x – false tutaj oznacza, że prawa krawędź Mai znajduje na prawo od lewej krawędzi kota. I tu już mamy prawdopodobieństwo kolizji! Gdyby kot był daleko na lewo, ta wartość była by równa true. Jednak nie jest jeszcze pewne, że nastąpiła kolizja. Kot może zawsze znajdować się nad, lub pod mają. Jednak wiadomo już, że na osi X, obiekty się spotkały.

Teraz trzeba sprawdzić oś Y.

entity1.y + entity1.height < entity2.y – false w tym wyrażeniu oznacza, że dolna krawędź kota znajduje się pod górną krawędzią Mai. Zgadza się, widać to na obrazku. Nie jest to jeszcze kolizja. Kot może być dużo niżej, wtedy poprzednie warunki też mogły być równe false ale jeśli ostatni będzie równy true – nici z kolizji. Jednak jest już bardzo blisko.

entity2.y + entity2.height < entity1.y – false tutaj oznacza, że dolna krawędź Mai jest niżej niż górna krawędź kota. Czyli znów. Jeżeli dolna krawędź kota jest niżej niż górna krawędź Mai a dolna krawędź mai jest niżej niż górna krawędź kota… Muszą się stykać!

Mam nadzieję, że teraz już każdy rozumie jak działa ta funkcja. Oczywiście nie ma sensu uczyć się jej na pamięć, zresztą po paru projektach pewnie i tak utkwi w głowie. Najważniejsze jest rozumienie, co jak działa.

OK, skoro już wiadomo jak sprawdzać kolizje, czas na trochę gorzkiej prawdy. Łatwość użycia, niestety niesie za sobą konsekwencje w postaci nie do końca dokładnego wykrycia kolizji. Problem pojawia się w momencie kiedy obiekty mają nieregularne kształty, lub przypominają bardziej koła niż prostokąty.

Co to znaczy? Oto przykład:

gwiazdki

Czy te dwa obiekty się stykają? no nie bardzo… A teraz zaznaczę ich prostokąty graniczne:

gwiazdkiZaznaczenie

Już wiadomo o co chodzi. Na szczęście większość obiektów, które pojawiają się w grach dwuwymiarowych (bo o takich cały czas tu mowa 😉 ) Można opisać z łatwością kwadratem lub kołem. A nawet jeśli nie jest to do końca dokładne, to w dynamicznej grze i tak ciężko ten brak dokładności zauważyć.

Ok, możesz pomyśleć, ale co z okręgami? Też będą problematyczne, wpisanie ich w kwadrat, pozostawi dużo wolnego miejsca w rogach. Te puste miejsca mogą generować fałszywe kolizje. Racja! Dlatego dla okrągłych obiektów opracowano inny wykrywacz kolizji. Ale o nim napiszę w kolejnym poście 🙂

Jeżeli chcesz być pewny, że jesteś na bieżąco z postami z tej serii, dobrym pomysłem będzie polubienie mojej strony na facebooku. Dzięki temu zawsze będziesz na bieżąco z tym co tutaj zamieszczam.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *