piątek, 17 marca 2017

Kamera, widok, akcja


Często aplikacja może być uruchamiana na różnych urządzeniach z różną wielkością ekranu. Samych rozdzielczości wyświetlania jest cała masa, przy czy można wyróżnić kilka formatów (Aspect Ratio), co widać kolorami na rysunku poniżej. Ma to wpływ bezpośrednio na to jak wygląda nasza gra, tu właśnie w libGDX przychodzą klasy Viewport, które określają sposób radzenia sobie z tym problemem. Dwa Elementy które będą nam potrzebne na dziś to camera i viewport. Spróbujmy jakoś to umówić.



Camera

Pierwszym krokiem będzie zastosowanie kamery. To tak jak w rzeczywistości możemy na obiekty patrzeć albo na wprost albo spod kąta. I w libGDX mamy do wyboru dwie możliwości PerspectiveCamera i OrthographicCamera. Przeszukując Internet udało się znaleźć fajny rysunek który idealnie prezentuje różnicę w obu podejściach.



Jak widać w kamerze ortogonalnej, obiekty rzutowane są równolegle do punktu widzenia, zaś w kamerze perspektywicznej widzenie rozchodzi się z jednego punku i pod różnym kontem rzutowane są zarysy obiektów na ekranie który je wyświetla. W naszym przypadku platformówki oczywiste jest zastosowanie kamery ortogonalnej, jako że obiekty widziane są bezpośrednio tak jakby z boku.

Poniższe linie umieszczamy w naszym projekcie. Najpierw w naszym ekranie gry czyli PlayScreen inicjalizujemy obiekt OrtographicCamera który nazywamy przykładowo camera.

    private OrthographicCamera camera;

Póki co jest to na razie tylko miejsce zarezerwowane. To co potrzeba teraz zrobić to w konstruktorze powiedzieć/przypisać że jest to nowy obiekt klasy OrthographicCamera następującą linią kodu:

    camera = new OrthographicCamera();

Właśnie tak w Javie wygląda tworzenie nowych obiektów wcześniej już zdefiniowanych klas. Dodatkowo można pomiędzy nawiasami przesyłać różne parametry, jeżeli dana klasa ma zdefiniowane różne konstruktory. O konstruktorach było już wcześniej, dlatego jeżeli istnieje potrzeba to wróć do wcześniejszego wpisu.

Viewport

Viewporty jest to pojęcie z teorii grafiki komputerowej, ale spokojnie to nie uczelnia ;d. Patrząc przez kamerkę jaką znamy np. z aparatu naszego telefonu widzimy tylko wycinek tego co dzieje się wokoło. Viewport to właśnie nic innego jak obszar w postaci wieloboku, inaczej obszar zainteresowania, to co widzimy. A co w przypadku gdy obszar wyświetlania się zmienia np. wysokość się zwęża, jak powinny wyglądać wtedy wyświetlane elementy? libGDX dostarcza różne możliwe na to strategie. Porównamy dla wyczucia różnic dwie z nich FitViewport oraz StretchViewport. Więc dopisujemy stosowne linie kodu, później sprawdzimy różnice i wybierzemy ten odpowiadający naszym potrzebom.

W PlayScreen dopisujemy:

    private Viewport view;

natomiast w konstruktorze

    view = new StrechViewport (800, 480, camera);

Sam StrechViewport ma zdefiniowane dwa konstruktory: dwu i trzy parametrowy. Wykorzystujemy jednak trzy parametry, czyli szerokość, wysokość okna, oraz z jakiej kamery mamy patrzeć na te obiekty. Patrzymy na wprost czyli konieczna jest użycie wcześniej stworzonej OrthographicCamery. StrechViewport przy zmianie wielkości ekranu rozciąga zawsze tak aby obiekt zajmował tą samą część ekranu, czyli nie trzyma aspect ratio. Co czyni niestety przy np. wąskich ekranach, że obiekt staje się słabo widoczny. Takiego efekt raczej nie będzie wyglądał ciekawie. Fitport dla odmiany utrzymuje aspect ratio przy zmianach szerokości i wysokości. A miejscach pozostałych dodaje czarne paski. Z reszta na rysunku wyraźnie to widać. W naszym projekcie zastosujemy Fitport.


HUD

Mamy zrobiony już widok oraz kamere. Jak w większości gier wyświetlają się w którym miejscu jakieś punkty życia, magii, zebrane punkty czy czas. Jest dobry moment aby się za to się zabrać i wyświetlić to na widocznym ekranie. Jednak nie można tego bezpośrednio tak po prostu wyświetlić w viewport, z prostej przyczyny że nasz świat będzie się poruszał. W tym celu stosuje się osobny viewport i kamerę, dzięki czemu będzie mogło być statyczne niezależnie od reszty. Określa się to HUD (Head-Up Display), jest to przezroczysta szybka która wyświetla informacje. Sam termin zaczerpnięty z zastosowań militarnych, gdzie służył do prezentacji danych w samolotach bojowych.

Tworzymy nową klasę Hud, umieszczając ją w nowym pakiecie Scenes.

Pierwsze co to inicjalizujemy obiekty jakie będziemy potrzebować. W języku Java który jest językiem obiektowym, każda element jest obiektem. Przykładowo zwykły tekst

    String nazwa = „przykładowy string”

jest również obiektem klasy String. Wracając jak było wcześniej wspomniane potrzeba stworzyć viewport i kamerę. Ponadto zakładamy że chcemy wyświetlić napis pokazujące zdobyte punkty i poziom gry.

    public Stage stage;
    private Viewport viewport;
    private Integer score;

Stage jest sceną na której występują aktorzy. Stage jest jakby kontenerem wszystkich obecnych obiektów. Aktorzy to graficzne elementy z konkretnymi parametrami, które mogą dodatkowo się zmieniać. Wszystko co widzimy na scenie jest jakby w trakcie podłączenia do prądu, występuje akcja, mogą na tym występować różne animacje, czynności np. zderzenia, czy nachodzenie na siebie. Wszystkie obiekty są nasłuchiwane. Dodatkowo mogą się poruszać i być animowane.  Oczekują także na interakcje z użytkownikiem, który może mieć wpływ na przebieg zdarzenia przez np. sterowanie klawiaturą

Sama zmienna Integer przechowuje jedynie wartość. Aby możliwe było wyświetlenie tego na ekranie potrzeba skorzystać z klasy Label.

    Label scoreLabel;
    Label levelLabel;

Pora na konstruktor, to co trzeba jeszcze wiedzieć to rysowanie wymaga skorzystania z klasy SpriteBatch. SpriteBatch jest jakby kontenerem elementów do rysowania, gdzie wskazuje się indeksy położenia. Przykładowo jeżeli chcemy wyświetlić jakiś obrazek potrzeba go w takim kontenerze umieścić. Wykonuje się to pomiędzy batch.begin() i batch.end(). Aby nasz Hud był widoczny potrzeba go umieścić w batch. Robi się to po przez przesłanie go przez konstruktor. Celem jest podanie tego przy tworzeniu Stage, tak aby mógł zostać uwzględniony w kontenerze do wyświetlania, drugie co podajemy to do Stage to jaką strategie wyświetlania wybraliśmy przez viewport.

        public Hud(SpriteBatch sb) {
        score = 0;

        viewport = new FitViewport(800, 480, new OrthographicCamera());
        stage = new Stage(viewport, sb);


Teraz potrzebne będzie jakaś organizacja wyświetlania naszych labeli na ekranie. W libGDX służy do tego klasa Table. To tak jak dla gości wystawiasz wszystko na stół, gdzie wszystko jest osobno poukładane. Pozwala nam ustawianie nam elementów w jakąś siatkę kolumnami, bądź wierszami, w orientacji jakiej chcemy czy to poziomej czy pionowej. Metodą setFillParent możemy określić aby zajmowała cała powierzchnię, natomiast top() aby były układane od góry. W tym miejscu można się domyśleć że wszystko co znajduje się kropce to korzystanie z metod. I tak bierzemy stworzony obiekt table i po kropce używamy jego metody top(). Co w zależności jak jest zaimplementowane wewnątrz można spodziewać się różnych rezultatów.

        Table table = new Table();
        table.top();
        table.setFillParent(true);

Dalej nic wielkiego się nie dzieje obiektom ustawiamy szereg właściwości. Taki jak format wyświetlania z przewidywaną ilością znaków, rodzaj domyślnego fontu, czy kolor biały

    scoreLabel = new Label(String.format("%06d", score), 
        new Label.LabelStyle(new BitmapFont(), Color.WHITE));
    levelLabel = new Label(String.format("1", score), 
        new Label.LabelStyle(new BitmapFont(), Color.WHITE));

W tym miejscu pora dodać je do naszego stołu metodą add. Metodą expandX() ustawiamy na pełną szerokość. Jednak gdy wiele przedmiotów znajduje się obok siebie to przyjmują one po równo przestrzeni w zależności ile jest tych przedmiotów. PadTop(10) ustala wielkość komórek.

    table.add(scoreLabel).expandX().padTop(10);
    table.add(levelLabel).expandX().padTop(10);

na końcu dodajemy cały table do naszej sceny. Hud jest już gotowy, pora go użyć.

    stage.addActor(table);

Wracamy do PlayScren inicjalizujemy Hud

    Private Hud hud;

W konstruktorze tworzymy ten obiekt przesyłając obiekt game.batch.

    Hud = new Hud(game.batch)

W metodzie render usuwamy te linie z przykładu gdzie był game.batch. Chcemy teraz widzieć Hud zamiast przykładowego rysunku.

Musimy wskazać spritebatch gdzie obecnie jesteśmy, tzn. gdzie patrzy kamera. To co na ekranie powinno być widoczne to właśnie to na co patrzy kamera. Combined jest połączeniem projekcji i widoku. Robimy to linią

    game.batch.setProjectionMatrix(hud.stage.getCamera().combined);

W końcu zlecamy wyrysowanie całości

    hud.stage.draw();

Efekt na obecny moment prezentuje się następująco


Przy okazji powstał pomysł na inny wpis więcej o kamerach i perspektywach w grach. Tyle na dziś do odbioru.

https://github.com/KrzysztofPawlak/WordCharger/tree/wpis5


Brak komentarzy:

Prześlij komentarz