Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

15. diel - Programujeme Android hru - Animácie, zvuky

Vitajte v ďalšom diele nášho kurzu programovanie hry pre Android. Než budeme pokračovať, stručne si vysvetlíme princíp činnosti nášho kódu z dielu minulého.

Trieda AssetManager.java je jasná, rovnako ako u obrázkov sme vykonali načítanie bitmapového písma (fontu) do pamäti - nič nové.

Zaujímavejšie je trieda OverlapsManager­.java. Neustále dookola je volaná jej metóda update (...), ktorá obsahuje na prvý pohľad zložitú podmienku:

if (Intersector.overlaps(food.getRectForOverlap(),chicken.getRectForOverlap()) && food.getWasClicked() && (!food.getWasCounted()) && chicken.isStanding())

Vyzerá zložito, ale nič na ňu nie je. Používame tu metódu overlaps (...) vstavané triedy Intersector, ktorá vracia true, ak sa pretínajú dva obdĺžniky odovzdanej ako argumenty. V našom prípade to sú obdĺžniky rectForOverlap v inštanciách chicken a food. Nám to ale nestačí, to že sa krmivo pretína s kuraťom, ešte neznamená pripočítanie skóre.

Musia byť splnené ďalšie podmienky. Na krmivo musí byť kliknuté (food.getWasClicked ()) a pripočítaní skóre musí prebehnúť až po zastavení kuraťa (chicken.isStanding ()). Nepravda! Food.getWasCounted () zabezpečí, aby skóre bolo pripísané iba raz, pretože ako už bolo povedané, metóda update (...) je volaná neustále. V okamihu, keď sú tieto podmienky splnené, dôjde k zvýšeniu skóre o jedničku a nastavenie premenné Lifetime v inštancii food na 0, tým fakticky začne žrebovanie a následné premietnutí krmiva na nových súradniciach. Neustále opakované volanie metód update () v našom projekte by sme v súčasnosti mohli znázorniť nasledujúcim diagramom:

Volanie metód update - Programujeme Android hru

Domnievam sa, že metóda clickedPosition (...) je výstižne okomentované. Je zavolaná inštancií inputManager vždy pri kliknutí používateľa na obrazovku a ako argumenty prijíma súradnice tohto kľučku. Stará sa o nastavení premennej wasClicked v inštancii krmiva ao zvýšenie / reset rýchlosti v inštancii kurčaťa.

To je na objasnenie minulej lekcie všetko. Už dobre vieme, že v súlade so zásadou OOP zapuzdrenie "musíme" premenné triedy deklarovať ako private a vytvoriť k nim prístupovej metódy a tiež, že po definíciu triedy musíme vytvoriť jej inštanciu (teraz opomenie statickú triedu). Pridanej metódy do triedy Render.java nie je nutné objasňovať, z ich názvu je jasné, čo premietajú.

Zvuky a animácie

Dnes sliepku pridáme zvuky a animáciu. Vždy, keď hráč klikne na krmivo, sliepky mu pípnutím "odpovie", že klikol na správne miesto, kde sa potrava nachádza. Ďalej pridáme animáciu so zvukom, ktorá sa prehrá vždy, keď sú splnené podmienky pre zvýšenie skóre, táto animácie bude teda znázorňovať, že sliepky práve žerie.

Začneme nahraním zdrojov. Otvoríme triedu AssetManager.java a pod deklarácia existujúcich premenných pridáme:

public static Texture eatLeft1,eatLeft2,eatLeft3,eatLeft4;      //na animaci eating
public static Texture eatRight1,eatRight2,eatRight3,eatRight4;      //na animaci eating
public static TextureRegion rEatLeft1,rEatLeft2,rEatLeft3,rEatLeft4;    //na animaci eating
public static TextureRegion rEatRight1,rEatRight2,rEatRight3,rEatRight4;//na animaci eating
public static Animation eatLeftAnime;                   //na animaci eating
public static Animation eatRightAnime;                  //na animaci eating

public static Music beepSound,eatSound;

Teraz všetky tieto premenné načítame, na koniec metódy load () pridáme nasledujúci kód:

/* zacatek nahrani animaci eating */
eatLeft1=new Texture(Gdx.files.internal("eatleft1.png"));
eatLeft1.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
eatLeft2=new Texture(Gdx.files.internal("eatleft2.png"));
eatLeft2.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
eatLeft3=new Texture(Gdx.files.internal("eatleft3.png"));
eatLeft3.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
eatLeft4=new Texture(Gdx.files.internal("eatleft4.png"));
eatLeft4.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);

eatRight1=new Texture(Gdx.files.internal("eatright1.png"));
eatRight1.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
eatRight2=new Texture(Gdx.files.internal("eatright2.png"));
eatRight2.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
eatRight3=new Texture(Gdx.files.internal("eatright3.png"));
eatRight3.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);
eatRight4=new Texture(Gdx.files.internal("eatright4.png"));
eatRight4.setFilter(TextureFilter.Nearest, TextureFilter.Nearest);

rEatLeft1=new TextureRegion(eatLeft1,0,0,115,90);
rEatLeft1.flip(false, true);
rEatLeft2=new TextureRegion(eatLeft2,0,0,115,90);
rEatLeft2.flip(false, true);
rEatLeft3=new TextureRegion(eatLeft3,0,0,115,90);
rEatLeft3.flip(false, true);
rEatLeft4=new TextureRegion(eatLeft4,0,0,115,90);
rEatLeft4.flip(false, true);
TextureRegion[]rEatLeft1234={rEatLeft1,rEatLeft2,rEatLeft3,rEatLeft4};
eatLeftAnime=new Animation(0.2f,rEatLeft1234);
eatLeftAnime.setPlayMode(Animation.PlayMode.LOOP);

rEatRight1=new TextureRegion(eatRight1,0,0,115,90);
rEatRight1.flip(false, true);
rEatRight2=new TextureRegion(eatRight2,0,0,115,90);
rEatRight2.flip(false, true);
rEatRight3=new TextureRegion(eatRight3,0,0,115,90);
rEatRight3.flip(false, true);
rEatRight4=new TextureRegion(eatRight4,0,0,115,90);
rEatRight4.flip(false, true);
TextureRegion[]rEatRight1234={rEatRight1,rEatRight2,rEatRight3,rEatRight4};
eatRightAnime=new Animation(0.2f,rEatRight1234);
eatRightAnime.setPlayMode(Animation.PlayMode.LOOP);
/* konec nahrani animaci eating */

beepSound = Gdx.audio.newMusic(Gdx.files.internal("pip12.mp3"));
eatSound = Gdx.audio.newMusic(Gdx.files.internal("woodpecker.mp3"));

Upraceme po sebe, na koniec metódy dispose () pridáme:

eatLeft1.dispose();
eatLeft2.dispose();
eatLeft3.dispose();
eatLeft4.dispose();
eatRight1.dispose();
eatRight2.dispose();
eatRight3.dispose();
eatRight4.dispose();

beepSound.dispose();
eatSound.dispose();

Pridáme importy, triedu uložíme. Samozrejme nezabudneme tieto súbory naimportovať do zložky assets.

Zdroje animácií i zvukov máme, ďalej potrebujeme zmeniť (prepnúť) stavové premenné kurčaťa na EATLEFT alebo EATRIGHT vždy, keď sú splnené podmienky pre zvýšenie skóre - keď "sliepky žerie" s tým, že týmto prepnutím v renderu vyvoláme premietaní týchto zdrojov animácií. Otvoríme si triedu OverlapsManager­.java a jej metódu update (...) zmeníme do nasledujúceho tvaru:

public void update(float delta) {   // trida hlida, zda se instance food a chicken protinaji!
    if (Intersector.overlaps(food.getRectForOverlap(),chicken.getRectForOverlap()) &&
    food.getWasClicked() && (!food.getWasCounted()) && chicken.isStanding()) {
        score[0]++;
        food.setWasCounted(true);

        if (chicken.getStandingState() == StandingState.STANDLEFT || chicken.getStandingState() == StandingState.STANDRIGHT) {
            if (chicken.getStandingState() == StandingState.STANDLEFT)
                chicken.setStandingState(StandingState.EATLEFT);
            else
                chicken.setStandingState(StandingState.EATRIGHT);
        }
    }
}

V tejto triede ešte upravíme metódu clickedPosition (...) a to tak, že do jej 1. if podmienky pridáme príkaz na prehranie zvuku pípnutia kuraťa, takže výsledná podoba tejto podmienky bude:

if(food.getRectForOverlap().contains(x, y)) {
    food.setWasClicked(true);
    chicken.incrWholeSpeed(10); // vzdy, kdyz tapneme na jidlo, zvysit rychlost
    if (!AssetManager.beepSound.isPlaying()) {
        AssetManager.beepSound.play();
    }
}

Predposledný záležitosťou, ktorú je nutné pre animácie vykonať, je rozvetvenie metódy update (...) v triede ObjectManager.java tak, že keď prebieha animácia, bude sa deltou odčítať jej "doba prehrávania" a keď animácie neprebieha, budú sa updatovať ostatné objekty . Otvoríme teda triedu ObjectManager.java a jej metódu update (...) rozšírme do nasledujúcej podoby:

public void update(float delta) {
    // kdyz probiha animace, tak updatuj prave jen animaci
    if (chicken.getStandingState() == StandingState.EATLEFT || chicken.getStandingState() ==    StandingState.EATRIGHT) {

        if (!AssetManager.eatSound.isPlaying()) {
            AssetManager.eatSound.play();
        }
        if (eatAnimeLifetime <= 0) {    // animace skoncila?
            if(chicken.getStandingState() == StandingState.EATLEFT) {
                chicken.setStandingState(StandingState.STANDLEFT);
                food.setLifeTime(0);
            } else {
                chicken.setStandingState(StandingState.STANDRIGHT);
                food.setLifeTime(0);
            }
        }
        eatAnimeLifetime-=delta;
    } else  {
        chicken.update(delta);
        food.update(delta);
        overlapsMng.update(delta);
        eatAnimeLifetime=2;
    }
}

Pridáme importy, IDE ohlasuje chybu neznáme premenné eatAnimeLifetime - napravíme. Pod deklarácia existujúcich premenných triedy doplníme ďalšie:

float eatAnimeLifetime;

a na konci konstruktoru prevedieme jej inicializáciu:

this.eatAnimeLifetime = 2;

Do metódy receivePosition (...) musíme pridať ešte jednu if podmienku, jej výsledná podoba bude:

public void receivePosition(int x,int y) { //souradnice z inputu //pozor, volano z inputu jen, kdyz je stav hry running

    if (!(chicken.getStandingState()==StandingState.EATLEFT || chicken.getStandingState()==StandingState.EATRIGHT) )
    {   // kdyz slepice zere, nereagovat na uzivatelsky vstup, kdyz zere tak nepravda
        overlapsMng.clickedPosition(x,y);
        turnMng.turnChicken(x, y);
        turnMng.setChickenDistance(x, y);
        turnMng.setChickenSpeed();

        chicken.setXPositionAchieved(false);
        chicken.setYPositionAchieved(false);
    }
}

Teraz už OK. Triedu uložíme.

Posledný nutnou vecou je premietnutie animácia. Otvoríme triedu Renderer.java, kde riadok s deklaráciami animáciou rozšírime do nasledujúcej podoby:

private Animation standLeftAnime,standRightAnime,eatLeftAnime,eatRightAnime;

Vykonáme inicializácii našich ďalších animácií, najlepšie blízko súčasných inicializáciou premenných standLeftAnime a standRightAnime v metóde initAssetsObjects ():

this.eatLeftAnime=AssetManager.eatLeftAnime;
this.eatRightAnime=AssetManager.eatRightAnime;

Na konci metódy drawChicken (...) existujúce else vetva:

else {
    runTime+=delta; // kvuli animaci
    if (chicken.getStandingState() == StandingState.STANDLEFT)
        batcher.draw(standLeftAnime.getKeyFrame(runTime), chicken.getPositionX(), chicken.getPositionY(), chicken.getWidth(), chicken.getHeight());
    else if (chicken.getStandingState() == StandingState.STANDRIGHT)
        batcher.draw(standRightAnime.getKeyFrame(runTime), chicken.getPositionX(), chicken.getPositionY(), chicken.getWidth(), chicken.getHeight());
    else
        Gdx.app.log("renderer:", "Sem by se rizeni nemelo dostat.");
}

rozšírime na:

else {
    runTime+=delta; // kvuli animaci
    if (chicken.getStandingState() == StandingState.STANDLEFT)
        batcher.draw(standLeftAnime.getKeyFrame(runTime),chicken.getPositionX(),chicken.getPositionY(),chicken.getWidth(),chicken.getHeight());

    else if(chicken.getStandingState() == StandingState.STANDRIGHT)
        batcher.draw(standRightAnime.getKeyFrame(runTime),chicken.getPositionX(),chicken.getPositionY(),chicken.getWidth(),chicken.getHeight());

    else if(chicken.getStandingState() == StandingState.EATLEFT)
        batcher.draw(eatLeftAnime.getKeyFrame(runTime),chicken.getPositionX(),chicken.getPositionY(),chicken.getWidth(),chicken.getHeight());

    else if(chicken.getStandingState() == StandingState.EATRIGHT)
        batcher.draw(eatRightAnime.getKeyFrame(runTime),chicken.getPositionX(),chicken.getPositionY(),chicken.getWidth(),chicken.getHeight());

    else
        Gdx.app.log("renderer:", "Sem by se rizeni nemelo dostat.");
}

Triedu uložíme. Tým máme pre dnešok hotovo. Pokiaľ si nie sme čímkoľvek istí, čerpáme zo zdrojového kódu, ktorý je ako vždy vrátane assets nižšie priložený k stiahnutiu. Aplikáciu spustíme, animácie eatLeftAnime aj eatRightAnime fungujú a tiež obaja zvuky sú v poriadku prehrajú.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 25x (6.39 MB)
Aplikácia je vrátane zdrojových kódov v jazyku Java

 

Predchádzajúci článok
Programujeme Android hru - Collision detection
Všetky články v sekcii
Programujeme Android hru
Preskočiť článok
(neodporúčame)
Programujeme Android hru - Energia kurčaťa
Článok pre vás napísal Jaroslav Polívka
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje převážně jazykům JAVA a C++
Aktivity