Nella precedente lezione su come sviluppare un gioco per Android abbiamo visto come disporre una semplice immagine nella nostra applicazione e, successivamente, abbiamo implementato una semplicissima funzione drag and drop. Giunti alla quinta lezione di questo corso, dovremmo sapere come avviare un’applicazione a schermo intero, come utilizzare un thread separato per il nostro ciclo di gioco, come caricare un’immagine dalle risorse, disegnarla all’interno dell’oggetto canvas e come gestire gli eventi più semplici del touchscreen. È giunta l’ora, dunque, di far muovere le immagini sul display, vediamo come.[divider]
Implementare la classe Speed.java
Il nostro obiettivo è quello di far muovere le immagini sul display e, nel nostro caso, dare al droide un’altra abilità: quella di muoversi autonomamente fino ai bordi del display (naturalmente non dovrà mai uscire!). Dovremo aggiungere, quindi, un metodo che sostituisca le coordinate x e y dell’immagine (la rappresentazione del droide) in base alla velocità di quest’ultimo.
La velocità dovrà essere una classe a sé stante, contenuta dal droide. Per questo, implementeremo Speed.java. L’implementazione, al momento, sarà molto semplice, ma nelle prossime lezioni utilizzeremo quelli che nel gergo vengono chiamati “Strategy Pattern“. Qui di seguito trovate la classe Speed.java:
package it.androidblog.droidz.model.components; public class Speed { public static final int DIRECTION_RIGHT = 1; public static final int DIRECTION_LEFT = -1; public static final int DIRECTION_UP = -1; public static final int DIRECTION_DOWN = 1; private float xv = 1; // valore velocità sull'asse delle X private float yv = 1; // valore velocità sull'asse delle Y private int xDirection = DIRECTION_RIGHT; private int yDirection = DIRECTION_DOWN; public Speed() { this.xv = 1; this.yv = 1; } public Speed(float xv, float yv) { this.xv = xv; this.yv = yv; } public float getXv() { return xv; } public void setXv(float xv) { this.xv = xv; } public float getYv() { return yv; } public void setYv(float yv) { this.yv = yv; } public int getxDirection() { return xDirection; } public void setxDirection(int xDirection) { this.xDirection = xDirection; } public int getyDirection() { return yDirection; } public void setyDirection(int yDirection) { this.yDirection = yDirection; } // cambia la direzione sull'asse delle X public void toggleXDirection() { xDirection = xDirection * -1; } // cambia la direzione sull'asse delle Y public void toggleYDirection() { yDirection = yDirection * -1; } }
Come potete vedere, utilizziamo delle costanti per determinare la direzione del droide sulle assi e, poiché quest’ultimo ha due velocità (una verticale e una orizzontale), ad ogni aggiornamento dello stato di gioco verranno impostate le nuove coordinate in merito alla direzione e alle due velocità. Il droide, naturalmente, si muoverà sulla superficie dell’oggetto canvas.
Per spostarsi seguendo una traiettoria diagonale, ad esempio, le due componenti della velocità dovranno essere uguali. Se vogliamo far muovere il droide solo orizzontalmente, dovremo impostare la velocità sull’asse y pari a zero. Se, invece, impostiamo la velocità sull’asse x pari a 1 e sull’asse y pari a 0.5, il droide si muoverà di 22.5 gradi rispetto all’asse x. Semplice geometria.
Nella classe Speed.java, potete trovare tutti i metodi setters e getters, più toggleXDirection() e toggleYDirection() che ci serviranno a cambiare la direzione del droide con una semplice chiamata. In seguito vedremo anche come rilevare le collisioni con la parete dello schermo che, vedrete, è molto utile.
[divider]
Aggiornare lo stato degli oggetti
A questo punto, nella classe MainThread.java, nel metodo run() dovremmo fare una piccola modifica: un’aggiunta al blocco di codice sincronizzato dove dovremo aggiornare lo stato di gioco. Prima di onDraw(), dovrete inserire la seguente linea di codice:
this.gamePanel.update();
Il corrispondente metodo update() lo implementeremo nella classe MainGamePanel.java, ma prima di farlo, andiamo ad apportare alcune modifiche alla classe Droid.java:
private Speed speed; // la velocità con le sue direzioni this.speed = new Speed(); public Speed getSpeed() { return speed; } public void setSpeed(Speed speed) { this.speed = speed; }
Se avete seguito la guida fino a questo punto, dovreste sapere che il codice sopra riportato non è strettamente sequenziale. Infatti dovrete dichiarare l’oggetto speed all‘inizio della classe, istanziarlo nel costruttore e inserire i due metodi dove ritenete più opportuno. A questo punto, possiamo passare al metodo update() del quale abbiamo parlato prima:
public void update() { // controllo collisioni con la parete destra if (droid.getSpeed().getxDirection() == Speed.DIRECTION_RIGHT && droid.getX() + droid.getBitmap().getWidth() / 2 >= getWidth()) { droid.getSpeed().toggleXDirection(); } // controllo collisioni con la parete sinistra if (droid.getSpeed().getxDirection() == Speed.DIRECTION_LEFT && droid.getX() - droid.getBitmap().getWidth() / 2 <= 0) { droid.getSpeed().toggleXDirection(); } // controllo collisioni verso il basso if (droid.getSpeed().getyDirection() == Speed.DIRECTION_DOWN && droid.getY() + droid.getBitmap().getHeight() / 2 >= getHeight()) { droid.getSpeed().toggleYDirection(); } // controllo collisioni verso l'alto if (droid.getSpeed().getyDirection() == Speed.DIRECTION_UP && droid.getY() - droid.getBitmap().getHeight() / 2 <= 0) { droid.getSpeed().toggleYDirection(); } // aggiornamento del droide droid.update(); }
Questo metodo si occuperà di aggiornare lo stato di tutti gli oggetti del gioco ma, in questo caso, abbiamo solo il droide. Il controllo che andiamo ad effettuare è molto semplice: se la posizione del droide è il muro, chiamiamo il metodo per cambiare la direzione di quest’ultimo e, infine, aggiorniamo la posizione del droide con un altro metodo update(), questa volta presente nella classe Droid.java.
Per capire meglio il semplice rilevamento delle collisioni che abbiamo implementato, bisogna ricordare che la posizione del droide è esattamente al centro dell’immagine (ecco perché dividiamo per due) e, infine, che getWidth () e getHeight () restituiscono la larghezza e l’altezza della View e il nostro pannello di gioco è esattamente una View, ricordate?
Di seguito, il metodo update() del nostro droide:
public void update() { if (!touched) { x += (speed.getXv() * speed.getxDirection()); y += (speed.getYv() * speed.getyDirection()); } }
A questo punto provate ad avviare l’applicazione e vedrete il risultato.
Inoltre, non avendo eliminato la funzione di drag e drop, potrete sempre usufruirne. Nella prossima lezione, vedremo finalmente un ciclo di gioco più avanzato che tiene in considerazione anche FPS e UPS. Come sempre, se cercate dei chiarimenti, commentate l’articolo.