Játékciklus MIDP 2.0-ban: GameCanvas, getKeyStates és Sprite
Közzétéve:
A MIDP 1.0 idején minden játékfejlesztő megírta a saját játékkönyvtárát: dupla puffert, sprite-ot, csempés hátteret. A MIDP 2.0 ebből sokat átvett a javax.microedition.lcdui.game csomagba. Ma már a legtöbb új telefon MIDP 2.0-s, ezért érdemes ezekre az osztályokra építeni. Ebben a jegyzetben egy egyszerű, de rendesen felépített játékciklust mutatok be.
A váz
import javax.microedition.lcdui.*;
import javax.microedition.lcdui.game.*;
public class JatekVaszon extends GameCanvas implements Runnable {
private static final int KEPKOCKA_MS = 50; // kb. 20 fps
private volatile boolean fut;
private Sprite hajo;
private int x = 60, y = 150;
public JatekVaszon(Image hajoKep) {
super(true); // a billentyűeseményeket elnyomjuk
hajo = new Sprite(hajoKep, 16, 16); // 16x16-os képkockák
}
public void indit() {
fut = true;
new Thread(this).start();
}
public void leallit() { fut = false; }
public void run() {
Graphics g = getGraphics();
while (fut) {
long kezdet = System.currentTimeMillis();
bemenet();
frissit();
rajzol(g);
flushGraphics(); // a háttérpuffer kirakása egyben
long eltelt = System.currentTimeMillis() - kezdet;
if (eltelt < KEPKOCKA_MS) {
try { Thread.sleep(KEPKOCKA_MS - eltelt); }
catch (InterruptedException e) { }
}
}
}
private void bemenet() {
int k = getKeyStates();
if ((k & LEFT_PRESSED) != 0) x -= 3;
if ((k & RIGHT_PRESSED) != 0) x += 3;
if ((k & UP_PRESSED) != 0) y -= 3;
if ((k & DOWN_PRESSED) != 0) y += 3;
}
private void frissit() {
hajo.setPosition(x, y);
hajo.nextFrame();
}
private void rajzol(Graphics g) {
g.setColor(0xF5F6F8);
g.fillRect(0, 0, getWidth(), getHeight());
hajo.paint(g);
}
protected void hideNotify() { leallit(); }
}
Miért így?
Saját szál. A GameCanvas-nál nem a rendszer hívja a paint()-et, hanem mi rajzolunk a getGraphics()-től kapott háttérpufferbe, majd a flushGraphics() egyszerre rakja ki a képet. Így nincs villogás.
Fix képkockaidő. A ciklus mindig 50 ms-ra egészít ki. Gyors telefonon többet alszik, lassún kevesebbet. Ha egy készülék a 50 ms-ot sem tudja tartani, a játék lelassul, de nem ugrál. Ennél a műfajnál ez a jobb választás.
Billentyűállapot. A super(true) miatt a keyPressed események nem érkeznek meg, cserébe a getKeyStates() egyetlen bitmaszkban adja vissza, mi van éppen lenyomva. Átlós mozgásnál (fel és jobbra egyszerre) ez a különbség érezhető.
Ütközés
if (hajo.collidesWith(akadaly, false)) { // gyors, téglalap alapú
if (hajo.collidesWith(akadaly, true)) { // pixelpontos, csak ha kell
robbanas();
}
}
A collidesWith második paramétere kapcsolja a pixelpontos vizsgálatot. Ez az átlátszó pixeleket is figyelembe veszi, de lassú. A fenti kétlépcsős megoldás a legtöbb esetben elég gyors marad.
Háttér: TiledLayer és LayerManager
A pályát érdemes TiledLayer-ből építeni (egy kép csempékre vágva, és egy tömb, amely megmondja, melyik csempe hol van), az egészet pedig egy LayerManager-be tenni. A setViewWindow hívással így egyetlen sorban lehet görgetni a nézetet, a rajzolási sorrendet pedig a rétegek sorrendje adja.
Végül: a szál leállítását a MIDlet pauseApp metódusából is hívd meg. A hideNotify önmagában nem minden telefonon érkezik meg hívás közben.