cerca
Giochino del Sottomarino
modifica cronologia stampa login logout

Wiki

Tools

Categorie

Help

edit SideBar

Utenti.GiochinoSottomarino-EsplosioniLFO History

Show minor edits - Show changes to output

Changed lines 55-56 from:
width = image.getWidth() / 4;
height = image.getHeight() / 4;
to:
width = image.getWidth() / sprites.getHorizontalCount();
height = image.getHeight() / sprites.getVerticalCount();
Added lines 57-58:

'''width''' ed '''height''' sono le dimensioni del singolo frame.
Changed line 44 from:
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 frames. 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.
to:
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.
Changed line 38 from:
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.
to:
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.
Added lines 115-219:

!!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:)
Added lines 1-119:
(:title Giochino del Sottomarino:)

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

%titolo%''':: Giochino del Sottomarino - Esplosioni e oscillazioni ::'''

!!Esplosioni
%lframe width=200px%Attach:sottomarino-esplosioni.png|'''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 frames. 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() / 4;
height = image.getHeight() / 4;

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:)

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

----
[[!Guide]]