To co jest przewidziane na ten wpis, to zrobienie ekranów gry. Mianowicie aby można było przełączać pomiędzy różnymi ekranami, tak jak przykładowo osobno jest ekran menu, ekran samej gry, pauzy itp.
Klasa Game i interfejs Screen
Potrzebna będzie
wykorzystać klasy game
i interfejs screen.
Klasa game umożliwia aplikacji posiadanie wielu ekranów takich jak: ustawienia,
menu główne i cokolwiek sobie wymyślimy. Sama gra jest pętlą która wykonuje się
cyklicznie. Nie wykonuje ona wszystkiego wewnątrz, a deleguje zadania innym
ekranom. Interfejs jest reprezentacja jednego z okna w aplikacji. Sam interfejs
zawiera definicje metod, bez implementacji (czyli logiki) i danych. Możemy go
spotkać w kombinacji: klasa która implementuje interfejs, co można
przetłumaczyć że klasa posiada metody, które są zdefiniowane w tym interfejsie.
Tworzymy nowego brancha
W celu zachowania
wcześniejszych wersji naszego kodu, możemy tworzyć odgałęzienia (branch).
Dzięki czemu możemy na nowo utworzonym odgałęzieniu dokonywać dalszych zmian, a
jednocześnie zawsze możemy wrócić do wersji starszej. Na blogu każdy osobny
wpis, który będzie dotykał kodu, będzie właśnie nowym branchem, głównie w celu
możliwości śledzenia zmian z wpisu na wpis. W android studio jest to dosyć
łatwe. W prawym dolnym rogu mamy możemy rozwinąć menu gdzie można zarówno
tworzyć nowe, jak i przełączać pomiędzy istniejącymi.
W celu podejrzenia kodu
brancha, który nas interesuje na git hubie, w danym projekcie rozwijamy
zakładkę Branch nazwa i w tym momencie wczytuje nam się struktura danego
rozgałęzienia.
Na końcu wpisu załączone
będą obie wersje kodu (sprzed i po).
Rozszerzamy klasy
Pierwszą zmianą będzie
zamiana rozszerzenia klasy głównej gry WordCharger z ApplicationAdapter na Game.
Rozszerza znaczy że dziedziczy wszystkie własności danej klasy. Klasa Game jest
częścią Frameworka Libgdx, dlatego z góry posiada zadeklarowane wszystkie
parametry i metody, czyli jest gotowym elementem. To co my robimy to bierzemy
gotowe fragmentaryczne elementy i łączy je w nowy sposób jaki sobie obmyślimy,
czyli logika gry. W ten sposób unikamy konieczności tworzenia całości gry od
podstaw, co byłoby zbyt czasochłonne i z pewnością nie optymalne. Framework
zawiera rozwiązania, które są już sprawdzone i rozwijane przez dłuższy czas
większej grupy osób. Podsumowując w klasie WordCharger mamy wszystko to co w
klasie Game i dodatkowo możemy tutaj to wykorzystywać oraz dopisywać właściwie
co dusza zapragnie.
Z kodu który pozostał po
stworzeniu początkowego projektu usuwamy inicjalizacje obiektu Texture img,
potem z metody create usuwamy jego definicję. W metodzie create dopisujemy linię:
setScreen(New PlayScreen(this));
Co oznacza ustawienie
nowego ekranu PlayScreen, czyli tam gdzie będzie toczyła się gra. Na razie
powinno podświetlić na czerwono, ponieważ jeszcze taki ekran nie istnieje,
zajmiemy się tym niedługo. W metodzie render czyli odpowiedzialnej za rysowanie
usuwamy zawartość i dopisujemy
super.render();
a to oznacza że rysowanie
będzie następować w klasie PlayScreen. Super w tym przypadku oznacza że
będziemy delegować metodę renderowania ekranowi PlayScreen. Na obecną chwilę
kod wygląda tak
public class WordCharger extends Game {
SpriteBatch batch;
@Override
public void
create() {
batch = new
SpriteBatch();
setScreen(new
PlayScreen(this));
}
@Override
public void render() {
super.render();
}
}
Aby gra mogła istnieć miejmy
w głowie że gdzieś w jakimś punkcie początkowym musi zostać zainicjalizowany
obiekt klasy WordCharger. Wewnątrz widzimy metodę Create której zawartość jest
wykonywana przy tworzeniu właśnie tego obiektu. Najczęściej zawiera ona definicję
innych obiektów składowych, które będziemy wykorzystywali w całej grze.
Należy wspomnieć skąd
wzięły się tutaj metody create i render. Tak, właśnie z klasy Game, którą
rozszerzyliśmy klasę WordCharger. Dodatkowo adnotacja @Override, znaczy że
zmieniamy ich zawartość na własną.
Ekrany
Chcemy stworzyć ekran
gry, jednak najpierw dla uporządkowania stworzymy pod pakiet screens, gdzie
będą umieszczane wszystkie ekrany. Pod pakiety najlepiej porządkować pod
względem funkcjonalności, tak aby łatwo później można było nawigować po nazwie.
Aby stworzyć nowy pakiet,
klikamy w drzewie projektu na nazwę pakietu wewnątrz której chcemy umieścić pod
pakiet. Kolejno New > Package i wpisujemy nazwę screens. Teraz umieszczamy
klasę analogicznie, klikając na utworzony pod pakiet screens > New > Java
Class i wpisując nazwę PlayScreen.
W PlayScreen jako że ma
to być to ekran, potrzebujemy wszystkich metod jakie taki normalny ekran
posiada, w tym celu potrzeba zaimplementować interfejs Screen, czyli będziemy
korzystać z takich metod jakie są zawarte w interfejsie Screen.
Po nazwie klasy
PlayScreen dopisujemy implements Screens. W tym momencie powinno podświetlać
nam cała linię. Można najechać i wyświetli nam w formie chmurki podpowiedź
czego dotyczy problem. Tutaj „Class ‘PlayScreen’ must
be declared abstract Or implement abstract method ‘dispose()’ in ‘Screen’. U nas powodem jest brak zawartej tu metody
‘dispose()’. Jeżeli uda nam się odpowiednio najechać wyskoczy nam ikonka
czerwonej żarówki z możliwymi sugestiami co zrobić.
Wybierzmy Implement
methods, bo tego właśnie klasa oczekuje. Zaznaczmy wszystkie metody, czyli:
dispose():void
hide():void
pause():void
render(delta:float):void
resize(width:int, height:int):void
resume():void
show():void
I zatwierdzamy przyciskiem OK.
Na tym etapie można tylko
o nich powiedzieć że odpowiadają za poszczególne operacje jakim można poddać
ekran.
Przekazywanie obiektów
Ponieważ w naszej głównej
klasie gry delegowaliśmy aby gra toczyła się w klasie PlayScreen, potrzebne
było wskazanie na rzecz jakiej klasy została wywołana metoda
setScreen(new PlayScreen(this));
oznacza to, że w klasie WordCharger
został ustawiony ekran PlayScreen. Parametr this wskazuje że, zostało to zrealizowanie
na rzecz klasy WordCharger. Od tyłu tłumacząc, tworzymy nowy obiekt PlayScreen,
dla klasy WordCharger, po to aby można było ustawić go jako ekran. W miejscu
this, czyli wskaznie na obecną klasę, zostaje przekazany obiekt WordCharger do
klasy PlayScreen, a tam potrzeby jest konstruktor aby ustawić przekazany obiekt
jako obowiązujący wewnątrz klasy PlayScreen. Dopisujemy więc:
Private WordCharger game;
public PlayScreen(WordCharger game) {
this.game = game;
}
Dla uściślenia
konstruktor opisuje w jaki sposób dany obiekt może zostać tworzony. Co oznacza
że może przyjmować parametry go określające. W naszym przypadku najpierw
tworzymy pusty obiekt WordCharger game. A konstruktor ustala te pole na to co
zostaje przekazane przy tworzeniu obiektu, widać to tu
new PlayScreen(this).
Testujemy
W celu przetestowania czy
wszystko wykonaliśmy popranie, możemy teraz uruchomić to co było w przykładowym
projekcie na początku. W klasie PlayScreen inicjujemy
Texture img;
Wewnątrz konstruktora
dopisujemy
img = New Texture(„badlogic.jpg”);
Wewnątrz metody render,
tak jak poprzednio z małą różnicą, że przed batch umieszczamy jeszcze game, po
to aby odwołać się do tego obiektu zdefiniowanego w głównej klasie gry
WordCharger. Batch służy do wyświetlania prostokątnych obszarów, w przykładzie
obrazek badlogic.
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game.batch.begin();
game.batch.draw(img,
0, 0);
game.batch.end();
W klasie głównej
WordCharger w linijce deklaracji obiektu SpriteBatch należy poprzedzić tzw.
modyfikatorem dostępu public. Ponieważ nazwy pakietów są różne nie mamy
bezpośrednio dostępu do obiektu batch, odpowiedni modyfikator załatwia tą
sprawę. Modyfikatory w głównej mierze służą do określania co ma być widoczne, a
co nie poza klasą w której go inicjujemy. Do tego jeszcze wrócimy.
Testujemy podobnie jak w
poprzednim wpisie. Wszystko jest identycznie, jednak tym razem jest to
poprawnie porozdzielane i przygotowane pod grę. Na dziś tyle, co i tak jest
sporo do zrozumienia.
Brak komentarzy:
Prześlij komentarz