HTML 5 Canvas: Snake – wersja kompletna

Jakiś czas temu zaprezentowałem szkic nowego projektu. Moim celem było odtworzenie legendarnej gry z telefonów komórkowych – Snake. Dziś przedstawiam kompletny projekt, wąż napisany w JavaScripcie, przy użyciu elementu canvas.

Gra dostępna pod tym linkiem. A poniżej, jak zwykle, opis kodu.

snake

Ta wersja gry Snake zawiera ekran startowy oraz możliwość pauzy w czasie rozgrywki. Również po zakończeniu rozgrywki, nie trzeba odświeżać przeglądarki, wystarczy nacisnąć enter aby rozpocząć ponownie. Wszystko opisane jest w instrukcjach, które wyświetlają się na planszy gry.

Poprawiłem błąd, przez który snake mógł przechodzić przez samego siebie. Teraz jeśli wpadnie na swój ogon, niestety czeka go smutny los. Dodałem również cel dla węża – jabłka. Zebranie jabłka daje graczowi punkty, ale też powoduje, że wąż rośnie, przez co staje się mniej poręczny.

Zmieniłem pierwotną koncepcję, zresztą spodziewałem się że będzie taka potrzeba. Zamiast jednego obiektu przechowującego wszystkie dane i logikę, stworzyłem trzy. Dwa z tych obiektów, Snake oraz Apple, obsługują zachowanie wirtualnego węża oraz jego celu – jabłka. Trzeci obiekt Game kontroluje całą logikę gry, wprawia dwa pozostałe w ruch 🙂

Rozmiar kodu moich projektów robi się coraz pokaźniejszy 🙂 Dlatego tym razem opiszę każdy obiekt osobno, zaznaczając miejsca, w których wchodzą ze sobą w interakcję. Dla czytelności, pola i metody obiektów, opisywać będę od myślników.

Obiekt Apple

Obiekt Apple, służy do tworzenia na planszy jabłka. Celem gry jest ‚zebranie’ przez gracza jak największej ilości jabłek.

Konstruktor przyjmuje dwa argumenty, wskaźniki do elementu canvas i jego kontekstu dwuwymiarowego.

Pola klasy:

  • canvas – przyjmuje wartość parametru, odnosi się do elementu canvas w dokumencie html.
  • ctx – przyjmuje wartość parametru, odnosi się do kontekstu dwuwymiarowego elementu canvas.
  • size – długość boku kwadratu reprezentującego jabłko na planszy.
  • onBoard – wartość boolowska. Potrzebna w obiekcie Game. Określa czy jabłko znajduje się już na planszy. Jeżeli onBoard jest ustawiona na false, gra utworzy nowe jabłko, w nowej lokalizacji.
  • x – poziome położenie jabłka, w konstruktorze ustawione na zero.
  • y – pionowe położenie jabłka, w konstruktorze ustawione na zero.
  • scoreValue – ilość punktów, która gracz otrzyma za ‚zjedzenie’ jabłka.

Metody klasy:

  • randomize – ta metoda ustawia pola x oraz y jabłka. Wartości generowane są losowo. Mogą zawierać liczby całkowite w przedziale od zera do wielkości elementu canvas podzielonej przez size jabłka
  • drawApple – ta metoda rysuje jabłko na planszy gry. Najpierw ustawiam kolor na zielony a następnie używając canvasowej metody rect umieszcza jabłko zgodnie z wartościami x, y oraz size. Ostatecznie kolor rysowania jest zmieniony z powrotem na czarny

Obiekt Snake

Obiekt Snake, jest bardzo podobny do tego, który zaprezentowałem w poprzedniej wersji gry. Pojawiło się parę dodatkowych pól a niektóre metody zostały przeniesione do obiektu Game , lecz po za tym to ten sam Snake 🙂

Konstruktor przyjmuje dwa argumenty, wskaźniki do elementu canvas i jego dwuwymiarowego kontekstu.

Pola klasy:

  • canvas – to samo co w obiekcie Apple.
  • ctx – to samo co w obiekcie Apple.
  • speed – prędkość węża. Ta wartość będzie wykorzystana w funkcji setInterval.
  • size – rozmiar boku jednego segmentu, z którego będzie zbudowany wąż.
  • bodyParts – liczba segmentów, z których w danej chwili składa się wąż.
  • bodyPartsHolder – tablica, przechowująca wszystkie segmenty węża w postaci mini obiektów. Obiekty te posiadają dwa pola: x oraz y(współrzędne danego segmentu na planszy).
  • direction – łańcuch znaków, według którego ustawiany jest kierunek poruszania się węża. Domyślnie ustawiony na „right”. Zmieniany w trakcie działania programu za pomocą strzałek.
  • paused – nowe pole. Wartość boolowska. True oznacza, że gra jest zaupauzowana. Wykorzystywane przez obiektGame.
  • alive – nowe pole. Wartość boolowska, true oznacza, że wąż żyje i gra toczy się dalej. Jeśli to pole zmieni się na false, pętla gry zatrzymuje się. Wykorzystywane przez obiektGame.
  • eaten – nowe pole. Wartość boolowska. Ustawiana na true, kiedy wąż zje jabłko. Wykorzystywane przez obiektGame.

Metody klasy:

  • initSnake – Ta metoda inicjalizuje węża. Wypełnia tablice bodyPartsHolder obiektami o odpowiednich wartościach pół x oraz y. Również wywołuje metodę makeSnakeListen.
  • updatePosition – metoda, która uaktualnia położenie wszystkich segmentów węża. Najpierw podnosi, lub obniża wartość x lub y(w zależności od aktualnej wartości pola direction) ostatniego elementu tablicy (‚głowy’ węża) o 1. Następnie uaktualnia położenia wszystkich następnych elementów o te, które ma element znajdujący się wcześniej.
  • makeSnakeListen – ta metoda przypisuje do obiektu window eventListener onkeydown. Naciśnięcie którejś ze strzałek, zmienia wartość pola direction. Nowością tutaj jest to, że naciśnięcie spacji zmieni wartość pola paused na true, oraz wypisze na planszy czerwonymi literami słowo ‚PAUZA’.
  • moveSnake – ta metoda wywołuje metody drawSnake oraz updatePosition.
  • drawSnake – metoda iteruje przez tablicę bodyPartsHolder i wyrysowywuje na planszy wszystkie segmenty węża, bazując na wartościach x oraz y każdego z elementów.
  • checkColissions – ta metoda wywoływana jest w obiekcie game i przyjmuje dwie wartości: aktualne x oraz y jabłka. Dwa pierwsze warunki były tu już wcześniej. Jeśli wąż wpadnie na ściane, pole alive otrzyma wartość false. To samo jeśli spełni się ostatni warunek, który sprawdza czy aby głowa węża nie ma takich samychx i y jak, którykolwiek inny segment. Trzeci z kolei warunek sprawdza czy przypadkiem głowa węża nie ma takich samych x i y jak jabłko (którego współrzędne, przekazane były jako argumenty). Jeśli tak, pole eaten zmienia się na true.
  • grow – Ostatnia metoda klasy Snake. Jest wywoływana przez obiekt Game gdy wąż zje jabłko. podnosi ona wartość pola bodyParts o 1. Dodaje również nowy element na początek tablicy bodyPartsHolder
  • . x oraz y nowego elementu ustawiane są na -1, aby nie pojawił się na planszy zanim metoda updatePosition, nie umieści go na końcu węża.

Obiekt Game

I na koniec obiekt Game, czyli serce tego programu. To tutaj ukrył się setInterval, który wprawia wszystko w ruch.

Już tradycyjnie konstruktor przyjmuje dwa argumenty, wskaźniki do elementu canvas i jego kontekstu dwuwymiarowego.

Pola klasy:

  • canvas – to samo co w poprzednich obiektach.
  • ctx – to samo co w poprzednich obiektach.
  • width – szerokość wybranego elementu canvas.
  • height – wysokość wybranego elementu canvas.
  • state – łańcuch znaków. Określa aktualny stan gry, wykorzystywany przez metodę init. Początkowa wartość to New.
  • snake – instancja obiektu Snake. Konstruktor wywołany z argumentami canvas oraz ctx obiektu Game.
  • apple – instancja obiektu Apple. Konstruktor wywołany z argumentami canvas oraz ctx obiektu Game.
  • appleCorrect – zmienna boolowska. Określa, czy jabłko zostało umieszczone na planszy poprawnie (nie na polu, które aktualnie zajmuje wąż).
  • score – pole, które przechowuje ilość punktów jaką udało się zdobyć graczowi, oczywiście początkowo ustawione na zero.

Metody klasy:

  • drawWelcome metoda wywoływana przez metodę init, gdy pole state jest równe New. Metoda ta wypisuje na planszy instrukcje, oraz dodaje do obiektu window eventlistner, który powoduje, że po naciśnięciu enter pole state zmienia się na „playGame” i ponownie odpalana jest metoda init.
  • activateGame – ta metoda najpierw inicjalizuje węża wywołując metodę obiektu SnakeinitSnake a następnie ustawia setInterval. W samym setInterval trochę się dzieje, ale są to głównie proste sprawdzania warunków. Najpierw metoda sprawdza czy wąż zjadł jabłko (poprzez pole obiektu Snakeeaten).Jeśli tak graczowi, doliczane są punkty, wąż rośnie a jabłko zostaje zdjęte z planszy. Kolejną rzeczą, którą robi metoda jest wyczyszczenie elementu canvas, pod warunkiem, że gra nie jest aktualnie zapauzowana (nie chcę aby z planszy zniknął napis PAUZA). Następnie, jeśli na planszy jest jabłko, wywoływana jest metoda obiektu AppledrawApple. Jeżeli jabłka nie ma, losowana jest nowa pozycja (tak długo, aż nie będzie pokrywać się z pozycją węża), po czym jabłko zostaje umieszczone na planszy i narysowane. Ostatnią rzeczą, która jest sprawdzana w setInterval to to czy pole obiektu Snakealive jest równe true. Jeśli tak i gra nie jest zapauzowana, waż jest rysowany i uaktualniany jak zwykle. Jeśli pole alive jest równe false, program zmienia wartość pola state na „gameLost”, umieszcza na ekranie smutną wiadomość i ponownie odpala metodę init.
  • init – ta metoda obsługuje odpowiednie stany gry i w zależności od wartości pola statewywołuje odpowiednie metody. Jeżeli stan jest równy „gameLost”, ta funkcja resetuje wszystkie ustawienia aby przygotować grę do nowej partii.
  • Podsumowanie

    Ze względu na wielkość kodu, nie wnikałem we wszystkie szczegóły i mechanizmy. Mam jednak nadzieję, że w miarę jasno udało mi się przekazać główne idee stojące za tym programem. W razie czego wszystko można dopatrzyć sobie w kodzie.

    Teraz wystarczy dodać ładną grafikę i obsługę eventów dotykowych, stworzyć bajerancki layout, wszystko zapakować w odpowiednią paczkę i mamy appkę na androida 😉

    2 thoughts on “HTML 5 Canvas: Snake – wersja kompletna”

      1. Hej, faktycznie masz rację. Najwyraźniej, kiedy zawrócisz węża ale ten nie zdąży jeszcze się poruszyć, przez krótki okres czasu można wykonać nielegalny ruch w przeciwną stronę. Oczywiście skutkuje to śmiercią węża 🙁 No cóż, podejrzewam, że nie jest to jedyny babol. Prawda jest taka, że jak pisałem tę grę byłem jeszcze większym noobem niż obecnie, musisz mi wybaczyć 🙂 Celem projektu było raczej doskonalenie umiejętności niż stworzenie gry idealnej, a myślę, że to mi się udało. Swoją drogą gratuluję zacięcia testerskiego! Pozdrawiam.

    Dodaj komentarz

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