17. diel - Programujeme Android hru - Jednoduchá herné slučka
Vitajte u ďalšej lekcie. Minule sme sa venovali energiu kuraťa, teda hlavné postavy našej hry. Na úvod dnešnej lekcie sa vás pokúsim motivovať vyhlásením, že v dnešnom diele píšeme kód pred nahraním našej hry do mobilu / tabletu naposledy.
Poďme vykonať sľúbené rozvetvenie kódu podľa herného stavu pomocou konštrukcie switch. Otvoríme triedu GameManager.java, kde k deklaráciám atribútov pridáme ďalšie:
public enum GameState {READY, RUNNING, GAMEOVER} private GameState gameState; private Food food; private OverlapsManager overlapsMng; private float delayer; private int score[];
Do konstruktoru pridáme ich inicializácia:
this.gameState = GameState.READY; this.food = objectManager.getFood(); this.overlapsMng = objectManager.getOverlapsManager(); this.delayer = 4; this.score = overlapsMng.getScore();
Existujúce metódu update (...) zmeníme do nasledujúcej 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 triedy pridáme ďalšie metódy, umiestnime treba pod našou novo upravenú metódu 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; }
Existujúce metóda:
public Chicken getChicken() { // kvůli render return chicken; }
sa javí už ako zbytočná, preto ju môžeme zmazať. Pridáme importy a triedu uložíme. V metóde update (...) pomocou switch vetvám kód do troch častí podľa enum konštanty GameState. Zostáva nám len zabezpečiť postupné prepínanie konštanty a realizovať premietanie podľa jej aktuálnej hodnoty (READY, RUNNING alebo GameOver).
Začneme prípravou premietačky. Otvoríme triedu Renderer.java a napríklad pod privátne metódu drawTextScore () si pridáme metódu ďalšie:
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); }
Ďalšie dve metódy si pridáme napríklad ihneď pod existujúce metódu 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 existujúce metódy render (...) implementujeme vetvenia pre premietanie podľa enum konštanty GameState. Mohli by sme to pokojne opäť vykonať pomocou switch, my si tu ale dáme klasické if / else if. Metóda render (...) bude mať 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."); }
Pridáme importy, triedu uložíme a môžeme opustiť. Posledným úlohou je zabezpečiť správne prepínanie enum konštanty GameState, podľa ktorej máme teraz rozvetvené updatované a premietanie našej hry. To bude veľmi jednoduchá záležitosť, ktorú nám vyrieši tri if podmienky. Otvoríme si našej triedu InputManager.java a jej funkciu Touchdown (...) prepíšeme na nasledujúce 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; }
Pridáme importy a vidíme, že nám tu svieti jeden warning s nevyužitým atribútom gameScreen. Prevedieme jeho odstránenie z deklarácie atribútov, zmažeme teda riadok:
private GameScreen gameScreen;
V konstruktoru odstránime tiež jeho inicializácii:
this.gameScreen = gameScreen;
Atribút odstránime aj z parametrov konstruktoru, výsledný tvar hlavičky konstruktoru teda bude:
public InputManager(GameManager gameManager, float scaleRatioX, float scaleRatioY)
Tým spôsobíme ďalšie warning s nevyužitým importom, tento drobný problém vyriešime zmazaním riadku:
import com.wackychicken.screens.GameScreen;
alebo našej známu klávesovou skratkou pre pridanie importov, iste ste si
už dávno všimli, že funguje aj pre ich odobratie, ak sú nevyužité. Triedu
uložíme a môžeme zatvoriť. Odstránením parametra
GameScreen gameScreen
z konstruktoru sa vyvolá ďalšia chyba v
triede GameScreen.java ,
Ktorú rýchlo vyriešime. Otvoríme triedu GameScreen.java a jej riadok:
InputManager inputManager = new InputManager(gameManager, this, w/orthoWidth, h/orthoHeight);
Skrátime na:
InputManager inputManager = new InputManager(gameManager, w/orthoWidth, h/orthoHeight);
Triedu uložíme a taktiež môžeme zatvoriť. Ospravedlňujem sa za komplikácie, tento odkaz na gameScreen mi tam omylom ostal, pretože som ho v nejakej svojej medzi-verziu hry potreboval pre implementáciu ďalších doplnkov.
Aplikáciu môžeme spustiť a vyskúšať, či nám funguje herné slučka a tiež ukladanie high score. Po spustení ešte hľadáme, či niekde v Eclipse nesvieti nejakej warnings alebo problems, snáď koukám dobre - žiadne nevidím:
V krátkom videu nižšie vidíme, že ukladanie, načítanie a zobrazovanie high score funguje. Herný slučka je tiež v poriadku. Tým sme s našou aplikáciou hotoví a nabudúce prevedieme jej nahranie do mobilu.
Ďakujem vám za prečítanie, zdrojový kód je ako vždy priložený k stiahnutiu.
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é 46x (6.27 MB)
Aplikácia je vrátane zdrojových kódov v jazyku Java