cerca
Giochino del Sottomarino
modifica cronologia stampa login logout

Wiki

Tools

Categorie

Help

edit SideBar

Giochino del Sottomarino

<< Sottomarini e bombe | Giochino Sottomarino | Immagini >>

 :: 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 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 , 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.

<< Sottomarini e bombe | Giochino Sottomarino | Immagini >>


Guide