cerca
Giochino del Sottomarino
modifica cronologia stampa login logout

Wiki

Tools

Categorie

Help

edit SideBar

Utenti.GiochinoSottomarino-Collisioni History

Hide minor edits - Show changes to output

Added lines 1-133:
(:title Giochino del Sottomarino:)

<<|[[Giochino Sottomarino]]|>>

%titolo%''':: Giochino del Sottomarino - Collisioni ::'''

!!Collisioni
Adesso che abbiamo i sottomarini e la nave che lancia le bombe, dobbiamo anche fare in modo che quando una bomba tocca un sottomarino questo se ne vada. Un giorno, quando avremo la grafica a posto etc. ci saranno anche delle appropriate esplosioni. Nel frattempo dobbiamo però capire come fare a sapere se una bomba è venuta in contatto con un sottomarino.

Nella [[lezione precedente -> GiochinoSottomarino-SottomariniBombe]] ho fatto aggiungere il metodo

public Rectangle getBoundingBox();

in '''Entity''', così che tutti i nostri oggetti potessero rendere visibile la propria '''bounding box''', cioè la scatola che racchiude il singolo oggetto.

L'avevamo usata per lanciare le bombe direttamente sotto la nave. Ora la useremo anche per le collisioni.

!!!Intersezione tra rettangoli
Per sapere se due rettangoli sono sovrapposti, occorrerebbe fare qualche semplice calcolo. Per ora, tuttavia, ci limiteremo ad usare un metodo predefinito fornito da '''Shape''', che è la classe di '''Slick''' da cui deriva il nostro '''Rectangle'''. Il metodo è il seguente:

public boolean intersects(Shape shape);

L'algoritmo sarà quindi il seguente:
* per ogni '''Bomba''' attiva...
* ... scandisco tutti i '''Sottomarini'''.
* se un '''Sottomarino''' è attivo, controllo se la '''Box''' della '''Bomba''' interseca la '''Box''' del '''Sottomarino'''
* se '''SÌ''', il '''Sottomarino''' è stato distrutto, e quindi disattivo sia lui che la '''Bomba'''
* se '''NO''', pace

Questo algoritmo va eseguito ad ogni giro, e pertanto lo mettiamo nella update. Eccolo:

for (Bomba b : bombe) {

if (b.isActive()) {

for (Sottomarino s : nemici) {

if (s.isActive()) {

Rectangle boxBomba = b.getBoundingBox();
Rectangle boxNemico = s.getBoundingBox();

if (boxBomba.intersects(boxNemico)) {
b.setActive(false);
s.setActive(false);

Log.info("Affondato!");
}
}
}
}
}

Et voilà! Provare per credere.

!!!Ma sta roba è efficiente?
Questo algoritmo non è, di per se, efficientissimo: O(m * n), e quindi al crescere del numero di '''Sottomarini''' e di '''Bombe''' sarebbe ben poco furbo. Ma nel nostro caso abbiamo al massimo 10 '''Bombe''' e 20 '''Sottomarin''', il che vuol dire che ci sono 200 casi da testare ad ogni giro. Non sono per niente tanti, la macchina nemmeno se ne accorge.

Per limitare il numero di controlli si potrebbe adottare il sistema seguente:

* metto le sole '''Bombe''' attive in una lista separata
* metto i soli '''Sottomarini''' attivi in una lista separata
* faccio la scansione di queste due sole liste

Tuttavia, per quanto in linea teorica questo sia migliore nel caso medio, all'atto pratico non cambierebbe proprio nulla.

Un altro sistema, che viene utile quando ci sono veramente tanti oggetti, è inserire i singoli oggetti in liste che rappresentano porzioni dello spazio di gioco. In questo modo si possono controllare le collisioni solo tra oggetti che appartengono alla stessa porzione di spazio di gioco, dal momento che sarebbe inutile in partenza verificare se una '''Bomba''' che si trova in alto a sinistra dello schermo possa collidere con un '''Sottomarino''' che si trova in basso a destra. Ma ancora, la gestione di queste liste porterebbe vantaggi solo se avessimo un numero molto maggiore di oggetti.

Sotto sotto, il metodo ''intersects'' di '''Rectangle''' è implementato così:

if(shape instanceof Rectangle) {
Rectangle other = (Rectangle)shape;
if ((x > (other.x + other.width)) || ((x + width) < other.x)) {
return false;
}
if ((y > (other.y + other.height)) || ((y + height) < other.y)) {
return false;
}

return true;
}

In questo modo si può vedere la logica: basta vedere se almeno uno degli angoli di un rettangolo è contenuto nei confini dell'altro. '''instanceof''' viene usato perché la '''Shape''' potrebbe anche essere un '''Circle''' o un altro tipo di poligono.

!!Frequenza di rilascio delle bombe
Adesso come adesso, non appena viene rilasciato lo SPAZIO, se c'è una '''Bomba''' disponibile questa viene lanciata subito. Vogliamo però limitare la frequenza di rilascio delle '''Bombe''', ad esempio imporre che il giocatore possa lanciarne una ogni 1.5 secondi. Per fare ciò useremo ancora la classe Timer introdotta precedentemente.

Ci serve quindi un '''Timer''' in '''StatoGioco''':

private Timer timerDrop;

e un'altra variabile, che chiamiamo '''canDrop''':

private boolean canDrop;

La condizione è questa: posso lanciare '''Bombe''' solo se '''canDrop''' è '''true'''. Quando lancio una '''Bomba''', quindi, metto '''canDrop''' a '''false''', e faccio partire il '''Timer''' chiamato '''timerDrop'''. Quando il '''timerDrop''' scade, rimette la condizione '''canDrop''' a '''true'''.

Nel metodo '''enter''' mettiamo quindi ste righe:

canDrop = true;

timerDrop = new Timer(new Runnable() {

public void run() {
canDrop = true;
}
}, 1500);

In '''update''' dobbiamo aggiornare questo '''Timer''':

timerDrop.update(delta);


e poi nel metodo '''keyReleased''' riscriviamo il codice che permette di lanciare le '''Bombe''':

if (canDrop && key == Input.KEY_SPACE) {
for (Bomba b : bombe) {
if (!b.isActive()) {
Rectangle r = player.getBoundingBox();
b.start(r.getX() + r.getWidth() / 2, r.getY() + r.getHeight());
canDrop = false;
timerDrop.start();
break;
}
}
}

Scritto così, il codice esegue esattamente quanto avevamo stabilito sopra.

<<|[[Giochino Sottomarino]]|>>

----
[[!Guide]]