W dzisiejszym wpisie zajmiemy się stworzeniem przeciwnika dla naszego bohatera. Będzie to postać reprezentowana w tej chwili przez niebieski kwadracik, który będzie poruszał się według prostego schematu: losowane są 2 liczby, jedna z nich to kierunek ruchu, a kolejna to ilość kroków w wylosowanym wcześniej kierunku. Nie jest to jakieś super inteligentne rozwiązanie, ale jak na nasze początkujące potrzeby wystarczy nam, aby móc sprawnie wyświetlić wroga na ekranie i później zająć się kolizją, ale wszystko w swoim czasie. Dziś wróg będzie tylko się poruszał po ekranie. Zatem przejdźmy do kodu.
Klasę związaną z wrogami nazwałem Monster.java
public class Monster extends Rectangle{ private int hp; private double speed; private Texture texture; private Pixmap pixmap; private int moveDirection; //Kierunek private int moveQuantity; //ilosc private Random r; public Monster(int x, int y){ super(x, y,20,20); r = new Random(); hp = 5; speed = 5; moveDirection = r.nextInt(4); moveQuantity = r.nextInt(5)+1; pixmap = new Pixmap(30, 30, Pixmap.Format.RGBA8888); pixmap.setColor(Color.BLUE); pixmap.fillRectangle(0,0,20,20); texture = new Texture(pixmap); pixmap.dispose(); } public Texture getTexture() { return texture; } public int getHp() { return hp; } public double getSpeed() { return speed; } public int getMoveDirection() { return moveDirection; } public int getMoveQuantity() { return moveQuantity; } public Pixmap getPixmap() { return pixmap; } public void setTexture(Texture texture) { this.texture = texture; } public void setHp(int hp) { this.hp = hp; } public void setMoveDirection(int moveDirection) { this.moveDirection = moveDirection; } public void setMoveQuantity(int moveQuantity) { this.moveQuantity = moveQuantity; } public void setPixmap(Pixmap pixmap) { this.pixmap = pixmap; } public void setSpeed(double speed) { this.speed = speed; } // ruch monstera public void moveToLeft(){ if(moveDirection == 0){ x -= speed; } } public void moveToRight(){ if(moveDirection == 2){ x += speed; } } public void moveToTop(){ if(moveDirection == 1){ y += speed; } } public void moveToBottom(){ if(moveDirection == 3){ y -= speed; } } // generowanie nowych ruchów public void generateMove(){ if(moveQuantity == 0){ moveDirection = r.nextInt(4); moveQuantity = r.nextInt(5)+1; } } }Nasza nowa klasa dziedziczy po klasie Rectangle, jak już wcześniej wspomniałem będzie reprezentowana przez niebieski kwadracik. Dalej znów mamy kilka pól odpowiedzialnych za statystyki typu życie hp, prędkość poruszania speed. Podobnie jak miała miejsce sytuacja przy tworzeniu bohatera również tu mamy dwie zmienne, które odpowiadają za rysowanie na ekranie: texture i pixmap. Kolejne dwie zmienne są związane z ruchem przeciwnika moveDirection oraz moveQuantity. Zmienna r jest zmienną klasy Random, która będzie odpowiadała, za losowanie kierunku oraz ilości kroków wykonanych przez wroga.
Teraz zajmijmy się naszym konstruktorem, względem konstruktora klasy Player zachodzi tu kilka zmian. Metoda super(), odpowiada za wywołanie konstruktora klasy dziedziczącej, która przyjmuje dwa parametry jako współrzędne początkowe oraz dwa jako szerokość oraz wysokość. Następna linijka odpowiada za inicjalizację zmiennej losującej. Kolejne dwie linijki odpowiadają za przypisanie wartości liczbowej atrybutowi hp oraz speed. Teraz w konstruktorze zajmujemy się losowaniem, pierw kierunku ruchu, a następnie ilością kroków. Następne linijki są analogiczne do klasy Player z małą różnicą otóż w naszym obecnym kodzie tworzymy kwadrat zamiast koła oraz zmieniamy kolor z żółtego na niebieski.
Kolejne linijki kodu to standardowe dla javy gettery oraz settery, tego nie będziemy opisywać. Ciekawiej zaczyna się robić w metodzie moveToLeft(), która jest odpowiedzialna za wykonywanie ruchu w lewo. jeżeli moveDirection jest równe 0, wtedy wykonaj ruch w lewo z prędkością określoną dla danego wroga. Kolejną metodą jest moveToRight(), wykonująca ruch na takiej samej zasadzie w prawo, moveToTop(), porusza przeciwnika do góry oraz ostatnia metoda moveToBottom(), wykonuje ruch w dół. I tak oto w ten sposób mamy metody odpowiedzialne za poruszanie przeciwnikiem po planszy. Teraz wypadałoby jeszcze zająć się losowaniem kolejnych ruchów, aby nasz wróg ciągle nie wykonywał tego samego ruchu.
Ostatnią metodą w naszej dzisiejszej klasie jest generateMove(), która generuje kolejne ruchy przeciwnika. Ruch generowany jest w chwili, kiedy pole moveQuantity jest równe 0, czyli innymi słowy, kiedy zostaną wykonane wszystkie wcześniej wylosowane ruchy. Wtedy następuje losowanie nowego kierunku oraz ilości kroków.
Teraz abyśmy mogli zobaczyć efekty naszej pracy musimy przejść do klasy UWar i tam dodać nowopowstałą klasę.
private Monster monster;Następnie w metodzie start() zainicjalizować dodaną wcześniej przez nas zmienną.
monster = new Monster(r.nextInt(500),r.nextInt(500));Współrzędne początkowe są losowane od 0 do 500, w późniejszej wersji gry zostanie to odpowiednio zmodyfikowane. Nowo utworzoną i zainicjalizowaną zmienną należy wyświetlić na ekranie a jak dobrze pamiętamy odpowiada za to metoda render() i tam wpisujemy poniższą linijkę
batch.draw(monster.getTexture(), monster.x + monster.width/2, monster.y + monster.height/2);Dlaczego do współrzędnej x oraz y dodajemy odpowiednio połowę szerokości i wysokości? Otóż w sytuacji kiedy byśmy tego nie zrobili środek rysowanej przez nas grafiki byłby wyznaczony w miejscu 0,0, nas nastomiast interesuje środek tego kwadratu. Jednak gdybyśmy teraz uruchomili naszą grę ujrzelibyśmy jedynie zmianę pozycji przeciwnika przez kilka pierwszych ruchów w zależności ile kroków wylosowanych zostałoby w konstruktorze. Zatem aby zmiana pozycji była ciągła należy dodać jeszcze kilka linijek w metodzie update().
if(monster.getMoveQuantity() > 0){ monster.moveToBottom(); monster.moveToLeft(); monster.moveToRight(); monster.moveToTop(); monster.setMoveQuantity(monster.getMoveQuantity()-1); }else{ monster.generateMove(); monster.moveToBottom(); monster.moveToLeft(); monster.moveToRight(); monster.moveToTop(); monster.setMoveQuantity(monster.getMoveQuantity()-1); }Początkowo sprawdzamy czy ilość kroków jest większa od 0, jeżeli tak to wróg wykonuje jeden z czterech możliwych ruchów oraz jest zmniejszona ilość kroków o jeden. W przeciwnym wypadku potwór dostaje nowo wygenerowany kierunek oraz ilość kroków jaką będzie miał wykonać w danym kierunku, a następnie wykonuje pierwszy krok z wygenerowanych.
Na koniec jeszcze pozostaje nam zapobiec wyciekowi pamięci poprzez usunięcie tekstury wykorzystywanej przez potwora, wiec dodajemy linijkę do metody dispose().
monster.getTexture().dispose();Efekt jaki zobaczymy po uruchomieniu naszego programu
Podsumowanie
Dziś zrobiliśmy kolejny krok w rozwoju naszej gry. Co prawda nie był to jakiś bardzo wymagający kod, ale jakże istotny dla nas w perspektywie najbliższych dni. Wykorzystamy obecny stan naszej gry do utworzenia kolizji strzałów z wrogami. W kolejnych wpisach zajmę się utworzeniem strzałów bohatera, kolizją strzałów z wrogami oraz menu gry, tak aby zaraz po włączeniu nie startowała automatycznie. Zatem czekajcie na kolejny wpis, bo pojawi się już niebawem.
Pozdrawiam
sirmarbug