Lekce 7 - Programujeme Android hru - Rozdělení hry do tříd I
Z minulé lekce kurzu o programování hry pro Android v Javě, Programujeme Android hru - Úvaha nad obrazovkami, máme vytvořené prázdné třídy.
Dnes a příště do nich budeme doplňovat. Nejprve si však ještě v balíčku com.wackychicken.managers vytvoříme novou třídu TurnManager.java, která se nám bude starat nejen o pohyb a natáčení slepice právě tím směrem, kterým půjde. Třídu zatím ponecháme od Eclipse ve výchozím stavu, tzn. prázdnou a s prázdným konstruktorem.
Nyní již pojďme psát kód do třídy, která bude reprezentovat naší hloupou slepici. Otevřeme si třídu Chicken.java a vyplníme následujícím kódem:
public class Chicken { private float wholeSpeed,dividedSpeedX,dividedSpeedY,positionX,positionY,distanceX,distanceY; private final float WIDTH=115,HEIGHT=90; private final int BEGINYCONSTANT; private final Vector2 SCREEN_BOUND_BEGIN,SCREEN_BOUND_END,DEMANDED_SCREEN; private boolean xPositionAchieved,yPositionAchieved; public Chicken(final Vector2 screenBoundBegin, final Vector2 screenBoundEnd, final Vector2 demandedScreen, final int BEGINYCONSTANT) { this.wholeSpeed = 50; this.dividedSpeedX=dividedSpeedY=0; this.xPositionAchieved=this.yPositionAchieved=true; this.positionX=screenBoundBegin.x+200; this.positionY=screenBoundBegin.y+BEGINYCONSTANT+(demandedScreen.y-BEGINYCONSTANT)/2 -(HEIGHT/2); this.BEGINYCONSTANT=BEGINYCONSTANT; this.SCREEN_BOUND_BEGIN=screenBoundBegin; this.SCREEN_BOUND_END=screenBoundEnd; this.DEMANDED_SCREEN=demandedScreen; } public void update(float delta) { checkBorders(); //druha kotva ohraniceni, kdyby byla spatne spocitana distance a kvuli //BEGINYCONSTANT if (!xPositionAchieved) { if (distanceX<0) { // kdyz je distance zaporna, tak dividedSpeed take positionX+= dividedSpeedX *delta; distanceX+=((-1)*dividedSpeedX *delta); if (dis anceX>0) xPositionAchieved=true; } if distanceX>0) { positionX+= dividedSpeedX *delta; distanceX+=((-1)*dividedSpeedX *delta); if(distanceX<0)xPositionAchieved=true; } } if (!yPositi nAchieve ) { if ( istanceY<0) { positionY+= dividedSpeedY *delta; distanceY+=((-1)*dividedSpeedY *delta); if(distanceY>0)yPositionAchieved=true; } if(distanceY>0) { positionY+= dividedSpeedY*delta; distanceY+=((-1)* dividedSp edY *delta); if (distanceY<0) yPositionAchieved=true; } } if(isStanding()) resetWholeSpeed(); } // konec update public float getWholeSpeed() { return wholeSpeed; } public void incrWholeSpeed(float speed) { this.wholeSpeed+=speed; } public void resetWholeSpeed() { this.wholeSpeed=50; } public void setXPositionAchieved(boolean achieved) { xPositionAchieved=achieved; } public void setYPositionAchieved(boolean achieved) { yPositionAchieved=achieved; } public boolean isStanding() { return (xPositionAchieved&&yPositionAchieved); } private void checkBorders() { if(positionX<=SCREEN_BOUND_BEGIN.x) { positionX=SCREEN_BOUND_BEGIN.x; } if(positionX+WIDTH>=SCREEN_BOUND_END.x) { positionX=SCREEN_BOUND_END.x-WIDTH; } if(positionY+HEIGHT<SCREEN_BOUND_BEGIN.y+BEGINYCONSTANT) { positionY=SCREEN_BOUND_BEGIN.y+BEGINYCONSTANT-HEIGHT; } if(positionY+HEIGHT>SCREEN_BOUND_END.y) { positionY=SCREEN_BOUND_END.y-HEIGHT; } } public float getPositionX() { return positionX; } public float getPositionY() { return positionY; } public float getWidth() { return WIDTH; } public float getHeight() { return HEIGHT; } public float getDistanceX() { return distanceX; } public float getDistanceY() { return distanceY; } public void setDistanceX(float x) { distanceX=x; } public void setDistanceY(float y) { distanceY=y; } public void setDividedSpeedX(float X) { dividedSpeedX = X; } public void setDividedSpeedY(float Y) { dividedSpeedY = Y; } }
Přidáme importy a uložíme. Třídu stručně popíši, o všech proměnných nebo konstantách se nebudu zmiňovat, protože jejich názvem je naznačeno, k čemu jsou určeny. Slepice bude mít nějakou rychlost, kterou se bude pohybovat po obrazovce (wholespeed). Aby chodila po obou osách x a y rovnoměrně, je nutné tuto celkovou rychlost rozložit (dividedSpeedX, dividedSpeedY) dle poměru vzdáleností os, které má naše hloupé kuře urazit. BEGINYCONSTANT je vytyčení hranice na y souřadnici, aby slepice chodila jen po trávě. Konstanty vektorů SCREEN_BOUND_BEGIN,SCREEN_BOUND_END,DEMANDED_SCREEN již známe a slouží k vytyčení virtuální obrazovky. Pravdivostní proměnné xPositionAchieved a yPositionAchieved uchovávají informaci o tom, zda slepice dosáhla požadovaných souřadnic. Metoda update() přičítá do proměnných positionX a Y a tím dává kuře na obrazovce do pohybu. Metodu checkBorders() jsem popsal v kódu komentářem. Nakonec máme přístupové metody (getters and setters) pro získání nebo nastavení hodnot proměnných.
Otevřeme naší prázdnou třídu TurnManager.java, kterou jsme před chvílí vytvořili a vyplníme jí kódem:
public class TurnManager { private Chicken chicken; private final Vector2 screenBoundEnd; private final Vector2 screenBoundBegin; private final int BEGINYCONSTANT; public TurnManager(GameScreen gameScreen,final int BEGINYCONSTANT) { final Vector2 demandedScreen=gameScreen.DEMANDED_SCREEN; this.screenBoundBegin=gameScreen.SCREEN_BOUND_BEGIN; this.screenBoundEnd=gameScreen.SCREEN_BOUND_END; this.BEGINYCONSTANT=BEGINYCONSTANT; this.chicken = new Chicken(screenBoundBegin,screenBoundEnd,demandedScreen,BEGINYCONSTANT); } public void setChickenDistance(int x, int y) { int clickedPositionX=x,clickedPositionY=y; float distanceX,distanceY; float chickenTailX = chicken.getPositionX() + chicken.getWidth(); //pozice chicken.x a y jsou floaty // je do nich pricitana delta if (Math.abs(clickedPositionX - chicken.getPositionX()) <= Math.abs(clickedPositionX - chickenTailX)) distanceX=clickedPositionX - chicken.getPositionX(); else distanceX=clickedPositionX - chickenTailX; if(distanceX < screenBoundBegin.x-chicken.getPositionX()) distanceX = screenBoundBegin.x-chicken.getPositionX(); if(distanceX > screenBoundEnd.x- chickenTailX) distanceX=screenBoundEnd.x- chickenTailX; distanceY=clickedPositionY-(chicken.getPositionY()+chicken.getHeight()); if(distanceY < (screenBoundBegin.y+BEGINYCONSTANT)-(chicken.getPositionY()+ chicken.getHeight())) distanceY=(screenBoundBegin.y+BEGINYCONSTANT)-(chicken.getPositionY()+ chicken.getHeight()); if(distanceY > (screenBoundEnd.y-(chicken.getPositionY()+chicken.getHeight())) ) distanceY = screenBoundEnd.y-(chicken.getPositionY()+chicken.getHeight()); if(Math.abs(distanceX)<0.5f) { if(distanceX >=0) distanceX=0.5f; else distanceX=-0.5f; } if(Math.abs(distanceY)<0.5f) { if(distanceY >=0) distanceY=0.5f; else distanceY=-0.5f; } chicken.setDistanceX(distanceX); chicken.setDistanceY(distanceY); } public void setChickenSpeed() { float speedRatio = chicken.getWholeSpeed() / (Math.abs(chicken.getDistanceX()) +Math.abs(chicken.getDistanceY())); chicken.setDividedSpeedX(speedRatio*chicken.getDistanceX()); chicken.setDividedSpeedY(speedRatio*chicken.getDistanceY()); } public Chicken getChicken() { // kvuli objectManager return chicken; } }
Přidáme importy a uložíme. Vyběhnou na nás tři chyby, že vektory obrazovky ve třídě GameScreen.java nejsou viditelné:

Otevřeme si tedy tuto třídu a změníme v ní řádek z:
private final Vector2 DEMANDED_SCREEN,SCREEN_BOUND_BEGIN,SCREEN_BOUND_END;
na:
public final Vector2 DEMANDED_SCREEN,SCREEN_BOUND_BEGIN,SCREEN_BOUND_END;
a změnu uložíme. Zatím neřešíme natáčení slepice do požadovaného směru, metoda setChickenDistance() vypočítává vzdálenost mezi aktuálními souřadnicemi slepice a souřadnicemi dotyku uživatele na obrazovce. Do výpočtu se musí zohlednit, kam uživatel klikl vůči aktuální pozici slepice, tedy zda se (ne)budou do vzdálenosti přičítat i její rozměry. Metodu setChickenSpeed() jsem již popsal výše, když jsem popisoval rozdělení celé rychlosti v proměnné wholeSpeed do proměnných dividedSpeedX a dividedSpeedY.
Pro dnešek je to vše.
Pokračovat budeme příště, Programujeme Android hru - Rozdělení hry do tříd II.