GCM – Sierpień: Space Shooter. kolejna gra mobilna.

Spodobało mi się granie we własnoręcznie napisaną grę na telefonie. Dlatego postanowiłem, że sierpniowa gra, też będzie mobilna. Od jakiegoś czasu nosiłem się z zamiarem napisania czegoś w rodzaju klonu popularnego Space Impact, gry znanej każdemu kto miał telefon-cegłę Nokia 3310.

Tak właśnie powstał prototyp gry na ten miesiąc. Już na sammym początku dodałem do niej funkcjonalność pozwalającą na grę na telefonie.

gra mobilna JavaScript

Aktualną wersję gry można przetestować klikając w obrazek powyżej. Na moim githubie znajduje się repozytorium zawierające jak najaktualniejszy stan gry. Wersja przedstawiona w poście dostępna jest po zajrzeniu w źródło strony gry .

Póki co w grze nie dzieje się zbyt wiele, nawet grafiki są póki co tylko prowizoryczne 😉 Nie, docelowo nie ma się latać prostokątem 🙂 . Na komputerze statkiem poruszamy się używając strzałek w górę i w dół. Aby strzelić należy nacisnąć spację. Na telefonie, do sterowania używamy przycisków widocznych po lewej i prawej stronie obszaru gry.

Przejdę od razu do kodu. Plikiem, którego zawartość uruchamia grę jest game.js. Oto jego treść:

Tworzę w nim nową instancję gry Phaserowej, dodaję do niej stany, oraz uruchamiam pierwszy z nich. Stany, jak i wszystkie inne elementy gry, znajdują się w module SpaceShooter. Ponieważ game.js jest dodany do dokumentu html na końcu, treść wszystkich plików jest dla niego dostępna.

Pierwszym uruchamianym stanem jest, klasycznie, init, którego zawartość znajduje się w pliku init.js:

Nie tworzę tu nic czego nie używałbym już, w którejś z poprzednich gier. Ponieważ jest to pierwszy plik dołączany do dokumentu, muszę w nim stworzyć pusty moduł SpaceShooter. następnie przypisuję do niego definicje stanu. Sam stan ma dwie metody, init, w której definiuję potrzebne do gry grafiki oraz create, w którym konfiguruję wszystkie opcje gry. Myślę, że ten kod powinien być w miarę jasny. Jeżeli coś nie jest, dajcie znać. Postaram się wyjaśnić co trzeba.

Gdy stan init zrobi swoją robotę, uruchamiany jest stan game, którego zawartość znajduje się w pliku main.js:

W metodzie create, dodaję do gry cztery obiekty: tło, obiekt gracza, obiekt małych asteroidów, oraz obiekt dużych asteroidów. Jeżeli gra nie jest uruchomiona na komputerze wywołuję również metodę addMobileInputs, odpowiedzialną za dodanie do gry przycisków.

Gdy wszystko jest już gotowe, program przechodzi do metody update, czyli game loopa tego stanu. Wewnątrz, najpierw przypisuję aktualny czas do globalnej zmiennej currTime a następnie sprawdzam, czy pomiędzy pociskami gracza a którymś z asteroidów nie zaszła kolizja. Proszę zwrócić uwagę, że używam metod fizyki frameworka. Mam tu do czynienia tylko i wyłącznie z phaseorwymi grupami. Nie tworzę już własnych kolekcji tak jak w Robocie. Po pierwsze kod jest dzięki temu czystszy, po drugie, liczę, że w ten sposób uniknę problemów z wydajnością.

Zanim przejdę do opisu obiektów, muszę pokazać treść pliku helpers.js. Zawiera on te funkcje, które nie pasowały nigdzie indziej 🙂 . Wiem, średnio fajna praktyka, ale cóż… To będzie mała gra 😉

helpers.js zawiera w sobie 3 funkcje, wszystkie wykorzystywane są w głównym stanie gry. Pierwsza z nich to addMobileInputs. Służy ona do dodania do gry przycisków, dzięki którym gracz może sterować statkiem również na telefonie. Kod wygląda niemalże tak samo jak ten w poprzedniej grze mobilnej. Gdy któryś z przycisków odpowiedzialnych za poruszanie się zostanie naduszony, wartość odpowiedniego pola (moveUp lub moveDown) w obiekcie gracza (do którego mamy dostęp dzięki parametrowi w funkcji) zmienia się na true. Naciśnięcie przycisku odpowiedzialnego za strzelanie wywołuje metodę obiektu gracza shoot.

Dwie pozostałe metody, wywoływane są gdy w głównym stanie gry zostanie wykryta kolizja pomiędzy pociskami gracza a dużymi i małymi asteroidami. Działają one niemal identycznie. W wyniku kolizji, oba obiekty usuwane są z gry. Jeżeli zestrzelony zostanie duży asteroid, dodatkowo wywoływana jest metoda addSmall pola smallOnes asteroida.

Ok, teraz mogę przejść do obiektów gry. Pierwszy a zarazem najmniej skomplikowany z nich to tło, logika tego obiektu znajduje się wewnątrz pliku background.js:

Wykorzystuję tu ten sam mechanizm co w poprzednich grach phaserowych. Do modułu gry przypisuję funkcję, która zwraca gotowy obiekt. Dzięki temu te same obiekty mogę wykorzystywać wiele razy w różnych stanach.

W tym wypadku do gry dodaję obiekt typu tileSprite. Jest on o tyle wyjątkowy, że układa grafikę w tle gry wiele razy. Oznacza to, że obok jednego tileSprite, znajduje się jeszcze jeden, taki sam. Mi pasuje to idealnie, ponieważ, mogę dzięki temu przesuwać je w lewo co daje wrażenie, że tło porusza się w nieskończoność. W obecnej wersji gry może nie być tego widać tak dobrze, ponieważ tło to póki co wielka czarna przestrzeń i mały pasek, reprezentujący powierzchnie obcej planety, na dole. W przyszłości grafika będzie bogatsza a „przesuwanie” tła będzie lepiej widoczne i da ciekawszy efekt.

Dokładnie takim przesuwaniem zajmuje prosta logika metody bg aktualizującej obiekt tła.

Kolejny obiekt dodany do gry to obiekt gracza. Oto treść pliku player.js, który zawiera odpowiedzialny kod.

Kod tego pliku wydaje się być największy, jednak powinien on również wyglądać najbardziej znajomo. W funkcji createPlayer tworzę obiekt gracza, który na końcu zostaje zwrócony. Posiada on pole bullets, które jest phasrową grupą przechowującą pociski. Są też pola moveUp oraz moveDown, których wartości boolowskie mogą być zmienione przez dotykanie przycisków odpowiedzialnych za ruch. Jest też pole lastShot, w którym przetrzymywać będę czas ostatnigo strzału.

Metoda upate obiektu gracza również powinna wyglądać znajomo. Jeżeli wciśnięte są odpowiednie klawisze na klawiaturze, lub dotknięte odpowiednie przyciski, statek zacznie poruszać się lub wystrzeli pocisk.

Ostatnia metoda obiektu zwracanego przez createPlayer to shoot. Jak nazwa wskazuje, obsługuje ona logikę strzelania. Działanie tutaj jest bardzo proste. Program pobiera pierwszy niekatywny obiekt z grupy bullets, resetuje go, ustawia mniej więcej w miejscu z którego wystrzeliwane są pociski i nadaje mu prędkość w poziomie. Każdy pocisk jest deaktywowany gdy wyleci poza obszar gry, lub gdy zetknie się z asteroidem.

Ostatnie dwa elementy gry to asteroidy. Są one do siebie bardzo podobne. Oto kod odpowiadający za logikę małego asteroida. Znajduje się on w pliku smallAsteroids.js

Zwracanym obiektem jest tym razem grupa. Posiada ona dwie metody. Pierwsza odpowiada za dodanie nowych asteroidów do gry. Miejsce ich pojawienia się, zależy od współrzędnych przekazanych w argumencie.

Podobnie jak w przypadku pocisków, metoda pobiera te asteroidy z grupy które są nieaktywne. Różnica jest taka, że tworzonych asteroidów jest kilka. Osiągam to za pomocą pętli, której liczba obiegów jest losowa. Idea jest taka, że po zestrzeleniu dużego asteroida, ten rozdziela się na kilka małych. Każdy nowy asteroid otrzymują losową prędkość w pionie i w poziomie. Jeżeli wyleci on poza obszar gry, jest deaktywowany.

Metoda update, powoduje, że asteroid obraca się wkoło i jeżeli zbliży się do dolnej krawędzi obszaru gry, ‚odbije się’.

Kod obsługujący duże asteroidy znajduje się w pliku bigAsteroids.js. Tak wygląda jego treść:

Tutaj sytuacja wygląda prawie identycznie jak w przypadku małych asteroidów. Ponieważ duże poruszają się tylko w poziomie, nie muszę sprawdzać czy uderzą o ziemie. Do tego otrzymują one również referencje do małych asteroidów. Dzięki temu po kolizji z pociskiem, mogę wywołać metodę addSmall małego asteroida.

I to tyle na dziś. Może opisałem kod dość ogólnikowo, ale wydaje mi się, że większość powinna być znana. Jeżeli jednak przypadkiem ominąłem jakąś ważną nowość, dajcie znać. Postaram się wyklarować wszystkie niejasności 🙂 .

Jeżeli chcesz być na bieżąco z postami na blogu zachęcam do polubienia mojej strony na facebooku. Zawsze zamieszczam tam informacje o wszystkich nowościach. Jest to też dobre miejsce na kontakt ze mną. Na wszystkie pytania zawsze odpowiem :). Do przeczytania.

Dodaj komentarz

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