17. díl - Programujeme Android hru - Jednoduchá herní smyčka

Java Android Programujeme hru Programujeme Android hru - Jednoduchá herní smyčka

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

Vítejte u další lekce. Minule jsme se věnovali energii kuřete, tedy hlavní postavy naší hry. Na úvod dnešní lekce se vás pokusím motivovat prohlášením, že v dnešním díle píšeme kód před nahráním naší hry do mobilu/tabletu naposledy.

Pojďme provést slíbené rozvětvení kódu dle herního stavu pomocí konstrukce switch. Otevřeme třídu GameManager.java, kde k deklaracím atributů přidáme další:

public enum GameState {READY, RUNNING, GAMEOVER}
private GameState gameState;
private Food food;
private OverlapsManager overlapsMng;
private float delayer;
private int score[];

Do konstruktoru přidáme jejich inicializace:

this.gameState = GameState.READY;
this.food = objectManager.getFood();
this.overlapsMng = objectManager.getOverlapsManager();
this.delayer = 4;
this.score = overlapsMng.getScore();

Stávající metodu update(...) změníme do následující podoby:

public void update(float delta) {
        switch (gameState) {
                case READY:
                        overlapsMng.scoreRestart();
                        this.delayer = 4;
                        break; // score vynuluj až po odchodu do READY
                case RUNNING:
                        updateRunning(delta);
                        break;
                case GAMEOVER:
                        updateGameOver(delta);
                        break;
                default: break;
        }
}

Do třídy přidáme další metody, umístíme je třeba pod naší nově upravenou metodu update(...):

private void updateRunning(float delta) {
        if (delta > 0.1f) {
                delta = 0.1f; // delta cap for old machines
        }
        if(chicken.getEnergy() <= 0) {  // kuře chcíplo
                gameState = GameState.GAMEOVER;
                chicken.restart();
                food.restart();
                if (score[0] > AssetManager.getHighScore()) {
                        AssetManager.setHighScore(score[0]);
                }
        }
        objectManager.update(delta);
}

private void updateGameOver(float delta) { // zpoždění z GAMEOVER state na READY
        if (delayer - delta < 0) {
                delayer=-0.1f;
        } else {
                delayer-=delta;
        }
}

public GameState getGameState() { // kvůli render
        return gameState;
}

public void setGameState(GameState gameState) {
        this.gameState=gameState;
}

public float getDelayer() {
        return this.delayer;
}

Stávající metoda:

public Chicken getChicken() { // kvůli render
        return chicken;
}

se jeví již jako zbytečná, proto ji můžeme smazat. Přidáme importy a třídu uložíme. V metodě update(...) pomocí switch větvíme kód do tří částí podle enum konstanty GameState. Zbývá nám pouze zajistit postupné přepínání konstanty a realizovat promítání podle její aktuální hodnoty (READY, RUNNING nebo GAMEOVER).

Začneme přípravou promítačky. Otevřeme třídu Renderer.java a například pod privátní metodu drawTextScore() si přidáme metodu další:

private void drawTextHighScore() {
        shadowFont.draw(batcher, "HighScore: "+AssetManager.getHighScore(), screenBoundBegin.x+demandedScreen.x*0.45f+2,screenBoundBegin.y+5+2);
        yellowFont.draw(batcher, "HighScore: "+AssetManager.getHighScore(), screenBoundBegin.x+demandedScreen.x*0.45f,screenBoundBegin.y+5);
}

Další dvě metody si přidáme například ihned pod stávající metodu render(...):

private void drawReady() {
        shadowFont.getData().setScale(1);
        yellowFont.getData().setScale(1);
        shadowFont.draw(batcher, "Wacky chicken", screenBoundBegin.x+240+2,screenBoundBegin.y+BEGINYCONSTANT+2 );
        yellowFont.draw(batcher, "Wacky chicken", screenBoundBegin.x+240,screenBoundBegin.y+BEGINYCONSTANT );
        shadowFont.draw(batcher, "Touch to play", screenBoundBegin.x+255+2,screenBoundBegin.y+BEGINYCONSTANT+55+2);
        yellowFont.draw(batcher, "Touch to play", screenBoundBegin.x+255,screenBoundBegin.y+BEGINYCONSTANT+55);
        shadowFont.getData().setScale(0.5f);
        yellowFont.getData().setScale(0.5f);
}

private void drawGameOver() {
        shadowFont.getData().setScale(1.5f);
        yellowFont.getData().setScale(1.5f);
        shadowFont.draw(batcher, "GameOver", screenBoundBegin.x+243+4,screenBoundBegin.y+BEGINYCONSTANT+4);
        yellowFont.draw(batcher, "GameOver", screenBoundBegin.x+243,screenBoundBegin.y+BEGINYCONSTANT);
        // zpozdeni ze state RUNNING na READY
        if (gameMng.getDelayer() > 0) {
                shadowFont.draw(batcher,""+(int)(1+gameMng.getDelayer()), screenBoundBegin.x+380+4,screenBoundBegin.y+BEGINYCONSTANT+80+4);
                yellowFont.draw(batcher,""+(int)(1+gameMng.getDelayer()), screenBoundBegin.x+380,screenBoundBegin.y+BEGINYCONSTANT+80);
        } else {
                shadowFont.draw(batcher,"Touch", screenBoundBegin.x+305+4,screenBoundBegin.y+BEGINYCONSTANT+80+4);
                yellowFont.draw(batcher,"Touch", screenBoundBegin.x+305,screenBoundBegin.y+BEGINYCONSTANT+80);
        }
        shadowFont.getData().setScale(0.5f);
        yellowFont.getData().setScale(0.5f);
}

A do stávající metody render(...) implementujeme větvení pro promítání dle enum konstanty GameState. Mohli bychom to klidně opět provést pomocí switch, my si zde ale dáme klasické if/else if. Metoda render(...) bude mít výsledný tvar:

public void render(float delta) {
        Gdx.gl.glClearColor(0, 0, 0, 1); // black background reduce flashing
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

        batcher.begin();
        batcher.draw(rBackground,screenBoundBegin.x, screenBoundBegin.y, demandedScreen.x, demandedScreen.y); // pozadi
        batcher.end();

        if (gameMng.getGameState() == GameState.RUNNING) {
                batcher.begin();
                drawTextScore();
                drawTextSpeed();
                drawTextFood();
                drawFood();
                drawChicken(delta);
                batcher.end();
                drawEnergyBar();
        }
        else if (gameMng.getGameState() == GameState.READY) {
                batcher.begin();
                drawReady();
                batcher.end();
        }
        else if (gameMng.getGameState() == GameState.GAMEOVER) {
                batcher.begin();
                drawTextScore();
                drawTextHighScore();
                drawGameOver();
                batcher.end();
        } else
                Gdx.app.log("renderer:", "Sem by se rizeni nemelo dostat.");
}

Přidáme importy, třídu uložíme a můžeme opustit. Posledním úkolem je zajistit správné přepínání enum konstanty GameState, podle které máme nyní rozvětvené updatování a promítání naší hry. To bude velmi jednoduchá záležitost, kterou nám vyřeší tři if podmínky. Otevřeme si naší třídu InputManager.java a její funkci touchDown(...) přepíšeme na následující tvar:

@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        screenX = scaleX(screenX);
        screenY = scaleY(screenY);

        // Gdx.app.log("Inputmng touchDown x:", "" + screenX);
        // Gdx.app.log("Inputmng touchDown y:", "" + screenY + "\n");

        if (gameManager.getGameState() == GameState.RUNNING) { // reaguj na stisk jen když je stav RUNNING
                objectManager.receivePosition(screenX, screenY);
        }
        if (gameManager.getGameState() == GameState.READY) {
                gameManager.setGameState(GameState.RUNNING);
        }
        if ((gameManager.getGameState() == GameState.GAMEOVER) && (gameManager.getDelayer() <= 0)) {
                gameManager.setGameState(GameState.READY);
        }
        return false;
}

Přidáme importy a vidíme, že nám zde svítí jeden warning s nevyužitým atributem gameScreen. Provedeme jeho odstranění z deklarace atributů, smažeme tedy řádek:

private GameScreen gameScreen;

V konstruktoru odstraníme také jeho inicializaci:

this.gameScreen = gameScreen;

Atribut odstraníme také z parametrů konstruktoru, výsledný tvar hlavičky konstruktoru tedy bude:

public InputManager(GameManager gameManager, float scaleRatioX, float scaleRatioY)

Tím způsobíme další warning :) s nevyužitým importem, tento drobný problém vyřešíme smazáním řádku:

import com.wackychicken.screens.GameScreen;

nebo naší známou klávesovou zkratkou pro přidání importů, jistě jste si již dávno všimli, že funguje i pro jejich odebrání, pokud jsou nevyužité. Třídu uložíme a můžeme zavřít. Odebráním parametru GameScreen gameScreen z konstruktoru se vyvolá další chyba ve třídě GameScreen.java :), kterou rychle vyřešíme. Otevřeme třídu GameScreen.java a její řádek:

InputManager inputManager = new InputManager(gameManager, this, w/orthoWidth, h/orthoHeight);

Zkrátíme na:

InputManager inputManager = new InputManager(gameManager, w/orthoWidth, h/orthoHeight);

Třídu uložíme a taktéž můžeme zavřít. Omlouvám se za komplikace, tento odkaz na gameScreen mi tam omylem zůstal, protože jsem ho v nějaké své mezi-verzi hry potřeboval pro implementaci dalších doplňků.

Aplikaci můžeme spustit a vyzkoušet, zda nám funguje herní smyčka a také ukládání high score. Po spuštění ještě hledáme, zda někde v Eclipse nesvítí nějaké warnings nebo problems, snad koukám dobře - žádné nevidím:

Bez problému

V krátkém videu níže vidíme, že ukládání, načítání a zobrazovaní high score funguje. Herní smyčka je rovněž v pořádku. Tím jsme s naší aplikací hotovi a příště provedeme její nahrání do mobilu.

Děkuji vám za přečtení, zdrojový kód je jako vždy přiložen ke stažení.


 

Stáhnout

Staženo 14x (6.27 MB)
Aplikace je včetně zdrojových kódů v jazyce Java

 

 

Článek pro vás napsal Jaroslav Polívka
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor se věnuje převážně jazykům JAVA a C++
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!