Na początku
tworzymy w pakiecie sprites nową klasę Battery
public class Battery extends Sprite {
wewnątrz potrzebne będą 2 zmiennie: regionu do wyświetlenia, oraz zmiennej w której przechowamy atlas
private TextureRegion batterylevel; private TextureAtlas atlas;
w konstruktorze
przypiszemy obiektowi klasy ten przekazany, oraz ustawimy rozmiar tekstury,
który będzie niezmienny dlatego robimy to od razu tutaj.
public Battery(TextureAtlas at) { this.atlas = at; setBounds(0, 0, 100 / WordCharger.PPM, 50 / WordCharger.PPM); }
Wyświetlanie baterii
Następnie robimy
resztę czynności które potrzebne są do wyświetlenia elementu: są to odnalezienie
odpowiedniego rejonu, oraz ustawienie go jako ten do wyświetlenia. Każdy kolor
baterii będziemy ustawiać za pomocą jednej z 4 metod. Robimy więc analogiczne
kroki dla 4 pozostałych kolorów wskazując odpowiednie miejsca. Współrzędne
można sprawdzić w pliku battery_enemy.pack w katalogu assets projektu
androidowego.
public void setOrange() { batterylevel = atlas.findRegion("red"); batterylevel = new TextureRegion(batterylevel.getTexture(), 0, 0, 128, 64); setRegion(batterylevel); }
Aktualizacja pozycji baterii
Do szczęścia
potrzebujemy jeszcze aktualizować pozycję baterii w miejscu aby znajdowała się
nad jego głową. W tym celu w metodzie przekażemy cały obiekt bohatera z którego
następnie wyciągniemy potrzebne nam informacje. Współrzędne wyciągamy kolejno z
b2dBody bohatera metodą getPosition(). Obiekt umieścimy wyżej o całą wysokość
sprita getHeight() od miejsca górnego krańca miejsca y bohatera. Akurat taka
wysokość jest odpowiadająca
public void update(BatteryHero hero) { setPosition(hero.b2dBody.getPosition().x / 0.7f - getWidth() / 2, hero.b2dBody.getPosition().y / 0.7f + getHeight()); }
Teraz przechodząc
do PlayScreen najpierw
inicjalizujemy obiekt
private Battery battery;
W konstruktorze
tworzymy obiekt i przekazujemy mu wymagany atlas, oraz wstępnie ustalamy
przykładowy kolor na pomarańczowy
battery = new Battery(atlas); battery.setOrange();
Na razie zmiana
kolorów ustalona jest na sztywno, w późniejszych etapach gdy będziemy mogli już
cos zbierać stworzymy odpowiednie warunki do zmiany koloru.
Obecnie bateria
będzie pozostawała jeszcze w miejscu, należy w metodzie update wywołać metodę
aktualizującą, wraz z obiektem player z którego wyciągniemy aktualne
współrzędne
battery.update(player);
Osiągnięty efekt
podążająca nad głową bateria
Kolizja
Obecnie wiemy że
następuje jakaś interakcja między obiektami (platformy, notatki) a bohaterem,
jest to oczywiste ponieważ nie możemy w tych miejscach stanąć. Teraz kolejnym
elementem który chcemy zrealizować jest zbieranie karteczek. Do tego wymagana
będzie informacja, kiedy jeden obiekt spotyka na swojej drodze drugi tzw.
następuje kolizja. Po identyfikacji co z czym koliduje, będziemy mogli
przypisać tym obiektom zachowania jakie chcemy osiągnąć np. spowodujemy aby
karteczka znikła. Elementy które dodaliśmy do world mogą zacząć być sprawdzane
czy występuje między nimi kolizja. Działa się to na prostej zasadzie
sprawdzania czy tzw. Fixture tych obiektów się dotykają.
Najpierw na
fixture obu elementów tzn. bohaterowi oraz Wordnote używamy metod dzięki którym
będziemy mogli później do tych obiektów odwołać się przy identyfikacji.
W konstruktorze
Wordnote wskazując na ten obiekt
fixture.setUserData(this);
Fixture ale dla klasy dziedziczącej
jest tu pewna
poważna rzecz, którą potrzeba uwzględnić
ponieważ obiekt
WordNote dziedziczy InteractiveTileObject, a chcemy ustawić metodę setUserData
konkretnym klasom, musimy dać możliwość odwołania się do Fixture klasie
nadrzędnej. Więc najpierw musimy stworzyć konkretny obiekt i przypisać mu tak
stworzone fixture. Modyfikator proctected umożliwi odwołanie się do tego
obiektu klasom dziedziczącym. Więc w klasie InteractiveTileObject tworzymy
protected Fixture fixture;
a w konstruktorze
przypisujemy wcześniej stworzony już obiekt do fixture
fixture = body.createFixture(fdef);
Jak pamiętamy
fixture tworzymy w klasie InteractiveTileObject, co jest powodem że jeżeli w
tym miejscu ustawilibyśmy etykietę setUserData, to brakowało by rozróżniania z
jakim obiektem mamy do czynienia.
Ustalając w
klasie wyżej WordNote tą metodą spowodujemy, że będziemy mogli identyfikować
konkretne obiekty.
Teraz w BatteryHero
przy tworzeniu Fixture dopisujemy metodę setUserData wraz z etykietką
„hero". Hero będzie podstawą naszego warunku, ponieważ to go właśnie
chcemy identyfikować czy koliduje z innymi obiektami na mapie.
b2dBody.createFixture(fixtureDef).setUserData("hero");
ustawiamy również
parametr isSensor dzięki czemu zbieramy informacje o kontakcie, ale nie
generujemy odpowiedzi kolizji.
fixtureDef.isSensor = true;
kolejną ważną
rzeczą jest stworzenie abstrakcyjnej metody w InteractiveTileObject. Będzie to
metoda która będzie wywoływana przy kontakcie bohatera z warstwą interaktywną
(platformy, notatki). Przy czy jest abstrakcyjna, ponieważ definicje jej
zrobimy osobno każdemu obiektowi, odpowiednio do zachowania jakie chcemy
uzyskać.
public abstract void onHit();
W klasie WordNote
natomiast nadpisujemy tą metodę w treści logując o następującej kolizji
@Override public void onHit() { Gdx.app.log("WordNote", "Collision"); }
ContactListener
Teraz pora na
identyfikacje kontaktów między obiektami. Tworzymy w pakiecie Tools nową klasę
WorldContactListener implementującą interfejs ContactListener. ContactListener
zawiera metody które są realizowane w momencie kontaktu.
public class WorldContactListener implements ContactListener {
Interfejs ContactListener
będzie od nas wymagał, aby zaimplementować wewnątrz 4 metody. Są to
odpowiednio:
beginContact –
kiedy rozpoczyna się dotyk dwóch fixture
endContact –
kiedy kończą
postSolve – zachowania
przy rozwiązywaniu wyjścia z kolizji
preSolve -
zachowania przy rozwiązywaniu wyjścia z kolizji
Zajmiemy się w
głównej mierze beginContact
Najpierw
wyciągamy oba fixture które biorą udział w kolizji
Fixture fixA = contact.getFixtureA(); Fixture fixB = contact.getFixtureB();
Identyfikacja obiektów
Sprawdzamy czy chociaż
jeden z nich to “hero”
if(fixA.getUserData() == "hero" || fixB.getUserData() == "hero") {
Sprawdzamy które
Fixture jest dokładnie czym. Fixture hero przypisujemy fixA lub fixB w
zależności czy fixA.getUserData() == "hero". Wystarczy sprawdzenie
jednego warunku, ponieważ są tylko 2 opcje.
Fixture hero = fixA.getUserData() == "hero" ? fixA : fixB;
Jeżeli hero ==
fixA, to obiektem jest fixB, jeżeli nie to automatycznie obiektem jest fixA
Fixture object = hero == fixA ? fixB : fixA;
Wywołanie metody klasy
interaktywnej
Teraz pora na
sprawdzenie czy obiekt jest klasy interaktywnej i wywołanie na nim metody.
Sprawdzamy czy
obiekt nie jest pusty i czy jest klasy InteractiveTileObject. Dokładniej to
sprawdzamy metodą isAssignableFrom() czy rozróżniony obiekt drugi poza
bohaterem object.getUserData().getClass())
jest oznaczony InteractiveTileObject.class.
Może to wystąpić jedynie w przypadku gdy dziedziczy właśnie z tej klasy
if(object.getUserData() != null && InteractiveTileObject.class.isAssignableFrom(object.getUserData().getClass())) { } }
Wewnątrz warunku rzutując object na InteractiveTileObject możemy wywołać abstrakcyjną metodę onHit(). A ta w zależności od implementacji wykona metodę zdefiniowaną bezpośrednio w klasie WordNote.
((InteractiveTileObject) object.getUserData()).onHit();
Wszystko co
dzisiaj zrobiliśmy było tylko dla jednego widocznego komunikatu w konsoli. Ale
co tam, było warto ;d
Tyle na dziś,
Pozdrawiam
https://github.com/KrzysztofPawlak/WordCharger/tree/wpis14
Brak komentarzy:
Prześlij komentarz