cerca
Giochino del Sottomarino
modifica cronologia stampa login logout

Wiki

Tools

Categorie

Help

edit SideBar

Giochino del Sottomarino

<< Immagini | Giochino Sottomarino | Fumo e stati di gioco >>

 :: Giochino del Sottomarino - Esplosioni e oscillazioni ::

Esplosioni


L'esplosione

Girovagando su internet ho trovato questa immagine di un'esplosione, già divisa in frames e quindi già pronta per il nostro giochino del sottomarino. Introdurremo quindi un paio di cose nuove per poter utilizzare questa immagine, che come le altre va salvata in ./data/esplosione.png.

Ci serve innanzitutto una nuova classe, che fantasiosamente chiameremo Esplosione, e che implementerà Entity. Le variabili inizialmente saranno le solite:

 boolean active;

 private float x;
 private float y;

 private float width;
 private float height;

Ci servono però altre cose:

 private Image image;
 private SpriteSheet sprites;
 private Animation animation;

Image già la conosciamo. Le altre due ci servono per creare l'animazione: Slick ci fornisce già l'infrastruttura necessaria.

Nel costruttore, dobbiamo innanzitutto creare l'Image:

 image = new Image("./data/esplosione.png");

e poi costruire lo SpriteSheet:

 sprites = new SpriteSheet(image, 128, 128);

Stiamo dicendo a Slick di costruire un foglio di sprites a partire dalla nostra Image, e gli diciamo che ognuna delle caselle che contengono sprites è larga 128 pixel ed alta 128. Slick automaticamente ricaverà i frames dall'immagine qui sopra. Lo SpriteSheet ci permette anche di accedere ad un singolo frame e così via.

Ma non è finita. Un conto è avere uno foglio di sprites, un conto è poi animarli. Slick ci offre già anche questo:

 animation = new Animation(sprites, 60);

Il primo parametro che passiamo al costruttore di Animation è lo SpriteSheet da cui trarre i frames. Il secondo parametro è la durata in millisecondi di ogni frame. Le animazioni possono essere messe in loop, ovvero ripartire automaticamente da capo quando finiscono. Noi non vogliamo ciò: quando l'animazione ha terminato un giro, anche l'esplosione è terminata.

 animation.setLooping(false);

Ci servono poi le altre variabili:

 active = false;

 x = 0.0f;
 y = 0.0f;

 width = image.getWidth() / sprites.getHorizontalCount();
 height = image.getHeight() / sprites.getVerticalCount();

width ed height sono le dimensioni del singolo frame.

Dobbiamo introdurre il solito metodo start, che chiameremo dall'esterno:

 public void start(float x, float y) {
   this.active = true;
   this.x = x;
   this.y = y;
   animation.restart();
 }

Diciamo anche all'animazione di ripartire dal primo frame.

Ecco il metodo update:

 if (active) {
   animation.update(delta);

   if (animation.isStopped()) {
     active = false;
   }
 }

Quando l'animazione arriva in fondo, alza automaticamente una flag, e noi possiamo scoprirlo subito.

In render dobbiamo disegnare l'esplosione nelle coordinate che ci sono state passate. Dal momento che passeremo le coordinate del centro dell'esplosione, dovremo fare una cosa del genere:

 if (active) {
   animation.draw(x - width / 2, y - height / 2);
 }

perché le coordinate passate a draw sono quelle dell'angolo in alto a sinistra.

Diciamo anche a NetBeans di costruire automaticamente un getter per la variabile active (pulsante destro - Insert Code etc.).

Attivare le esplosioni

Torniamo allo StatoGioco. Ci serve la solita ArrayList di Esplosioni, e vi lascio come compito la scrittura di questo codice. In update e render dovremo aggiornare e renderizzare tutte le Esplosioni, allo stesso modo delle altre Entity.

Quando un Sottomarino viene colpito, vogliamo che l'esplosione avvenga nel centro esatto del Sottomarino. Andiamo quindi alla parte di codice, nella update di StatoGioco, che controlla le collisioni, e ne modifichiamo il nucleo in questo modo:

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

   attivaEsplosione(boxNemico.getCenterX(), boxNemico.getCenterY());
 }

Ovviamente, ci serve anche il metodo attivaEsplosione, che si commenta da sé:

 public void attivaEsplosione(float x, float y) {
   for (Esplosione e : esplosioni) {
    if (!e.isActive()) {
      e.start(x, y);
      break;
    }
   }
 }

Fatto! Ora ci sarà una bellissima Esplosione quando un Sottomarino viene affondato:)

LFO

Mi è venuto in mente che sarebbe bello poter animare un po' la Nave e anche le Bombe, mentre cadono. Traendo ispirazione dai sintetizzatori audio, inventeremo allora una classe LFO, che sta per Low Frequency Oscillator.

A questa classe passeremo una frequenza di oscillazione, e l'ampiezza di tale oscillazione. Internamente useremo la funzione trigonometrica seno per ottenere l'oscillazione stessa.

 public class LFO {
    private double speed;
    private float start;
    private float span;
    private float angle;
  • speed: la velocità di rotazione. Convertiremo infatti una rotazione in distanza lineare mediante il seno.
  • start è il valore centrale da cui partire, e attorno al quale "oscilleremo"
  • span è l'ampiezza lineare dell'oscillazione
  • angle è ad uso interno, e serve per tenere traccia dell'angolo

Nel costruttore inizializziamo tutto ciò:

 public LFO(float hz, float start, float span) {

   this.start = start;
   this.span = span;

   this.speed = (Math.PI * hz) / 1000;

   this.angle = 0;

 }

Convertiamo la frequenza in velocità di rotazione data in radianti al secondo, usando come al solito i millisecondi.

Facciamo subito anche un metodo restart:

 public void restart() {
   angle = 0;
 }

In update dobbiamo aggiornare la rotazione:

 public void update(int delta) {
   angle += speed * delta;

   if (angle > Math.PI * 2) {
     angle -= Math.PI * 2;
   }
 }

In questo modo, l'angolo viene incrementato secondo la velocità di rotazione, ovvero la frequenza passata al costruttore. Lo riportiamo sotto Math.PI così che se ci venisse voglia di tracciarlo ci capiremmo qualcosa di più, se no continuerebbe a crescere.

L'ultimo metodo è quello che converte da angolo a distanza lineare:

 public float getValue() {
   return (float) (start + Math.sin(angle) * span);
 }

Lo si potrebbe benissimo implementare fuori da LFO, ovvero farsi dare l'angolo e poi pensarci noi a "linearizzarlo", ma va bene anche così.

Oscillazione della Nave

Vogliamo che la Nave oscilli su e giù per via delle onde (che ancora non ci sono). Aggiungiamo un LFO alla lista delle variabili della classe, lfoY, e nel costruttore lo inizializziamo:

 lfoY = new LFO(0.4f, 0, 6);

Lo useremo per modificare la posizione verticale della Nave. In update dobbiamo ricordarci di aggiornare l'LFO, e poi trarne il valore da applicare alla y:

 lfoY.update(delta);
 box.setLocation(x, y + lfoY.getValue());

Dal momento che in render abbiamo questo codice:

 if (direction) {        
   naveDx.draw(box.getX(), box.getY());
 } else {
    naveSx.draw(box.getX(), box.getY());
 }

avremo la Nave che va su e giù:) Provate a modificare l'ampiezza dell'oscillazione e la frequenza per vedere che cosa succede.

Oscillazione delle Bombe

La Bomba, quando cade, dovrà ondeggiare a destra e a sinistra. Forse è più un comportamento da bomba di aereo, ma va bene lo stesso:)

Aggiungiamo l'LFO alla Bomba:

 private LFO lfoAngolo;

e nel costruttore lo inizializziamo:

 lfoAngolo = new LFO(0.8f, 0, 20);

In start lo resettiamo:

 lfoAngolo.restart();

e in update lo aggiorniamo:

 lfoAngolo.update(delta);

Il suo valore però lo leggiamo solo nella render. Infatti, lo usiamo per disegnare l'immagine della Bomba ruotata di qua e di là, ma senza spostare la Box. L'Image permette di essere disegnata con un angolo, però espresso in gradi sessagesimali, e non in radianti! Ecco perché l'ampiezza dell'oscillazione, sopra, era a 20: 20° è la rotazione massima in entrambi i sensi.

Nella render, quindi, facciamo così:

 bomba.setRotation(lfoAngolo.getValue());
 bomba.draw(x, y);

E così anche la Bomba oscilla quando cade:)

<< Immagini | Giochino Sottomarino | Fumo e stati di gioco >>


Guide