Lekce 14 - Programujeme Android hru - Collision detection
V minulé lekci programování hry pro Android, Programujeme Android hru - Krmení II, jsme se věnovali krmení.
Dnes budeme zjišťovat možnou kolizi našeho kuřete s krmením a pokud zjistíme, že k této kolizi došlo, zvýšíme herní skóre o jedničku. Skóre a některé další údaje budeme chtít samozřejmě promítnout na obrazovku, pro tento účel si do assets nahrajeme své vlastní písmo.
Otevřeme si třídu AssetManager.java a doplníme k deklaracím proměnných:
public static BitmapFont yellowFont, shadowFont;
Do metody load() přidá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);
Dále do třídy přidáme metodu dispose(), která slouží k uvolnění paměti po našich zdrojích:
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(); }
Přidáme importy a uložíme. Nezapomeneme naimportovat soubory písma a jeho stínu do složky assets, to již umíme. Metodu dispose() musíme ještě zavolat, otevřeme si třídu WackyChicken.java a zde na konec funkce dispose() přidáme řádek:
AssetManager.dispose();
Už jsme si zvykli, že již vypisuji pouze změny (přírůstky) kódu, na celý výpis tady nemáme prostor, vše najdeme v kompletním zdrojovém kódu, který je jako obvykle níže ke stažení.
Ohledně písma to je vše a nic nám nebrání v jeho použití, budeme ho potřebovat později při implementaci promítání skóre. Nyní pojďme řešit kolizi.
V balíčku com.wackychicken.managers si vytvoříme novou třídu OverlapsManager.java a vyplníme ji do následujícího 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; } }
Přidáme importy a uložíme. Eclipse na nás oprávněně vyhodí chybu, že v instanci chicken nezná přístupovou metodu getRectForOverlap(), nejenže tam nemáme tuto přístupovou metodu, nemáme tam ani proměnnou, ke které přistupovat chceme. Pojďme to napravit, otevřeme třídu Chicken.java, kde k deklaracím proměnných přidáme:
private Rectangle rectForOverlap;
Do konstruktoru přidáme:
this.rectForOverlap = new Rectangle(positionX,positionY,this.WIDTH,this.HEIGHT);
Na začátek metody update(...) doplníme:
rectForOverlap.set(positionX, positionY, this.WIDTH, this.HEIGHT);
Na konec třídy pod stávající metody přidáme:
public Rectangle getRectForOverlap() { return rectForOverlap; }
Přidáme importy a uložíme. Co nám zbývá dále? Založit instanci naší třídy OverlapsManager.java, zavolat její metody update(...) a clickedPosition(...), otevřeme třídu ObjectManager.java, kde k deklaracím proměnných přidáme:
private OverlapsManager overlapsMng;
Na konec konstruktoru přidáme:
this.overlapsMng=new OverlapsManager(food);
Na konec funkce update(...) přidáme:
overlapsMng.update(delta);
Na začátek funkce receivePosition(...) přidáme:
overlapsMng.clickedPosition(x,y);
Na konec třídy ještě přidáme přístupovou metodu:
public OverlapsManager getOverlapsManager(){ return this.overlapsMng; }
Změny v třídě ObjectManager.java uložíme, třída je ve stejném balíčku, proto importy nejsou vyžadovány.
Nyní nám zbývá pouze pomocí našeho nově naimportovaného písma promítnout skóre, rychlost a "životnost" krmiva na obrazovku.
Otevřeme třídu Renderer.java, kde k deklaracím proměnných přidáme:
private OverlapsManager overlapsMng; private BitmapFont yellowFont,shadowFont; private int score[];
Na konci funkce initAssetsObjects() provedeme inicializaci výše uvedených proměnných přidáním:
this.overlapsMng=gameMng.getObjectManager().getOverlapsManager(); this.score=overlapsMng.getScore(); this.yellowFont=AssetManager.yellowFont; this.shadowFont=AssetManager.shadowFont;
Dále do třídy Renderer.java přidáme 3 privátní metody, která umístíme například ihned pod metodu 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 již nám nezbývá nic jiného, než provést volání těchto metod, provedeme to ve funkci render(...) ihned po promítnutí pozadí:
drawTextScore(); drawTextSpeed(); drawTextFood();
Přidáme importy, třídu uložíme a apk. spustíme.

Vše nám funguje, slepici můžeme postupně popohánět k vyšší rychlosti. Při kolizi dojde k vylosování nové pozice krmiva a samozřejmě k započítání skóre. Rovněž "životnost" krmiva je odčítána.
Příště, Programujeme Android hru - Animace, zvuky, si ve stručnosti vysvětlíme princip činnosti a přidáme další fíčury.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 30x (6.18 MB)
Aplikace je včetně zdrojových kódů v jazyce Java