14. diel - Programujeme Android hru - Collision detection
V minulej lekcii programovanie hry pre Android sme sa venovali kŕmenie. Dnes budeme zisťovať možnú kolíziu nášho kurčaťa s kŕmením a ak zistíme, že k tejto kolízii došlo, zvýšime herné skóre o jednotku. Skóre a niektoré ďalšie údaje budeme chcieť samozrejme premietnuť na obrazovku, na tento účel si do assets nahráme svojej vlastné písmo.
Otvoríme si triedu AssetManager.java a doplníme k deklaráciám premenných:
public static BitmapFont yellowFont, shadowFont;
Do metódy load () pridáme:
yellowFont = new BitmapFont(Gdx.files.internal("yellowfont.fnt"),true); yellowFont.getData().setScale(0.5f, 0.5f); shadowFont = new BitmapFont(Gdx.files.internal("blackfont.fnt"),true); shadowFont.getData().setScale(0.5f, 0.5f);
Ďalej do triedy pridáme metódu dispose (), ktorá slúži na uvoľnenie pamäti po našich zdrojoch:
public static void dispose() { Gdx.app.log("AssetMng ", "dispose()"); background.dispose(); carrot.dispose(); autumn.dispose(); corn.dispose(); wheat.dispose(); yellowFont.dispose(); shadowFont.dispose(); left1.dispose(); left2.dispose(); leftSideDown1.dispose(); leftSideDown2.dispose(); leftSideUp1.dispose(); leftSideUp2.dispose(); right1.dispose(); right2.dispose(); rightSideDown1.dispose(); rightSideDown2.dispose(); rightSideUp1.dispose(); rightSideUp2.dispose(); standLeft12.dispose(); standRight12.dispose(); }
Pridáme importy a uložíme. Nezabudneme naimportovať súbory písma a jeho tieňa do zložky assets, to už vieme. Metódu dispose () musíme ešte zavolať, otvoríme si triedu WackyChicken.java a tu na koniec funkcie dispose () pridáme riadok:
AssetManager.dispose();
Už sme si zvykli, že už vypisujem iba zmeny (prírastky) kódu, na celý výpis tu nemáme priestor, všetko nájdeme v kompletnom zdrojovom kóde, ktorý je ako obvykle nižšie k stiahnutiu.
Ohľadom písma to je všetko a nič nám nebráni v jeho použití, budeme ho potrebovať neskôr pri implementácii premietaní skóre. Teraz poďme riešiť kolízii.
V balíčku com.wackychicken.managers si vytvoríme novú triedu OverlapsManager.java a vyplníme ju do nasledujúceho tvaru:
public class OverlapsManager { private Food food; private Chicken chicken; private int []score; public OverlapsManager(Food food) { this.food=food; this.chicken=food.getChicken(); this.score=new int[1]; this.score[0]=0; } public void update(float delta) { // trida hlida zda instance food a chicken se protinaji! if (Intersector.overlaps(food.getRectForOverlap(),chicken.getRectForOverlap()) && food.getWasClicked() && (!food.getWasCounted()) && chicken.isStanding() ) { score[0]++; food.setWasCounted(true); food.setLifeTime(0); } } public void clickedPosition(int x, int y) { // souradnice kliku z inputMng! if(food.getRectForOverlap().contains(x, y)) { food.setWasClicked(true); chicken.incrWholeSpeed(10); // vzdy kdyz tapneme jidlo, zvysit rychlost } else { // zvysit rychost ale chceme i kdyz se neklika primo na zradlo if( (!chicken.isStanding() && chicken.getStandingState()==StandingState.STANDLEFT && x<chicken.getPositionX()) || (!chicken.isStanding() && chicken.getStandingState()==StandingState.STANDRIGHT && x>chicken.getPositionX()+chicken.getWidth()) ) { chicken.incrWholeSpeed(10); } } // nize vyresetovani rychlosti pri zmene smeru if (!chicken.isStanding() && chicken.getStandingState()==StandingState.STANDLEFT && x>chicken.getPositionX()+chicken.getWidth()) chicken.resetWholeSpeed(); if (!chicken.isStanding() && chicken.getStandingState()==StandingState.STANDRIGHT && x<chicken.getPositionX()) chicken.resetWholeSpeed(); } public int[] getScore() { return score; } public void scoreRestart() { this.score[0]=0; } }
Pridáme importy a uložíme. Eclipse na nás oprávnene vyhodí chybu, že v inštancii chicken nepozná prístupovú metódu getRectForOverlap (), nielenže tam nemáme túto prístupovú metódu, nemáme tam ani premennú, ku ktorej pristupovať chceme. Poďme to napraviť, otvoríme triedu Chicken.java, kde k deklaráciám premenných pridáme:
private Rectangle rectForOverlap;
Do konstruktoru pridáme:
this.rectForOverlap = new Rectangle(positionX,positionY,this.WIDTH,this.HEIGHT);
Na začiatok metódy update (...) doplníme:
rectForOverlap.set(positionX, positionY, this.WIDTH, this.HEIGHT);
Na koniec triedy pod existujúce metódy pridáme:
public Rectangle getRectForOverlap() { return rectForOverlap; }
Pridáme importy a uložíme. Čo nám zostáva ďalej? Založiť inštanciu našej triedy OverlapsManager.java, zavolať jej metódy update (...) a clickedPosition (...), otvoríme triedu ObjectManager.java, kde k deklaráciám premenných pridáme:
private OverlapsManager overlapsMng;
Na koniec konstruktoru pridáme:
this.overlapsMng=new OverlapsManager(food);
Na koniec funkcie update (...) pridáme:
overlapsMng.update(delta);
Na začiatok funkcie receivePosition (...) pridáme:
overlapsMng.clickedPosition(x,y);
Na koniec triedy ešte pridáme prístupovú metódu:
public OverlapsManager getOverlapsManager(){ return this.overlapsMng; }
Zmeny v triede ObjectManager.java uložíme, trieda je v rovnakom balíčku, preto importy nie sú vyžadované.
Teraz nám zostáva iba pomocou nášho novo naimportovaného písma premietnuť skóre, rýchlosť a "životnosť" krmiva na obrazovku.
Otvoríme triedu Renderer.java, kde k deklaráciám premenných pridáme:
private OverlapsManager overlapsMng; private BitmapFont yellowFont,shadowFont; private int score[];
Na konci funkcie initAssetsObjects () vykonáme inicializáciu vyššie uvedených premenných pridaním:
this.overlapsMng=gameMng.getObjectManager().getOverlapsManager(); this.score=overlapsMng.getScore(); this.yellowFont=AssetManager.yellowFont; this.shadowFont=AssetManager.shadowFont;
Ďalej do triedy Renderer.java pridáme 3 privátne metódy, ktorá umiestnime napríklad ihneď pod metódu render (...):
private void drawTextScore() { shadowFont.draw(batcher, "Score: "+score[0], screenBoundBegin.x+demandedScreen.x*0.22f+2,screenBoundBegin.y+5+2); yellowFont.draw(batcher, "Score: "+score[0], screenBoundBegin.x+demandedScreen.x*0.22f,screenBoundBegin.y+5); } private void drawTextSpeed() { shadowFont.draw(batcher, "Speed: "+(int)(chicken.getWholeSpeed()/10), screenBoundBegin.x+demandedScreen.x*0.45f+2,screenBoundBegin.y+5+2 ); yellowFont.draw(batcher, "Speed: "+(int)(chicken.getWholeSpeed()/10), screenBoundBegin.x+demandedScreen.x*0.45f,screenBoundBegin.y+5 ); } private void drawTextFood() { shadowFont.draw(batcher, "Food: "+(int)(1+food.getLifeTime()), screenBoundBegin.x+demandedScreen.x*0.7f+2,screenBoundBegin.y+5+2 ); yellowFont.draw(batcher, "Food: "+(int)(1+food.getLifeTime()),screenBoundBegin.x+demandedScreen.x*0.7f,screenBoundBegin.y+5 ); }
A už nám nezostáva nič iné, než vykonať volanie týchto metód, vykonáme to vo funkcii render (...) ihneď po premietnutí pozadia:
drawTextScore(); drawTextSpeed(); drawTextFood();
Pridáme importy, triedu uložíme a apk. spustíme.
Všetko nám funguje, sliepku môžeme postupne poháňať k vyššej rýchlosti. Pri kolízii dôjde k vyžrebovanie novej pozície krmiva a samozrejme k započítanie skóre. Rovnako "životnosť" krmiva je odčítanie. Nabudúce si v stručnosti vysvetlíme princíp činnosti a pridáme ďalšie fíčury.
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é 30x (6.18 MB)
Aplikácia je vrátane zdrojových kódov v jazyku Java