W ostatniej aktualizacji, do gry dodałem nowych przeciwników. Tym razem wesprę pilota sterowanego przez gracza statku, dając mu możliwość ulepszenia swojej broni.
Gdy zbierze on odpowiedni obiekt w grze, zmieni się aktywny atak. Nowa broń może być dużo potężniejsza niż ta, którą dotychczas dysponował gracz. Może jednak być słabsza. Do nowych ataków zaliczają się między innymi podwójne strzały, potrójne strzały, szybkostrzelne działo pulsacyjne, a nawet laser.
Aktualną wersję gry przetestować można 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.
Aby dać graczowi możliwość używania więcej niż jednej broni, musiałem zmodyfikować to w jaki sposób
tworzę element player. Teraz same bronie to osobny element gry, który po ‚zainicjalizowaniu’ dodaję do obiektu gracza. W głównym stanie (plik main.js), w metodzie create, dodaję dwie nowe linijki kodu, pierwsza przed a druga po dodaniu obiektu gracza:
1 2 3 |
this.weapons = SpaceShooter.createWeapons(game); this.player = SpaceShooter.createPlayer(game); this.player.weapons = this.weapons; |
Do stanu dodaję teraz obiekt weapons, używając metody createWeapons (którą opiszę za chwilę). Następnie po dodaniu obiektu gracza, do jego pola weapons, przypisuję obiekt broni.
Wewnątrz obiektu gracza, wykorzystuję weapons w następujący sposób:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
SpaceShooter.createPlayer = function(game){ /* Stary kod */ player.weapons; player.currWeapon = 'single'; /* Stary kod */ player.update = function() { /* Stary kod */ if (game.spaceKey.isDown || this.shooting) { this.shoot(); } /* Stary kod */ } player.shoot = function(){ this.weapons[this.currWeapon].shoot(this.x,this.y); } return player; } |
Dwa nowe pola w obiekcie gracza to: weapons oraz currWeapon. Pierwsze zawierać będzie referencje do obiektu broni, którą dodaję podczas tworzenia głównego stanu gry. Drugie pole przechowuje informację o tym, jaka broń jest aktualnie używana. Informacja ta przechowywana jest w formie łańcucha znaków. Jest to zarazem nazwa pola, w obiekcie weapons, które zawiera informacje na temat danego rodzaju broni (o tym za chwilę).
Kolejna zmiana dotyczy samego strzelania. Teraz kiedy gracz przyciśnie spację, lub gdy dotknięty zostanie przycisk na ekranie dotykowym. Wywoływana jest metoda shoot. shoot z kolei wywołuję metodę o tej samej nazwie, znajdującej się w obiekcie weapons, w polu, którego nazwa równa jest aktualnej wartości pola currWeapon.
A teraz coś na temat obiektu weapons. Jest on tworzony wewnątrz funkcji createWeapons. Jest ona przypisana bezpośrednio do głównego modułu gry i znajduje się w pliku weapons.js. Plik ten jest dość spory, więc nie będę wrzucał tutaj całej jego zawartości. Zamiast tego, podaję link do wersji, która nie będzie się już zmieniać.
Jako przykład przedstawię tylko podstawową broń czyli single (od pojedynczego pocisku)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
var weapons = {}; weapons.single = game.add.group(); weapons.single.enableBody = true; weapons.single.createMultiple(10, 'bullet'); weapons.single.nextFire = 0; weapons.single.bulletSpeed = 500; weapons.single.fireRate = 350; weapons.single.shoot = function(srcx,srcy){ var bullet = this.getFirstDead(); if (!bullet) { return; } if(game.currTime > this.nextFire){ bullet.anchor.setTo(0.5); bullet.reset(srcx+100, srcy+25); bullet.body.velocity.x = this.bulletSpeed; bullet.checkWorldBounds = true; bullet.outOfBoundsKill = true; this.nextFire = game.currTime + this.fireRate; } } |
W pierwszej linijce createWeapons, tworzę pusty obiekt: weapons. Do niego przypisywał będę wszystkie rodzaje broni. Jest on zwracany na końcu metody.
Każdy rodzaj broni to phaserowa grupa z włączoną fizyką. Ilość elementów grupy zależy od tego z jakiego rodzaju bronią mam do czynienia. Do tego obiekty broni zawierają takie informacje jak: kiedy czas na kolejny strzał (nextFire), Jak szybko poruszają się kule (bulletSpeed) oraz jak często są one wystrzeliwane (fireRate).
Każda broń posiada metodę shoot. To właśnie ta ona wywoływana jest przez shoot wewnątrz obiektu gracza. Dla każdego rodzaju ataku shoot działa trochę inaczej, lecz jest tu pewien schemat. Najpierw pobierany jest pierwszy nieaktywny element z grupy (w przypadku niektórych broni może to być więcej elementów), następnie ustawiany jest on w odpowiednim miejscu na mapie (zależnym od pozycji statku gracza) aby na koniec otrzymać odpowiednią prędkość poruszania się w poziomie (czasem też w pionie). Oczywiście wszystkie pociski są deaktywowane po opuszczeniu obszaru gry.
Nie będę opisywał metody shoot każdej z broni. Mechanizmy, które nimi sterują nie są specjalnie skomplikowane, zachęcam do zapoznania się z nimi na własną rękę. W razie jakichkolwiek pytań, zapraszam do pozostawienia komentarza pod postem. Chętnie rozwieję wszelkie wątpliwości.
Teraz kiedy bronie są gotowe, potrzebny jest sposób aby móc z nich korzystać. Domyślnie gracz posiada zwykły atak (pojedynczy pocisk), aby zmienić broń, musi zebrać odpowiedni bonus. Bonus taki pojawia się gdy, któryś z wrogów zostanie zestrzelony. Jednak nie za każdym razem. Jest na to pewna szansa.
Najpierw, muszę stworzyć w grze element, który reprezentować będzie taki bonus. W tym celu dodaję do głównego modułu metodę createPowerUp. Znajduje się ona w pliku powerUp.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
SpaceShooter.createPowerUps = function(game){ powerUps = game.add.group(); powerUps.enableBody = true; powerUps.createMultiple(5, 'powerUp'); powerUps.add = function(srcX,srcY) { var powerUpChance = game.rnd.integerInRange(1, 25); if(powerUpChance != 1){ return; } var powerUp = this.getFirstDead(); if (!powerUp) { return; } var verticalspeed = game.rnd.integerInRange(50, 0); var hotizonstalspeed = game.rnd.integerInRange(-50, -100); powerUp.anchor.setTo(0.5); powerUp.reset(srcX, srcY); powerUp.body.velocity.x = hotizonstalspeed; powerUp.body.velocity.y = verticalspeed; powerUp.checkWorldBounds = true; powerUp.outOfBoundsKill = true; } powerUps.update = function(){ this.forEachExists(function(item){ if(item.y >= game.world.height - item.height - 50) { item.y = game.world.height - item.height - 55; item.body.velocity.y *= -1; } },this) } return powerUps; } |
Jak widać, jest to bardzo podstawowa konstrukcja, która powinna wyglądać już naprawdę znajomo. Moimi bonusami są zwykłe spritey, należące do grupy, zwracanej w wyniku użycia metody createPowerUp.
Są one tworzone i aktualizowane podobnie jak małe asteroidy. Jeśli opuszczą obszar gry, są deaktywowane, jednak jeżeli zbliżą się do dolnej krawędzi ekranu, ‚obiją się’. Jedyna różnica jest taka, że nowy bonus zostanie faktycznie dodany do gry, dopiero jeśli wynikiem losowej liczby z przedziały od 1 do 25, będzie 1. Czyli szansa na pojawienie się nowej broni, po pokonaniu przeciwnika to jeden do dwudziestu pięciu.
Teraz wystarczy tylko dodać odpowiednią metodę, która wywoływana będzie w wyniku kolizji gracza z aktywnym power’upem. Metoda taka nazywa się collectPowerUp i znajduje się w pliku helpers.js:
1 2 3 4 5 6 |
SpaceShooter.collectPowerUp = function(player,powerUp){ var powerUps = ['single','double','triple','scatter','beam','doubleGrow'] var collected = game.rnd.integerInRange(0, 5); player.currWeapon = powerUps[collected]; powerUp.kill(); } |
Wewnątrz metody znajduje się tablica zawierająca wszystkie rodzaje broni. Losowo wybieram indeks jednej z nich, a następnie do pola currWeapon obiektu gracza, przypisuję to co znajduje się pod tym indeksem. Na koniec power’up jest deaktywowany.
I to wszystko. W ten dość prosty sposób dodałem do gry różne rodzaje ataków, co znacznie zwiększa grywalność 🙂 . Kolejny krok to kolizje pomiędzy obiektem gracza a resztą świata.
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.