Kdo si J2ME, nezlobí – základ hry
V tomto článku se už skutečně pustíme do programování. Začneme úplně minimálním základem, který pak budeme později rozšiřovat o různé vymoženosti. Nejprve však musíme aplikaci spustit a ukončit a mezi tím také něco nakreslit na displej.
Třídy aplikace
Hra se bude pro začátek skládat ze tří tříd:
GameMIDlet
– hlavní třída aplikace rozšiřující abstraktní třídujavax.microedition.midlet.MIDlet
.GameCanvas
– třída zodpovědná za vlastní hru.GameSprite
– pomocná třída sloužící k reprezentaci pohyblivých objektů. Implementuje rozhraní třídyjavax.microedition.lcdui.game.Layer
z herního API MIDP 2.0, ke kterému přidává navíc ještě metodu pro kontrolu vzájemných kolizí objektů dle vzoru třídyjavax.microedition.lcdui.game.Sprite
z téhož herního API. U telefonů, které implementují MIDP 2.0, se místo této třídy použije třídajavax.microedition.lcdui.game.Sprite
.
GameMIDlet
V našem případě bude hlavní třída velmi jednoduchá, protože píšeme minimalistickou aplikaci bez větší logiky. Jediné, co musíme udělat, je při spuštění aplikace vytvořit instanci třídy GameCanvas
a zobrazit ji na displeji.
protected void startApp() throws MIDletStateChangeException {
if(canvas==null){
// Spuštění aplikace
canvas = new GameCanvas(this);
Display.getDisplay(this).setCurrent(canvas);
} else {
// Aktivace aplikace po jejím pobytu v pasivním stavu, v našem případě zde není potřeba dělat nic.
}
}
Při ukončení aplikace v metodě destroyApp()
zatím také nemusíme dělat skoro nic. Pokud bychom však chtěli ukládat aktuální stav hry, patřila by tato akce sem.
protected void destroyApp(boolean arg0) {
/* Dáváme aplikačnímu manažeru najevo, že midlet přešel do zrušeného stavu */
notifyDestroyed();
}
Pokud jste v J2ME úplní začátečníci, doporučuji přečíst si článek J2ME v kostce – první midlet.
GameCanvas
Na displej lze kreslit komponenty, které implementují rozhraní javax.microedition.lcdui.Displayable
. Tyto zobrazitelné komponenty se dělí na základní dvě skupiny – vysokoúrovňové komponenty a nízkoúrovňové komponenty.
Vysokoúrovňové komponenty neumožňují odchytávání událostí klávesnice, ani kreslení jednodušších útvarů na konkrétní souřadnice. Hodí se například k zobrazování formulářů a seznamů položek (viz článek J2ME v kostce – kreslíme na displej). To je pro hru samozřejmě nedostatečné, takže třída GameCanvas
bude rozšiřovat třídu javax.microedition.lcdui.Canvas
, která jako jediná ve specifikaci MIDP 1.0 umožňuje nízkoúrovňovou grafiku. Specifikace MIDP 2.0 rozšiřuje nabídku nízkoúrovňových komponent o formulářovou položku CustomItem
, pomocí které je možné napsat i jednodušší hry, ale její popis nespadá do rámce tohoto článku.
Třída GameCanvas
je celkem komplexní. Musí vykreslovat hru, pravidelně aktualizovat polohu předmětů, reagovat na stisk kláves uživatelem a umožnit uživateli ukončit aplikaci.
Abstraktní příkaz
Po svém předku javax.microedition.lcdui.Canvas
třída GameCanvas
zdědila možnost ovládání pomocí abstraktních příkazů. Toto ovládání je společné se všemi zobrazitelnými komponentami. Praktické provedení je následující (ukázka z konstruktoru třídy GameCanvas
):
/* vytvoření nového příkazu, první parametr určuje jeho text, druhý jeho typ a třetí prioritu */
endCmd = new Command(„Konec“, Command.EXIT, 1);
/* přidání příkazu komponentě */
addCommand(endCmd);
/* nastavení posluchače událostí, který obsluhuje vyvolání příkazu uživatelem */
setCommandListener(this);
Umístění a způsob zobrazení abstraktních příkazů na displeji je zcela závislé na implementaci MIDP v telefonu. Obvyklé například bývá, že jeden příkaz se zobrazí na displeji dole k některému ze softkey (kontextová klávesa) a druhý softkey obsahuje rozbalovací menu se zbylými příkazy. Tento způsob zobrazování má tu nevýhodu, že aplikace nemá k dispozici celý displej, ale je ochuzená o pruh s abstraktními příkazy.
Obrázek ukazuje, jak abstraktní příkazy zobrazují telefony značky Nokia. Jsou-li příkazy maximálně dva, vejdou se všechny najednou na displej, je-li jich víc, vejde se na displej jeden a ostatní se zobrazí po stisknutí odpovídajícího softkey na zvláštní obrazovce.
Nokia – dva příkazy
Nokia – více příkazů
Nokia – menu
Telefony značky Siemens přítomnost abstraktních příkazů pouze naznačí šipkou v pravém dolním rohu displeje a po stisku odpovídajícího softkey teprve zobrazí všechny příkazy.
Siemens – naznačení přítomnosti příkazů
Siemens – menu
Jak si jistě pozorní čtenáři všimli, třída GameCanvas
implementuje rozhraní javax.microedition.lcdui.CommandListener
. Toto rozhraní obsahuje pouze metodu commandAction(Command command, Displayable displayable)
, která slouží k obsluze události vyvolání příkazu. Parametr command
obsahuje příkaz, který byl vyvolán, a parametr displayable
obsahuje zobrazitelnou komponentu, v níž událost nastala. Nás bude zajímat pouze parametr command
, protože v aplikaci zatím máme jen jednu zobrazitelnou komponentu. Kdybychom však měli aplikaci složenou z více grafických komponent, byl by například příkaz „Zpět“ typickou ukázkou příkazu, který obsahují skoro všechny obrazovky a jehož zpracování se liší v závislosti na obrazovce, kde událost nastala.
public void commandAction(Command command, Displayable displayable) {
if(command==endCmd){
midlet.destroyApp(true);
}
}
Kreslení na displej
Veškeré kreslení probíhá v metodě paint(Graphics graphics)
zděděné ze třídy javax.microedition.lcdui.Canvas
. Nelze předpokládat, že je stále zobrazené, co jsme nakreslili při předchozím volání této metody, je potřeba pokaždé nakreslit vše znova. Parametr graphics
se může používat pouze v rámci metody paint()
(a samozřejmě také v rámci metod volaných z této metody).
Při každém volání metody jsou nastaveny některé vlastnosti grafiky na výchozí hodnoty:
- aktuální barva je černá
- font je nastaven na výchozí font
- styl čáry je nepřerušovaný (
Graphics.SOLID
) - počátek souřadnic je v levém horním rohu displeje
Aplikační manažer zavolá metodu paint()
jenom v případě, že je Canvas
právě zobrazený na displeji. O překreslení displeje lze zažádat metodou repaint()
, která neblokuje běh aplikace (ukončí se nezávisle na volání metody paint()
). To však neznamená, že bude displej okamžitě překreslen, neboť jeho překreslení závisí na viditelnosti překreslované oblasti.
Pokud si chceme na překreslení displeje počkat a teprve poté pokračovat v běhu aplikace, musíme použít po volání metody repaint()
ještě metodu serviceRepaints()
. Tato metoda si vynutí obsluhu všech zatím neobsloužených požadavků na překreslení displeje a ukončí se teprve až po jejím provedení. Jedinou výjimkou je případ, kdy Canvas
není viditelný, v tom případě volání metody serviceRepaints()
neprovede nic. Při zobrazování nějaké animace se celkem hodí vykreslit všechny její kroky, takže budeme volat funkci repaint()
spolu s funkcí serviceRepaints()
.
V konstruktoru třídy GameCanvas
je také potřeba nahrát obrázky. V našem případě můžeme nahrát klidně všechny, včetně těch, které použijeme až při výbuchu lodi. Při tvorbě aplikací s větším počtem velkých obrázků se však pravidelně stává, že se všechny nevejdou telefonu do paměti. Pak je potřeba důsledně uvolňovat všechny zdroje v okamžiku, kdy nejsou potřeba, a nahrávat je až těsně před jejich použitím.
Obrázky v J2MEWTK dáme do adresáře J2MEWTK_HOME/Blabouch/res. Tyto obrázky při vytváření JAR souboru s aplikací skončí přímo v kořeni tohoto JAR souboru a nahrají se následujícím způsobem:
try{
Image background = Image.createImage(„/background.png“);
} catch(Exception e){
e.printStackTrace();
}
Před ukázkou metody paint()
se hodí ještě blíže popsat způsob umisťování obrázků a textových řetězců na displej. Metody Graphics.drawImage()
a Graphics.drawString()
v prvním parametru dostávají objekt, který chceme vykreslit. Druhý a třetí parametr určují souřadnice kotvy, poslední parametr pak říká, v jakém místě obrázku nebo textu se kotva nachází. Umístění kotvy se definuje pomocí bitového součtu (operátor |
) konstanty třídy Graphics
definující její x souřadnici a konstanty definující její y souřadnici. V následující ukázce je kotva umístěna v obou případech v levém horním rohu objektu:
protected void paint(Graphics g) {
// nastavení barvy na bílou
g.setColor(0xffffff);
// překreslení celého displeje aktuální barvou
g.fillRect(0,0,getWidth(), getHeight());
// nakreslení obrázku background
g.drawImage(background, 0, 0, Graphics.TOP | Graphics.LEFT);
/* zde se vykreslují jednotlivé předměty */
// nastavení barvy na červenou
g.setColor(0x840400);
// nakreslení počtu bodů
g.drawString(„“ + score, 0, 2, Graphics.TOP | Graphics.LEFT);
}
GameSprite
Třídu GameSprite
použijeme k reprezentaci lodi, mincí a bomb. Jak už je řečeno výše, je to velmi minimalistická implementace třídy Sprite
z herního rozhraní MIDP 2.0. Třída Sprite
navíc umožňuje spoustu praktických věcí, například animace obrázků či jejich transformace. Také metoda collidesWith(GameSprite sprite, boolean pixelLevel)
v GameSprite
bohužel v rámci MIDP 1.0 nemá možnost zjistit kolize na úrovni pixelů (dva obrázky kolidují pouze tehdy, překrývají-li se neprůhlednými pixely), takže druhý parametr je zde jen do počtu z důvodu kompatibility se třídou Sprite
.
Třída GameSprite
obsahuje jeden obrázek a pozici jeho levého horního rohu, kterou lze měnit relativně (move(int dx, int dy)
) nebo nastavit absolutně (setPosition(int x, int y)
). Za vykreslení je zodpovědná metoda paint(Graphics g)
. Třída GameSprite
může být viditelná či neviditelná. Je-li neviditelná, metoda paint(Graphics g)
neudělá nic.
Ke stažení
Veškeré zde uvedené zdrojové kódy i obrázky ke hře a další materiál si můžete stáhnout a použít.
Starší komentáře ke článku
Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.
Mohlo by vás také zajímat
-
Nepodceňte UX na vašem webu: Proč na něm záleží?
10. dubna 2024 -
Členská sekce: 4 důvody proč ji mít na svém webu
12. března 2024
Nejnovější
-
Výkonný a kompaktní: ASOME Max Studio s výjimečným poměrem cena/výkon
11. listopadu 2024 -
Šokující data od Microsoftu: Kyberútoky rostou o stovky procent!
8. listopadu 2024 -
Chcete jedinečnou doménu? Objevte koncovky FOOD, MEME a MUSIC!
7. listopadu 2024 -
OpenAI představilo novou funkci ChatGPT Search
6. listopadu 2024