środa, 12 kwietnia 2017

Tekstura gracza

W dzisiejszym wpisie trochę upiększymy grę i i w końcu dodamy grafikę gracza. Po chwili prób stworzenia własnej grafiki, dałem sobie spokój i stwierdziłem że wymagać będzie to zbyt dużo wysiłku, dlatego skorzystamy z gotowych. Uczymy się w końcu programowania, a nie tworzenia grafik. W oko dosłownie wpadła umieszczona ta tutaj


W późniejszych etapach dodamy jeszcze poziomą baterię nad głową która się ładuje. O taki sobie wymysł. Poza tym animacja wygląda jakby kogoś goniła, albo uciekała. Fajne skojarzenie. A w końcu chcemy dorwać trochę słówek w języku obcym. A tu poniżej bateria mojego autorstwa. Niby banał, a zajął ponad 1 godzinę. Zrobienie zielonego ludka trwało by wieki. Zrobione przy użyciu darmowego narzędzia do tworzenia grafiki wektorowej Inkscape.


Jak widać grafika bohatera jest w różnych stadiach, to po to aby zrobić z tego animację. Ale o tym w następnym wpisie. Bateria natomiast może być w osobnych plikach graficznych ponieważ nie będzie animacją, a jedynie zmieniającym się stanem.

Narzędzia do sprit-ów

Mamy więc grafiki, można powrzucać je do folderu asset projektu androidowego. W przypadku wielu odrębnych plików do wczytania, dodatkowo istniałaby potrzeba wykorzystać do tego libgdx assets managera. To jedna sprawa. Inna to aby mieć pewne udogodnienie i móc korzystać szybciej i efektywniej w przyszłości, należy tektury upakować w jeden plik graficzny, z dodatkowym plikiem, który zawiera informacje o właściwościach grafik. Grafiki układa się tak, aby wykorzystać każdą wolną przestrzeń w sposób najbardziej optymalny, czyli w praktyce jedna obok drugiej możliwie ciasno. Poza tym narzędzie ma szereg dodatkowych opcji jak skalowanie na różne urządzenia, czy kompresja w celu przyspieszenia gry. Można skorzystać z dowolnego przeznaczonego do tego narzędzia, jest ich cała masa. My skorzystamy z tego tutaj wraz z GUI (graficznym interfejsem). Pobieramy i odpalamy

Tworzenie Pack’a

Tworzymy nowy pakiet klikając New pack, a następnie wpisujemy nazwę (np. battery_enemy


Kolejną niezbędną rzeczą będzie wskazać Input directory, czyli miejsce gdzie są wszystkie grafiki które chcemy dodać. Output directory wskazujemy na miejsce zapisu, zapiszmy więc od razu w folderze projektu androidowego assets. Po wskazaniu możemy kliknąć Pack’em all, powinno być już dostępne. W projekcie w android studio pojawiły dwa pliki. Obrazek .png oraz informacje o nim w pliku battery_enemy.pack. Pozwoli to nawigować po konkretnych grafikach z całej zapakowanej zawartości.

Wczytanie grafiki z atlasa

Zaczynamy więc od zadeklarowania obiektu  w klasie PlayScreen


private TextureAtlas atlas;

To do niego wczytamy cały zbiór obrazków z utworzonego wcześniej pliku .pack zawierające informacje o grafikach. Dokonujemy tego w konstruktorze, dzięki czemu będzie następowało od razu utworzeniu ekranu gry


atlas = new TextureAtlas("battery_enemy.pack");

Chcemy skorzystać z tego atlasu w klasie naszej postaci aby wybrać dla niej odpowiedni sprite, musimy więc jej to umożliwić. Przy tworzeniu BatteryHero dodatkowo przesyłamy wcześniej wczytany atlas


player = new BatteryHero(world, atlas);

Teraz przechodząc do klasy BatteryHero zmieniamy konstruktor dodając kolejny przekazany parametr atlas


public BatteryHero(World world, TextureAtlas at) {

Wycinanie Fragmentu

Aby móc wyciągnąć interesującą nas grafikę należy najpierw z atlasu wskazać odpowiedni do tego region (czyli nazwa grafiki)


this.batteryhero = at.findRegion("monsterSpriteSheet");

Później wyciągamy textury z tego obszaru i przekazujemy jako jeden z parametrów obiektu TextureRegion. Dodatkowe 4 parametry określają współrzędne obszaru jaki chcemy wyciąć. Bez nich wczytamy cała grafikę z wszystkimi elementami.


batteryhero = new TextureRegion(batteryhero.getTexture(), 0, 133, 100, 100);


metodą setBounds ustawimy pozycję i rozmiar rysowanego sprite, ponownie należy pamiętać o przeskalowaniu o PPM.


setBounds(0, 0, 100 / WordCharger.PPM, 100 / WordCharger.PPM);

zaś set region() ustawi textury i współrzędne obszaru który będzie wyświetlany


setRegion(batteryhero);

Wyświetlanie

Do obu metod mamy dostęp ponieważ dziedziczymy z klasy Sprite I TextureRegion odpowidenio, które sa częścią naszego obiektu BatteryHero.

Do wyświetlenia pozostaje nam to wrzucić do pojemnika jak robiliśmy poprzednim razem. Najpierw ustalamy w jaki sposób ma być wyświetlane to co zostanie dodane do pojemnika. Jednocześnie podajemy przeskalowanie. Wczytany obrazem jest wymiar 100x100, my jednak chcemy uzyskać wymiar 70x70 tak jak w przypadku reszty kafelek. Więc 100*0,7f=70


game.batch.setProjectionMatrix(camera.combined.scale(0.7f, 0.7f, 0));

Otwieramy puszkę


game.batch.begin();

wskazujemy aby na obiekcie gracza wywołać metodę rysowania, metoda draw() znajduje się w klasie sprite i dzięki dziedziczeniu możemy z niej skorzystać. Niezbędne jest podać w parametrze puszkę całej gry


player.draw(game.batch);

na końcu zamykamy całą puszkę gry
       

game.batch.end();

Omówione tutaj na szybko, ponieważ rysowanie przy pomocy batcha zostało dokładnie opisane w wpisie przy wyświetlaniu hud.

Obecnie wyświetla się w prezentowany niżej sposób. Na razie ludek gdzieś poniżej platformy. Jednak sprite nie podąża i nie porusza się jeszcze za pozycją debugowanego gracza. Więc pora się tym zająć.


Podążanie sprita z graczem

Najpierw zróbmy metodę do aktualizacji w klasie BatteryHero


public void update(float dt) {
        setPosition(b2dBody.getPosition().x / 0.7f - getWidth() / 2 , b2dBody.getPosition().y / 0.7f - getHeight() / 2);
    }

wewnątrz niej chcemy ustawiać pozycję sprita w miejscu b2dBody czyli miejsca debugowania (naszego bubla). Różnicą getWidth() / 2 ustalimy grafikę centralnie w środku. Samym getPosition wskażemy krańcową współrzędną obszaru po prawej stronie i to w niej pojawiłaby się nieprawidłowo grafika. Czyli 2x35=70. Różnicą cofniemy postać do miejsca żółtego x. Podobnie sprawa wygląda w przypadku y.


I tu drugi element który się pojawił czyli 0.7f. Pierwotnie w tym miejscu powinno być skalowanie które użyliśmy wcześniej czyli 1 / 1.3f = 0.76.... Jednak obszary minimalnie opóźniały się w pokrywaniu. Według mnie powodem jest utrata dokładności z dzielenia, chociaż nie dam sobie za to ręki uciąć. Więc całe skalowanie w grze zostało zmienione na 0.7f. Pojawiło się jak do tej pory około 7-8 razy. Najsprawniej miejsca zmian będzie można zerknąć w kod na githubie umieszczonym w linku na końcu wpisu. Właściwie do tej pory nie mam pojęcia dlaczego akurat wcześniej ustaliłem 1/1.3f.

Na końcu w klasie PlayScreen w metodzie update dodajemy


player.update(dt);

przekazując parametr czasu odświeżania.

Mission completed na dzisiaj, efekt poniżej



Pozdrawiam

Brak komentarzy:

Prześlij komentarz