wtorek, 14 marca 2017

Czas trochę namieszać przy projekcie.


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.glClearColor(1, 0, 0, 1);
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.

Public SpriteBatch batch;

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