Základy naší hry máme už za sebou. V tomto článku přidáme na scénu další objekty a rozhýbeme je. Dále přidáme ovládání uživatelem, takže výsledkem už bude opravdová hra, byť celkem jednoduchá.

Ukázka hry

Třídy aplikace

Zatím se hra skládá ze tříd GameMIDlet, GameCanvas a GameSprite. Třídu GameCanvas ještě rozšíříme a přidáme novou třídu:

  • GameTask – tato třída je zodpovědná za vznik a pohyb malých objektů (mince, bomby, loď) a kontrolu kolizí mezi nimi.

Začneme rozšířením třídy GameCanvas.

Odchyt událostí generovaných uživatelem

K odchytu událostí generovaných uživatelem slouží ve třídě Canvas několik metod. Rozhraní počítá s možností ovládat zařízení klávesnicí nebo stylusem.

Události kláves

Každá klávesa, která při stisku generuje událost, má přiřazen nějaký kód, který se předá metodě odpovídající oné události:

  • keyPressed(int keyCode) – stisk klávesy
  • keyReleased(int keyCode) – uvolnění klávesy
  • keyRepeated(int keyCode) – automatický opakovaný stisk klávesy (autorepeat), zda jej zařízení podporuje se zjistí metodou hasRepeatEvents()

Standardní sada kláves má kódy kláves definované konstantami třídy Canvas. Jsou to klávesy s čísly 1 – 9 (KEY_NUM1, KEY_NUM2, …, KEY_NUM9), hvězdička (KEY_STAR) a mříže (KEY_POUND). Ostatní klávesy mohou nebo nemusí generovat události uživatele, je to zcela na libovůli implementátora specifikace, takže je jednodušší s nimi nepočítat.

Pro aplikace, které potřebují používat šipky, a pro hry byly zavedeny ještě herní akce, které jsou namapovány na kódy kláves. Platí, že jedné klávese může odpovídat maximálně jedna herní akce, ale tatáž herní akce může být namapována na více kláves. Například má-li zařízení speciální klávesy se šipkami, může být herní akce LEFT namapována na šipku doleva a zároveň na klávesu KEY_NUM4. Herní akce jsou následující: UP, DOWN, LEFT, RIGHT, FIRE, GAME_A, GAME_B, GAME_C a GAME_D. Ke kódu klávesy se odpovídající herní akce získá metodou getGameAction(int keyCode).

Události stylusu

Zařízení, která nemají klávesnici, se ovládají obvykle stylusem (ukazovátko, kterým se uživatel dotýká displeje). Metody volané k obsluze událostí stylusu tu jen pro pořádek vyjmenuji a dál se jim nebudu věnovat, protože většina zařízení s MIDP profilem se ovládá klávesami. Podporu událostí stylusu lze v programu ověřit metodou hasPointerEvents().

  • pointerDragged(int x, int y) – stylus byl táhnut na souřadnice [x, y]
  • pointerPressed(int x, int y) – stylus byl přitisknut na souřadnicích [x, y]
  • pointerReleased(int x, int y) – stylus byl uvolněn na souřadnicích [x, y]

Události v GameCanvas

V naší hře při stisku klávesy pouze nastavíme velikost relativního posunu lodi podle toho, zda uživatel zmáčkl klávesu odpovídající herní akci LEFT (doleva) či RIGHT (doprava), a nastavíme dále na true parametr určující, zda uživatel klávesu drží (tedy loď se má pohybovat).

protected void keyPressed(int key) {
   /* zjištění herní akce odpovídající kódu stisknuté klávesy */
   int action = getGameAction(key);
   switch(action){
      case Canvas.LEFT: // pohyb lodí doleva
         dx = -4;
         move = true;
         break;
      case Canvas.RIGHT: // pohyb lodí doprava
         dx = 4;
         move = true;
         break;
   }
}

Při uvolnění klávesy loď opět zastavíme.

public void keyReleased(int keyCode) {
   /* zjištění herní akce odpovídající kódu stisknuté klávesy */
   int action = this.getGameAction(keyCode);
   if (action == Canvas.LEFT || action == Canvas.RIGHT) {
      // ukončení pohybu lodi
      move = false;
   }
}

Modelů pohybu objektu ovládaného uživatelem samozřejmě existuje několik. Mohli bychom lodí pohybovat daným směrem až do té doby, než uživatel stiskne klávesu odpovídající jinému směru (takto bývá ovládán had v populární hře, kde musí uživatel trefit hadem postupně všechna čísla a nenarazit do překážky), nebo bychom lodí mohli pohybovat krokově – při každém stisku klávesy by se loď posunula o kus daným směrem. Tento způsob pohybu se může hodit například do her odehrávajících se na mřížovém plánu složeném z jednotlivých polí. Pro každou hru je potřeba při jejím návrhu vybrat nejvhodnější z možných modelů, aby byla co nejzábavnější a nejhratelnější.

Události zobrazení třídy Canvas na displeji

Ze třídy Canvas nás budou zajímat ještě dvě metody. Metoda showNotify(), která je volána pokaždé těsně před nakreslením instance třídy Canvas na displej, ještě před metodou paint(), a metoda hideNotify(), kterou aplikační manažer zavolá, když je instance třídy Canvas překreslena na displeji jinou komponentou. Jejich obsah však nechám na později, protože úzce souvisí s další třídou naší aplikace.

GameTask

S plánováním nějaké akce na konkrétní čas nebo s jejím periodickým opakováním se v MIDP profilu také počítá. Používají se k tomu třídy java.util.Timer (časovač) a java.util.TimerTask. Nejprve musíme rozšířit abstraktní třídu TimerTask a v její metodě run() naprogramovat úkol, který pak pomocí třídy Timer naplánujeme k periodickému opakování. Jelikož v metodě run() budeme používat mnoho proměnných instance třídy GameCanvas, bude nejjednodušší, když třída GameTask rozšiřující třídu TimerTask bude vnitřní třídou třídy GameCanvas.

public class GameCanvas extends Canvas implements CommandListener {
   /* vnitřní třída která může používat proměnné
   instance třídy GameCanvas */

   class GameTask extends TimerTask {
      public void run() {
         /* pohyb lodí */
         /* pohyb mincemi a bombami a kontrola
         jejich kolize s lodí */

         /* vygenerování další padající věci */
         repaint();
         serviceRepaints();
      }
   }
}

Akce prováděné v metodě run() zde jen zběžně popíšu:

  • Pohyb lodi – má-li proměnná move hodnotu true (uživatel drží šipku doleva či doprava), pohneme lodí a následně zkontrolujeme, zda je loď stále celá na displeji. Není-li, nastavíme její souřadnice tak, aby byla u okraje, který přesáhla (nedovolíme lodi opustit viditelnou plochu).
  • Pohyb padající mince – posuneme mincí a zkontrolujeme, zda nenastala její kolize s lodí a zda je ještě stále na displeji. Pokud nastala kolize mince s lodí, zvýšíme skóre o hodnotu mince a minci odstraníme. Pokud je mince mimo displej, minci odstraníme.
  • Pohyb bomby – liší se od pohybu mince pouze řešením kolize s lodí. Nastane-li kolize bomby s lodí, ukončíme běh hry, zrušíme všechny padající mince a bomby a místo lodi zobrazíme explozi.
  • Vygenerování další věci – jako proměnné máme minimální a maximální počet period, za které vygenerujeme další věc. Náhodně z tohoto intervalu určíme, za jak dlouho vygenerujeme další věc. Pak náhodně určíme, jakou věc vygenerujeme nyní a na jaké x-ové souřadnici se tato věc objeví.

Když máme hotovou třídu, která provádí vše, co je potřeba periodicky opakovat, je už velmi jednoduché toto opakování nastavit:

Timer timer = new Timer();
TimerTask task = new GameTask();
timer.schedule(task, 0, 80);

Druhým paramterem metody schedule() je údaj, za kolik milisekund se má úkol poprvé provést, a třetím parametrem je perioda opakování úkolu, také v milisekundách. Instance třídy Timer může mít naplánováno více úkolů. Používá k tomu jedno vlákno běžící na pozadí aplikace. Chceme-li zrušit provádění pouze jednoho úkolu, uděláme to metodou cancel() instance příslušné třídy TimerTask. Zrušený úkol už nejde znovu použít, musí se vytvořit nový úkol. Provádění všech úkolů časovače zrušíme metodou cancel() tohoto časovače, který stejně jako zrušený úkol už nejde dál použít, při pokusu o použití vyhodí výjimku java.lang.IllegalStateException.

Teď už můžeme napsat metody showNotify() a hideNotify() třídy GameCanvas. Při jejím zobrazení chceme spustit běh hry a při jejím překreslení jinou komponentou zase běh hry přerušíme.

Timer timer;
TimerTask task;
// tento kanvas je odstraněn z displeje zařízení
protected void hideNotify(){
   // pozastavení běhu hry
   if(timer!=null){
      timer.cancel();
      timer = null;
      task = null;
      }
}
// tento kanvas bude vykreslen na displej zařízení
protected void showNotify() {
   // obnovení běhu hry
   if(timer==null && gameOver==false){
      task = new GameTask();
      timer = new Timer();
      timer.schedule(task, 0, tick);
   }
}

Vyladění hratelnosti hry

Nyní máme hotové torzo hry, které obsahuje spoustu parametrů určujících průběh hry. Jsou to typy padajících předmětů, intervaly jejich generování, rychlost a model pohybu loďky a tak podobně. Na správném nastavení všech těchto parametrů závisí, zda bude nebo nebude hra zábavná. Během hry se třeba mohou přidávat nové typy předmětů, může se zrychlovat jejich padání, zvyšovat frekvence generování nových předmětů, některé nové předměty mohou měnit dočasně vlastnosti hry – loď bude rychlejší, pomalejší, hrany displeje budou cyklicky propustné – lze si vymyslet nepřeberné množství variací.

Na závěr vás dnes tedy do praktického života vybavím poznáním, že z celkového času vývoje hry zabere 10 % programování a 90 % „závěrečné“ vylaďování.

GAME OVER

Ke stažení

Veškeré zde uvedené zdrojové kódy, obrázky ke hře a hotovou aplikaci si můžete stáhnout a použít pro vlastní potřebu.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Žádný příspěvek v diskuzi

Odpovědět