cerca
Giochino del Sottomarino
modifica cronologia stampa login logout

Wiki

Tools

Categorie

Help

edit SideBar

Utenti.GiochinoSottomarino-FumoStati History

Show minor edits - Show changes to output

Added lines 139-148:

!!!Il passaggio di stato
Sempre in '''update''', dobbiamo effettivamente attivare il passaggio allo stato '''StatoBoss'''. Lo facciamo così:

if (cambiaLivello) {
cambiaLivello = false;
game.enterState(Globali.LIVELLO_BOSS);
}

Il passaggio sarà un po' brusco, ma vedremo come avere delle transizioni tra uno stato e l'altro.
Changed lines 56-138 from:
''...continua...''
to:
!!Aggiungiamo uno Stato
Vogliamo aggiungere uno stato di gioco in cui prossimamente metteremo il boss di fine livello:) Prepariamo dunque le cose per questo "switch off" (termine di moda ora che c
'è il digitale terrestre).

Per prima cosa, ci serve una nuova classe che chiameremo
'''StatoBoss'''. Anch'essa implementerà '''GameState''', e per ora la lasciamo vuota. Comunque, per renderla funzionale, invito a guardare la [[prima lezione]] per un setup di base. Prossimamente la riempiremo anche.

!!!L'ID degli stati e altre cosucce globali
Dal momento che stiamo introducendo un nuovo stato di gioco, nasce la necessità di tenere via delle informazioni che "travalichino" i confini della singola istanza di ogni '''GameState'''. Un modo per ottenere ciò è quello di utilizzare variabili pubbliche statiche infilate in una classe apposita.

Creiamo quindi una classe chiamata fantasiosamente '''Globali''' e ci mettiamo alcune variabili.

public static int punti;

in cui salveremo il punteggio ottenuto dal giocatore.

public static int vittime;

in cui salveremo il numero di sottomarini affondati, per avere un'idea della nostra efficienza sterminatrice.

Poi ci mettiamo anche queste variabili:

public static final int LIVELLO_GIOCO = 0;
public static final int LIVELLO_BOSS = 1;

In questo modo non rischiamo di confonderci con gli ID dei vari stati di '''Gioco'''. Lo '''StatoGioco''' dovrà quindi ritornare '''Globali.LIVELLO_GIOCO''' nel suo metodo '''getID()''', mentre '''StatoBoss''' dovrà ritornare '''Globali.LIVELLO_BOSS'''.

!!!Aggiunta dello StatoBoss
In '''Applicazione''' dobbiamo semplicemente istanziare il nuovo stato, ed aggiungerlo alla lista:

GameState boss = new StatoBoss();
boss.setInput(container.getInput());
this.addState(boss);

!!!Passaggio da uno stato all'altro
Decidiamo che il primo livello dovrà durare 60 secondi, trascorsi i quali si passerà automaticamente allo '''StatoBoss'''. Per fare questo tireremo in piedi un nuovo '''Timer''' in '''StatoGioco''', il quale allo scadere chiamerà una callback che farà cambiare lo stato corrente all'applicazione. Colui che può cambiare livello è il '''Game''', che è visibile nella '''update'''. Questo vuol dire che la callback dovrà tirare su una flag, che verrà poi letta nella '''update'''.

private boolean cambiaLivello;

e la mettiamo a '''false''' in '''enter'''.

Creiamo poi il timer:

private Timer timerLivello;

e lo possiamo inizializzare direttamente nel costruttore di '''StatoGioco''' in questo modo:

timerLivello = new Timer(new Runnable() {
public void run() {
cambiaLivello = true;
}
}, 60 * 1000);

e sapete già che cosa succederà:) Nella '''update''' lo aggiorniamo, e nella '''enter''' ne chiamiamo lo '''start'''.

!!!Il giocatore deve sapere quanto tempo manca!
Giusto.

!!!E come facciamo?
Andiamo nella '''render'''. '''Slick''' offre un paio di sistemi per disegnare testo a schermo. Il più semplice sfrutta il metodo '''drawString''' di '''Graphics''', ed è quello che faremo anche noi.

Come prima cosa, dobbiamo sapere quanto tempo effettivamente manca:

int tempoMancante = (timerLivello.getTempo() - timerLivello.getTempoTrascorso()) / 1000;

Divido per 1000 perché vogliamo il tempo in secondi, mentre il '''Timer''' funziona a millisecondi. Appena sotto aggiungiamo anche questo:

g.drawString("Tempo: " + tempoMancante, screenWidth - 100, 10);

così che sarà visualizzato il tempo mancante.

!!!Altre informazioni
Già che ci siamo, vogliamo visualizzare anche altre informazioni, ovvero i ''punti'' e il numero di sottomarini affondati, cioè le ''vittime''. Sempre in render quindi mettiamo queste righe:

g.drawString("Affondati: " + Globali.vittime, screenWidth - 135, 40);
g.drawString("Punti: " + Globali.punti, screenWidth - 100, 70);

Nella '''enter''' dobbiamo azzerare queste variabili, altrimenti cresceranno sempre.

Andiamo ora nel metodo '''update''', nel punto in cui il controllo delle collisioni tra '''Bombe''' e '''Sottomarini''' ha avuto successo, e aggiungiamo queste due righe:

Globali.vittime ++;
Globali.punti += 100;

Ovviamente se i punti non vanno bene mettetene degli altri:)
Added line 56:
''...continua...''
Added lines 1-60:
(:title Giochino del Sottomarino:)

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

%titolo%''':: Giochino del Sottomarino - Fumo e Stati di Gioco ::'''

!!Aggiungere Fumo alla Nave
La '''Nave''' sarebbe più carina se uscisse del fumo dal comignolo. Per fare il fumo, però, non usiamo un'animazione "semplice", perché voglio introdurre un coso nuovo. In '''Slick''' c'è la possibilità di utilizzare un '''particle system''', ovvero una specie di sistema autonomo in cui ci sono delle particelle più o meno libere, che rispondono a variabili come la gravità etc., e che si muovono in modo quasi casuale. Serve per creare cose come esplosioni, fumo, scintille, in generale effetti grafici un po' più casuali e dall'aspetto naturale rispetto ad animazioni predefinite.

Nella [[pagina di Slick -> http://slick.cokeandcode.com]] c'è il link al webstart di '''Pedigree''', che è un editor che permette di creare questi particle system. Poi questi sistemi vengono esportati, e vengono tirati su via codice. Ovviamente sono un po' più onerosi per la CPU rispetto ad un'animazione "normale", ma tanto qui non stiamo esagerando.

[[Qui dentro -> Attach:fumo.zip]] c'è l'occorrente per il fumo: un semplice effetto che ho preparato. Lo zip contiene due file:
* fumoemitter.xml
* particle.tga

L'XML contiene la definizione dell'emettitore. L'immagine è invece l'immagine che sarà usata per ogni particella. Mettiamo questi due files nella solita cartella, '''./data'''.

Andiamo quindi alla classe '''Nave'''. Ci servono queste variabili nuove:

ParticleSystem fumo;
ConfigurableEmitter fumoEmitter;

'''fumo''' è il particle system, che può contenere a sua volta diversi emettitori. Pedigree permette di esportare interi sistemi con diversi emettitori etc., ma qui per motivi di flessibilità creeremo il particle system via codice e ci attaccheremo un emitter, '''fumoEmitter'''.

Nel costruttore di '''Nave''' mettiamo queste righe:

fumo = new ParticleSystem(new Image("./data/particle.tga"));
fumoEmitter = ParticleIO.loadEmitter("./data/fumoemitter.xml");
fumo.addEmitter(fumoEmitter);

Questo codice potrebbe generare un'eccezione '''IOException''', regolatevi voi se volete racchiuderlo in un '''try .. catch''' oppure far rimbalzare il tutto fuori da '''Nave'''.

Le righe sono abbastanza autoesplicative: creo il particle system dandogli in pasto il nome dell'immagine che farà da particella. Creo successivamente l'emitter a partire dalla configurazione XML, e poi aggiungo questo emitter al system.

Nel metodo '''render''' devo anche disegnare il fumo. Decidiamo di disegnarlo "sopra" la '''Nave''', quindi facciamo così:

if (direction) {
naveDx.draw(box.getX(), box.getY());
fumo.render(box.getX() + box.getWidth() / 2 - 15, box.getY());
} else {
naveSx.draw(box.getX(), box.getY());
fumo.render(box.getX() + box.getWidth() / 2 + 15, box.getY());
}

Le coordinate passate alla '''render''' del fumo sono quelle dell'emitter. Sono definite così perché il camino della '''Nave''' non è esattamente centrato rispetto all'immagine della '''Nave''' stessa.

Andiamo ora al metodo '''update'''. Chiaramente dovremo anche aggiornare l'emitter del fumo. Però, siccome la '''Nave''' si muove, vogliamo che il fumo faccia come il fumo vero, cioè resti in qualche modo "dietro" alla '''Nave'''.

A questo proposito, l'emitter ci viene incontro, perché ha un parametro che rappresenta il vento. Lo impostiamo così:

fumoEmitter.windFactor.setValue((speed / maxSpeed) * -3.0f * dirMult);
fumo.update(delta);

Così facendo il fumo andrà in direzione opposta alla '''Nave'''. Possiamo ora provare il gioco e vedere l'effetto che fa:)


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

----
[[!Guide]]