cerca
Giochino del Sottomarino
modifica cronologia stampa login logout

Wiki

Tools

Categorie

Help

edit SideBar

Utenti.GiochinoSottomarino-SottomariniBombe History

Hide minor edits - Show changes to output

Changed line 242 from:
bombe.add(new Bomba());
to:
bombe.add(new Bomba(0.0f, 0.0f));
Changed lines 75-76 from:
Facciamo aggiungere a NetBeans, tramite la funzionalità '''Insert Code''', il '''getter''' e il '''setter''' della variabile booleana '''active''', perché serviranno qui sotto.
to:
Facciamo aggiungere a NetBeans, tramite la funzionalità '''Insert Code''', il '''getter''' e il '''setter''' della variabile booleana '''active''', perché serviranno qui sotto. Per generare automaticamente questo codice si clicca con il destro nella zona di editing, si sceglie '''Insert code''' e quindi scegliere i '''getter''', i '''setter''' e così via. Comodo.
Changed lines 230-240 from:
E mettiamoci anche metodi accessori per la variabile '''active''':

public boolean isActive() {
return active;
}

public void setActive(boolean active) {
this.active = active;
}

Netebeans può generare automaticamente questo codice di accesso ad '''active'''. Cliccando con il destro nella zona di editing della classe, si può scegliere ''Insert code''' e quindi scegliere i '''getter''', i '''setter''' e così via. Comodo
.
to:
E facciamo mettere a NetBeans anche metodi accessori per la variabile '''active'''.
Added lines 74-75:

Facciamo aggiungere a NetBeans, tramite la funzionalità '''Insert Code''', il '''getter''' e il '''setter''' della variabile booleana '''active''', perché serviranno qui sotto.
Changed lines 8-9 from:
Il '''Sottomarino''' è il nostro nemico. Per ora niente immagini, però possiamo comunque inventarci una classe ed infilarla nel gioco.
to:
Il '''Sottomarino''' è il nostro nemico. Per ora niente immagini, però possiamo comunque inventarci una classe ed infilarla nel gioco. Anche lui dovrà implementare '''Entity'''.
Changed lines 12-17 from:
private float x;
private float y;
private float speed;
private boolean active;
private Rectangle box;
to:
public class Sottomarino implements Entity {
private float x;
private float y;
private float speed;
private boolean active;
private Rectangle box;
Changed lines 166-168 from:
''...coming soon...''
to:
Usiamo, per le '''Bombe''', la stessa strategia dei '''Sottomarini''', cioè ne teniamo una lista in '''StatoGioco''' e le gestiamo da lì.

public class Bomba implements Entity {

boolean active;
private float x;
private float y;

private float speed = 100.0f / 1000.0f;

private Rectangle box;

Come le altre classi, ha bisogno delle solite variabili. La '''speed' è fissa perché assumiamo che sia sempre quella. Magari poi ci viene in mente di modificarla, ma per ora va bene così.

Il costruttore è molto simile a quello del '''Sottomarino''':

public Bomba (float x, float y) {
this.x = x;
this.y = y;

box = new Rectangle(x, y, 32, 32);

active = false;
}

Usiamo anche qui il metodo '''start''' per far partire la '''Bomba'''. Diversamente dal sottomarino qui le dimensioni della '''box''' sono fisse.

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

}

Nel metodo '''update''' dobbiamo vedere se la bomba è scomparsa al di sotto dello schermo. In quel caso, la disattiviamo:

public void update(GameContainer container, StateBasedGame game, int delta) {

if (active) {

y += speed * delta;

if (y > container.getHeight()) {

Log.info("Bomba fuori dallo schermo");
active = false;
}

box.setLocation(x, y);

}

}

Il metodo '''render''' è sempre il solito:

public void render(GameContainer container, StateBasedGame game, Graphics g) {
if (active) {
g.draw(box);
}
}

E mettiamoci anche metodi accessori per la variabile '''active''':

public boolean isActive() {
return active;
}

public void setActive(boolean active) {
this.active = active;
}

Netebeans può generare automaticamente questo codice di accesso ad '''active'''. Cliccando con il destro nella zona di editing della classe, si può scegliere ''Insert code''' e quindi scegliere i '''getter''', i '''setter''' e così via. Comodo.

!!!Le Bombe nello StatoGioco
Torniamo allo '''StatoGioco'''. Ci serve un array che conterrà le bombe:

private ArrayList<Bomba> bombe;

che verrà creato e popolato nel metodo '''enter''':

bombe = new ArrayList<Bomba>();

for (int i = 0; i < 10; i++) {
bombe.add(new Bomba());
}

Esattamente come per i '''Sottomarini''', in '''update''' aggiornerò tutte le bombe:

for (Bomba b : bombe) {
b.update(container, game, delta);
}

e in '''render''' le disegno:

for (Bomba b : bombe) {
b.render(container, game, g);
}

!!!Il lancio delle bombe
Il giocatore lancerà le bombe premendo SPAZIO. Dal momento che l'array delle bombe contiene 10 elementi, non ci saranno più di 10 bombe contemporaneamente sullo schermo. Per essere precisi, vogliamo che la bomba venga sganciata quando il giocatore ha rilasciato il tasto spazio. C'è un metodo apposito nel '''GameState''':

public void keyReleased(int key, char c) {

if (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());
break;
}
}
}
}

In questo codice c'è una chiamata ad un metodo non ancora esistente: '''player.getBoundingBox()'''. Questo metodo dovrà tornare la '''box''' del '''player''', cioè della '''Nave''' mossa dal giocatore.

Lascio questo come esercizio: aggiungere il metodo '''getBoundingBox''' alla '''Entity''' e quindi inserirlo in tutte le classi che finora implementano '''Entity''', cioè '''Nave''', '''Sottomarino''' e '''Bomba'''. Ci servirà per identificare le collisioni:)

Dopo aver fatto queste modifiche, premendo SPAZIO si rilasceranno le bombe sotto la '''Nave''':)
Added lines 114-167:

!!!L'evento
Torniamo allo '''StatoGioco'''. Ci serve una variabile per il Timer:

private Timer timerSottomarino;

e in '''enter''' la istanzio completa di tutto:

timerSottomarino = new Timer(new Runnable() {
public void run() {
attivaSottomarino();
}
}, 5000);

timerSottomarino.start();

Ecco la classe anonima. Il primo parametro del costruttore di '''Timer''' vuole un'istanza di '''Runnable'''. Invece di dichiararla a parte, con questa sintassi la posso dichiarare ed istanziare al volo, con tanto di metodo '''run''' incluso. Il vantaggio è che a questa istanza anonima di '''Runnable''' saranno visibili tutti i metodi della classe che la ospita, che nel nostro caso è la classe '''StatoGioco'''!

Infatto, in '''run''' c'è la chiamata ad '''attivaSottomarino''', che è un metodo di '''StatoGioco''':

public void attivaSottomarino() {
for (Sottomarino s : nemici) {
if (!s.isActive()) {
// posizione casuale

float width = (float) (50 + Math.random() * 200f);
float height = (float) (20 + Math.random() * 50f);
float depth = (float) (200 + Math.random() * (screenHeight - 200));
float speed = (float) ((25 + Math.random() * 100) / 1000f);

double caso = Math.random() * 100;

float ics = caso < 50 ? 0 : screenWidth - width - 1;

s.start(ics, depth, width, height, caso < 50 ? speed : speed * -1);

break;
}
}

timerSottomarino.start();
}

Le variabili '''screenWidth''' e '''screenHeight''' le dichiariamo come '''int''' in '''StatoGioco''', e nel metodo '''init''' le inizializziamo:

screenWidth = container.getWidth();
screenHeight = container.getHeight();

In questo metodo ci inventiamo la forma e la posizione del '''Sottomarino''', e lo facciamo partire.

!!Le Bombe
''...coming soon...''
Added lines 1-118:
(:title Giochino del Sottomarino:)

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

%titolo%''':: Giochino del Sottomarino - Sottomarini e bombe ::'''

!!Il Sottomarino
Il '''Sottomarino''' è il nostro nemico. Per ora niente immagini, però possiamo comunque inventarci una classe ed infilarla nel gioco.

Così come la nave, il '''Sottomarino''' avrà una posizione, delle dimensioni ed una velocità:

private float x;
private float y;
private float speed;
private boolean active;
private Rectangle box;

'''active''' ci dice se il sottomarino è attivo o meno. A che cosa serve? Ora lo spiego.

La meccanica di gioco prevede che ogni tot secondi un sottomarino parta da un lato dello schermo ad una profondità variabile e con una velocità variabile. Potremmo quindi, ogni tot secondi, istanziare un nuovo sottomarino e farlo partire nel gioco. Non è una cosa sbagliata, non stiamo istanziando l'universo mondo e quindi non si tratterebbe di nulla di pesante. Però vogliamo usare un altro sistema, un po' differente, che ci permette di limitare anche il numero massimo di sottomarini che stanno contemporaneamente sullo schermo. Dei mille modi per ottenere ciò, qui ne presenterò uno.

Nel costruttore del sottomarino mettiamo dei valori di inizializzazione:

public Sottomarino() {
x = 0f;
y = 0f;
speed = 0f;

box = new Rectangle(0, 0, 100, 100);

active = false;
}

e ci premuriamo di lasciare il sottomarino inattivo. Per farlo partire ci serviamo di un altro metodo, '''start''', in cui gli passeremo posizione, dimensione e velocità.

public void start(float x, float y, float width, float height, float speed) {
this.x = x;
this.y = y;
this.speed = speed;
this.box.setBounds(x, y, width, height);
this.active = true;
}

Questo vuol dire che lo '''StatoGioco''', quando ci ha voglia, si inventerà delle dimensioni e una posizione e dirà ad un '''Sottomarino''' di assumere quella forma e di partire alla ventura.

Il metodo '''render''' è il solito, visto che per ora non ci sono immagini:

public void render(GameContainer container, StateBasedGame game, Graphics g) {
if (active) {
g.draw(box);
}
}

Nel metodo '''update''' invece dobbiamo tenere conto della '''speed''' per avere un controllo in più: se il sottomarino esce dallo schermo, deve essere disattivato.

public void update(GameContainer container, StateBasedGame game, int delta) {

if (active) {
x += speed * delta;

if ((speed > 0 && x + box.getWidth() > container.getWidth())
|| (speed < 0 && x < 0))
{
active = false;
Log.info("Sottomarino fuori schermo");
}

box.setLocation(x, y);
}
}

A seconda della '''speed''' la condizione per sapere se il '''Sottomarino''' è uscito dallo schermo è differente. La chiamata '''Log.info''' viene completata automaticamente con CTRL-SHIFT-I, e si tratta di una utility interna a Slick.

!!!I Sottomarini nello StatoGioco
Nello '''StatoGioco''' voglio avere una lista di '''Sottomarini''', e la dichiaro così:

private ArrayList<Sottomarino> nemici;

Nel metodo '''enter''' popolerò questa lista:

nemici = new ArrayList<Sottomarino>();

for (int i = 0; i < 20; i++) {
nemici.add(new Sottomarino());
}

In '''update''' mi preoccupo di aggiornare tutti i sottomarini:

for (Sottomarino s : nemici) {
s.update(container, game, delta);
}

e in '''render''' li disegnerò tutti:

for (Sottomarino s : nemici) {
s.render(container, game, g);
}

!!!Quando li faccio partire? Il Timer
Ma quando li faccio partire? Ogni tot tempo, si era detto. La cosa più semplice è la seguente: tenere via una variabile ed incrementarla con il valore di '''delta''' ad ogni giro. Se supera un certo valore, allora scateno un evento.

Questa è una cosa piuttosto comune e di facile implementazione. Ma voglio sfruttarla per introdurre una cosuccia che magari non tutti conoscono, e cioè la classe anonima.

Innanzitutto, decido di creare una nuova classe e di chiamarla '''Timer'''. Questa nuova classe contiene un valore di tempo da raggiungere ed il tempo trascorso finora, e viene aggiornata con il classico '''update'''. Per renderla un filo più completa deve anche essere possibile farla partire, fermarla, metterla in pausa. Quando il tempo è trascorso, il Timer deve automaticamente scatenare un certo evento.

Come faccio a fargli memorizzare un evento? Siccome siamo in Java, si tratterà di un oggetto. Decido che l'evento sia un'istanza dell'interfaccia '''Runnable'''. Viene usata di solito per i thread, e quindi è già esistente, è semplice e fa per noi. L'unico metodo che ha è '''run'''. Quello che succederà sarà quindi questo:
* creo un'istanza di '''Runnable'''
* nel metodo '''run''' ci metto il codice del mio evento
* passo il tutto al '''Timer''' e lo faccio partire
* aggiorno il timer nella '''update''' dello '''StatoGioco'''
* quando l'evento viene scatenato, faccio ripartire il '''Timer'''

Siccome la classe '''Timer''' è lunghetta, non la posto tutta, ma [[la allego -> Attach:classetimerjava.txt]].

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

----
[[!Guide]]