Poszukiwania kontrolera
Od razu do głowy
przyszło gdzieś tam stare wspomnienie że w grze rayman-ie na androidzie było to
zrobione w dość fajny sposób. Tu co prawda akurat zdjęcie z innej platformy ale
wygląda identycznie.
Zdjęcie stąd: http://www.imore.com/rayman-2-the-great-escape-now-in-app-store
Mamy tu
sterowanie za pomocą analoga, coś na zasadzie określania pozycji dotyku palca
(pole siwe) względem środka koła czerwonego. Trzeba więc tu przeliczać trochę
współrzędnych, wychylenie. Zbyt czasochłonne aby to zrobić, więc rezygnujemy.
Szukając dalej w
Internecie, w serwisie youtube w oko wpadła jedna gra. Filmik prezentuje po
rosyjsku jakiś ranking gier mobilnych w 2015 roku, albo coś podobnego. Gra
znajdziemy na google play pod link.
Co szczególnego w tej grze to prostota, która wygląda dość fajnie. A i
sterowanie idealne do naszych warunków polowych ;d
Link do tego
rosyjskiego rankingu: https://www.youtube.com/watch?v=eoAG9lOPqTk
Klasa kontrolera
Więc zrobimy to w
taki sam sposób jak w grze powyżej, przy czym później po prawej stronie nad
przyciskiem skoku obsadzimy dodatkowe przyciski/tabliczki z słówkami. Plan więc
jest, to do dzieła.
Na początku
umieśmy grafiki odpowiedzialne z sterowanie w katalogu assets projektu
androidowego.
Teraz stwórzmy klasę która będzie odpowiedzialna za rysowanie strzałek i wychwytywanie zdarzeń po ich kliknięciu. Tworzymy więc w pod pakiecie scenes nową klasę Controller. Klasa ta będzie miała dużo cech wspólnych z klasą Hud, gdzie wyświetlamy pola tekstowe u góry ekranu. Dodajemy elementy które będą potrzebne
Viewport viewport; Stage stage; boolean upPressed, leftPressed, rightPressed; OrthographicCamera cam;
Viewport będzie
obszarem zainteresowania który widzimy, stage jest sceną na której występują
aktorzy, a w naszym przypadku to figury geometryczne które nasłuchujemy i które
są odpowiedzialne za sterowanie. W zmiennych boolean będziemy przechowywać
informacje czy akurat trzymamy palce nad danym przyciskiem, zmienna ta będzie
mówiła o wysłaniu impulsu kierunku w odpowiednia stronę. Oraz ostatnią rzeczą
będzie kamera mówiąca jak mamy patrzeć na ekran. Więcej informacji o tym w znajdziecie
w wpisie.
Następnie tworzymy
konstruktor wewnątrz którego umieszczamy
public Controller() { cam = new OrthographicCamera(); viewport = new FitViewport(800, 480, cam); stage = new Stage(viewport, WordCharger.batch);
Zwykła
inicjalizacja. Tu dosyć ważne przy tworzeniu sceny ustalamy gdzie mamy patrzeć
oraz w drugim parametrze powinno się umieszczać batch (pojemnik z elementami do
wyświetlania), jednak nie nowy, a ten z głównej klasy gry. Musimy więc poczynić
małą zmianę, aby móc się do niej odwołać. W klasie WordCharger dodajemy
zmiennej batch modyfikator static, dzięki temu określamy że od tego momentu
może być tylko jeden taki egzemplarz tej zmiennej oraz istniejący przez cały
czas działania programu.
public static SpriteBatch batch;
InputProcessor z nasłuchem
Dalej w
konstruktorze ustalamy elementowi stage Input
Processor, który jest odpowiedzialny za wyłapywanie wszelkich sygnałów
wejściowych (touch screen, myszki, czy ekranu).
Gdx.input.setInputProcessor(stage);
Kolejnym krokiem
jest dodanie grafik zgodnie z kierunkiem sterowania. Zrobimy to dla jednego
kierunku. Reszta wygląda analogicznie. Tworzymy więc obiekt Image nadając jakąś
nazwę związana z kierunkiem np. controlsRightImage. Dalej w parametrze obiektu
tworzymy nowy Texture wraz z nazwą pliku graficznego umieszczonego w folderze
assets controlRight.png.
Image controlsRightImage = new Image(new Texture("controlRight.png"));
Ustalamy rozmiar
jaki ma przyjąć obrazek
controlsRightImage.setSize(96, 96);
I teraz ważne
dodajemy Listener przez stworzenie nowego listenera, otwieramy klamry wewnątrz których
chcemy zaimplementować użyteczne nam metody. Listener jest obiektem który
nasłuchuje, tzn. sprawdza czy nie pojawia się jakiś sygnał wejściowy.
controlsRightImage.addListener(new InputListener() { }
Metody listenera
Klikając wewnątrz
otwartego listenera prawym przyciskiem myszy wybieramy Generate… a następnie
Override Methods… Z pośród wszystkich dostępnych wybieramy touchDown i touchUp.
Te są akurat odpowiedzialne za dotknięcia, co będziemy wykonywać na ekranie
naszego telefonu. Poza tym można zobaczyć że są inne odpowiedzialne za
scroolowanie (przewijanie), obsługa myszy, czy klawiatury.
@Override
@Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { rightPressed = true; return true; } @Override public void touchUp(InputEvent event, float x, float y, int pointer, int button) { rightPressed = false; }
Metoda down jak
nazwa mówi wykona się jak paluch będzie nad tym elementem strzałki. Ustawiamy
więc wartość boolean rightPressed na true. Metoda jest typu boolean więc taką
wartość trzeba zwrócić przez return true. Zresztą środowisko w którym pracujemy
nas o tym poinformuje przez podkreślenie błędu. Metoda touchUp jest
przeciwieństwem więc trzeba ustawić wartość boolean na false. Sama metoda jest
typu void więc nic nie zwraca. Krótko podsumowując tworzymy obiekty obrazków
które będą nasłuchiwane/sprawdzane czy nie następuje ich dotknięcie. W
zależności od tego ustawiamy odpowiednią zmienną boolean. Ta w późniejszych
etapach będzie przekazana do głównej pętli gry i w zależności od wartości tych
zmiennych będzie wykonywany impuls w odpowiednia stronę.
Wyświetlanie w table
Teraz czas aby
wyświetlić wszystkie strzałki kierunku. Aby zachować pewną organizacją i
łatwość ustalania położenia skorzystamy z table (stołu). Już to wcześniej robiliśmy.
Obszerna dokumentacja o wszystkich możliwych położeniach jest tutaj.
Więc tak generalnie
umieszczamy przycisk lewo i prawo na dole ekranu po lewej stronie. Tworzymy
stół ustalając jego pozycję i metodą setFillParent mówimy aby dopasował się
wielkości stage.
Table tableLeftRight = new Table(); tableLeftRight.left().bottom(); tableLeftRight.setFillParent(true); Table tableJump = new Table(); tableJump.right().bottom(); tableJump.setFillParent(true);
Tworzymy 2 stoły
już tłumacze dlaczego. Chcemy stworzyć przyciski lewa/prawa po lewej stronie i
góra (up) po prawej.
Metodą left()
można od razu określić pozycję stołu po lewej stronie. I to by było ok na ten
moment. Dodatkowo chcemy lekko odsunąć od lewej krawędzi ekranu, co przy
różnych ekranach zawsze było by uwzględniane. Problem pojawia się przy dodaniu
przycisku po prawej stronie. Ustalając raz pozycję stołu, zmieniając teraz
pozycję na prawo metodą right() przeniosło by to wszystkie elementy na prawo.
To nas nie urządza.
Drugą opcją
byłoby dodaniu przestrzeni ileś pikseli zaczynając od lewej strony np. przy
korzystaniu z metody pad. Tylko to zupełnie nie zdało by testu przy innych
ekranach.
Co nam potrzeba
to złapać prawą stronę i to od niej odliczyć odległość jaką chcemy od prawej
krawędzi ekranu. Te rozwiązanie zda test przy każdych rozdzielczościach,
dlatego więc tworzymy 2 niezależne stoły.
Teraz poniżej
image które stworzyliśmy do istniejących stołów dodajemy właśnie te obiekty, a
następnie całość dodajemy jako aktor do stage. Stage jest sceną z obiektami
które będą mogły być wyrysowane.
Dla przycisku
lewa i prawa
tableLeftRight.padBottom(10); tableLeftRight.add().pad(10); tableLeftRight.add(controlsLeftImage).size(controlsLeftImage.getWidth(), controlsLeftImage.getHeight()); tableLeftRight.add().pad(20); tableLeftRight.add(controlsRightImage).size(controlsRightImage.getWidth(), controlsRightImage.getHeight()); stage.addActor(tableLeftRight);
dla przycisku skoku
tableJump.padBottom(10); tableJump.add(controlsUpImage).size(controlsUpImage.getWidth(), controlsUpImage.getHeight()); tableJump.add().pad(10); stage.addActor(tableJump);
Warto zwrócić
uwagę że przy dodawaniu do stołu dodaliśmy obiekt obrazku wraz z jego rozmiarem który, określiliśmy przy
tworzeniu obiektu.
odległości które poustawialiśmy wyżej
Obsługa z zewnątrz
Potrzebujemy
teraz móc wywołać metodę rysowania na obiekcie tego stage z klasy głównej gry,
tworzymy więc do tego odpowiednią metodę.
public void draw() { stage.draw(); }
Pora użyć
zbudowanego kontrolera w grze. W klasie PlayScreen deklarujemy
Controller controller;
W konstruktorze
PlayScreen zaś inicjujemy tworząc nowy obiekt Controller
controller = new Controller();
W metodzie render
czas na wyświetlenie. Robimy to na końcu, dzięki czemu będzie ostatnie rysowane
i będzie przykrywało wszystkie inne obiekty (będzie na wierzchu)
controller.draw();
Warto przy okazji
przenieść tutaj na koniec kiedyś dokonane rysowanie hud, co dotychczas było
niepoprawnie, ponieważ było niewidoczne zakryte gdzieś pod spodem
hud.stage.draw();
Efekt już widać
Touch, Touch, Touch
Jak można
sprawdzić jest wszystko widoczne, ale brak reakcji na jakiekolwiek dotknięcie.
Trzeba sobie przypomnieć jak to działało wcześniej dla klawiatury. W metodzie
update która wykonywana jest cyklicznie co określony przyrost czasu była
wywoływana metoda handleInput(). A w wewnątrz niej sprawdzaliśmy czy jakiś
klawisz nie jest wciśnięty. Teraz aby dokonać tego samego dla stworzonego
kontrolera trzeba dodać kolejne warunki w handleInput()
if (contoller.isRightPressed() && player.b2dBody.getLinearVelocity().x <= 2) { player.b2dBody.applyLinearImpulse(new Vector2(2f, 0), player.b2dBody.getWorldCenter(), true); } if (contoller.isLeftPressed() && player.b2dBody.getLinearVelocity().x >= -2) { player.b2dBody.applyLinearImpulse(new Vector2(-2f, 0), player.b2dBody.getWorldCenter(), true); } if (contoller.isUpPressed()) { player.b2dBody.applyLinearImpulse(new Vector2(0, 1f), player.b2dBody.getWorldCenter(), true); }
Nie będzie to
omawiane. Najważniejszym punktem jest zmiana pierwszej części warunku z input na
sprawdzenie wartości boolean w klasie controller. Musimy więc wydobyć te
wartości z klasy kontrolera. Przechodzimy i tworzymy wewnątrz Controllera
gettery, czyli metody odpowiedzialne za zwracanie wartości. Getter działa dosyć
prosto zwraca wartości zmiennej która jest widoczna w danej klasie. Ponieważ do
zmiennych w klasie nie powinno się mieć dostęp w sposób bezpośredni, stosuje
się metody które służą tylko do
wyciągania. W tym przypadku daje to pewne zabezpieczenie, ponieważ getter
oferuje tylko zwracanie wartości która jest ustawiona, nie może jednak tej
wartości z innej klasy zmienić. Chyba że istnieją jeszcze settery. Na ten
moment te wyjaśnienie z pewnością wystarczy. Zarówno gettery jak i settery
można wygenerować automatyczne, z racji że jest to często używany mechanizm,
środowiska programistyczne nam to udogadniają.
Klikamy prawym
przyciskiem gdzieś poniżej ciała konstruktora i wybieramy Generate… i następnie
Getter. Wybieramy leftPressed, rightPressed i upPressed.
public boolean isLeftPressed() { return leftPressed; } public boolean isRightPressed() { return rightPressed; } public boolean isUpPressed() { return upPressed; }
Mamy więc gettery
z których możemy skorzystać jako metody klasy Controller. Przechodzimy z powrotem
do warunku odpowiedzialnego w metodzie handleInput()
I wywołujemy na
obiekcie kontroler właśnie te metody contoller.isRightPressed(). Metoda zwróci
nam odpowiednią wartość boolean zmianianą przez listener obrazka strzałki
sterowania i jeżeli któraś z nich będzie
będzie true to zostanie wykonany impuls.
Po przetestowaniu
dodatkowo został zmieniony wektor siły przy wciśnięciu UpPressed na newVector (0,
1f), jest
bardziej realistyczny i nie wywalający od razu w górę.
Tyle, Efekt jak
wyżej na rysunku, tylko można dodatkowo sterować.
Pozdrawiam
https://github.com/KrzysztofPawlak/WordCharger/tree/wpis16
Brak komentarzy:
Prześlij komentarz