<< Collisioni | Giochino Sottomarino | Esplosioni e oscillazioni >>
:: Giochino del Sottomarino - Immagini ::
Un po' di grafica
Attenzione: ho realizzato alcune immagini, ma non sono esattamente belle. Spero servano a rendere l'idea. Se poi qualcuno di più dotato decide di collaborare, ne sarei ben felice:)
Dove mettere le immagini
Nella cartella del nostro progetto creiamo una nuova cartella, chiamandola data. Ad esempio, se il nostro progetto si trovava in
/home/dario/NetBeansProject/Sottomarino
la cartella sarà
/home/dario/NetBeansProject/Sottomarino/data
Image
La classe Image fornita da Slick è il sistema attraverso cui passa il caricamento di immagini da files, il loro trasferimento in memoria video e il loro successivo rendering.
La costruzione di un'Image può causare una SlickException. NetBeans avviserà, e l'opzione che scelgo è di solito quella di far aggiungere a NetBeans la clausola relativa all'eccezione. In questo modo se qualcosa va storto il programma si pianta. Non è il massimo della vita, in realtà, però soprattutto in questa fase è bello sapere che le cose non stanno funzionando, così le sistemiamo:)
Per disegnare un'immagine usiamo il metodo draw. Ce ne sono varie versioni. Quella basica permette di specificare le coordinate x e y da cui far partire il disegno. Le ascisse vanno da sinistra a destra, mentre le ordinate dall'alto in basso. Quindi, y = 0 corrisponde alla cima dello schermo.
La Nave
La nave
La Nave che ho realizzato ha un bel fiocchetto in stile Hello Denis. Salvate l'immagine qui a sinistra in /data e chiamatela nave.png.
Andiamo nella classe Nave. Aggiungiamo due variabili, una per la Nave in direzione "normale", cioè rivolta a destra, e una per la Nave rivolta a sinistra.
private Image naveDx;
private Image naveSx;
Nel costruttore di Nave mettiamo
naveDx = new Image("./data/nave.png");
naveSx = naveDx.getFlippedCopy(true, false);
Con la prima riga diciamo a Slick di caricare l'immagine nave.png dalla cartella /data a partire dalla cartella base del progetto. La naveSx è una copia invertita di naveDx. I due parametri sono due booleani che dicono se è invertita sull'asse delle X, e se invertita sull'asse delle Y. Provate per vedere quello che esce.
Quello che internamente Slick fa è di tenere via in memoria video una sola copia dell'immagine caricata. Le Image create a partire da questa possono essere viste come delle modalità di accesso a questa texture. Quindi non c'è nessun problema a caricare più copie della stessa immagine, cosa che accadrà con le bombe.
Dimensioni della nave
Ovviamente le dimensioni della Nave ora dovranno essere quelle dell'immagine caricata. Di conseguenza, anche la Box dovrà avere dimensioni diverse.
box = new Rectangle(x, y, naveDx.getWidth(), naveDx.getHeight());
Direzione della Nave
Per sapere quale nave disegnare, introduciamo un nuovo booleano in Nave:
private boolean direction;
e stabiliamo che se andiamo a destra lo mettiamo a true, se andiamo a sinistra lo mettiamo a false.
public void goLeft() {
direction = false;
this.speed = maxSpeed;
}
public void goRight() {
direction = true;
this.speed = maxSpeed;
}
Non ricordo se avevo già introdotto la variabile maxSpeed, ma comunque è un float e nel costruttore lo mettiamo così:
maxSpeed = 50.0f / 1000.0f;
che vuol dire che la nostra nave farà 50 pixel al secondo. Wow!
Andiamo al metodo update. Per sapere in che direzione sta andando la Nave, usiamo la variabile direction:
int dirMult = (direction ? 1 : -1);
x += speed * delta * dirMult;
if ((x + box.getWidth()) > container.getWidth()) {
x = container.getWidth() - box.getWidth();
}
if (x < 0) {
x = 0;
}
Questo codice controlla anche che la Nave non esca dallo schermo. Lasciamo com'era la riga che aggiornava la posizione della Box.
Ma a che serve dirMult? In effetti, per ora a niente. Maggiori info nelle prossime puntate:)
Passiamo al metodo render:
if (direction) {
naveDx.draw(box.getX(), box.getY());
} else {
naveSx.draw(box.getX(), box.getY());
}
Domanda: se uso la Box per avere le coordinate, a che mi servono le variabili x e y? Boh, per ora mi pare che non servano, penso si possano togliere:)
Ad ogni modo, così facendo la Nave viene disegnata nell'ultima direzione che aveva prima di fermarsi. Questo perché quando la fermiamo mediante stop la direction non viene modificata.
I Sottomarini
Il sottomarino
Ecco il fantastico sottomarino. Mamma mia. Salvate quest'immagine in ./data chiamandola sottomarino.png.
Andiamo alla classe Sottomarino. Stavolta lo lascio come esercizio: ci servono due Image, una sottomarinoDx e una sottomarinoSx, esattamente come per la Nave.
Dobbiamo anche stravolgere del vecchio codice: la Box ora avrà dimensioni fisse, che saranno quelle dell'immagine del Sottomarino. Precedentemente era la classe StatoGioco a stabilire le dimensioni del Sottomarino. Adesso cambiamo le carte in tavola:
box = new Rectangle(0, 0, sottomarinoDx.getWidth(), sottomarinoDx.getHeight());
Di conseguenza, il metodo start sarà differente:
public void start(float x, float y, float speed) {
this.x = x;
this.y = y;
this.speed = speed;
this.box.setLocation(x, y);
active = true;
}
Anche qui mi chiedo a che cosa serva il duplicato delle coordinate x e y. Boh, magari più in là disaccoppiamo grafica e bounding box o roba simile, e allora verrebbero buone.
Dal momento che abbiamo modificato start, NetBeans diligentemente ci informerà di errori in StatoGioco. Andiamo lì e modifichiamo il metodo attivaSottomarino, levando dalle balle le variabili width ed height.
Le Bombe
La bomba
Anche la Bomba ha il fiocchetto:) Solito discorso: salviamo l'immagine bomba.png in ./data.
Nella classe Bomba aggiungiamo un'Image:
private Image bomba;
Stavolta non ci serve la copia invertita, perché la Bomba ha una sola direzione di movimento. Nel costruttore usiamo queste due righe:
bomba = new Image("./data/bomba.png");
this.box = new Rectangle(x, y, bomba.getWidth(), bomba.getHeight());
Anche qui vale il discorso fatto precedentemente: Slick carica una sola versione dell'immagine bomba.png in memoria, quindi nessun problema anche se abbiamo 10 Bombe e 20 Sottomarini.
In render il codice sarà così:
if (active) {
bomba.draw(x, y);
}
Il fondo marino
Il fondo marino
Ho anche realizzato questo interessantissimo fondo marino. Salviamo l'immagine qui a sinistra in ./data/fondo marino.png.
Costruiamo una classe apposita per il Mare, perché magari poi ci aggiungiamo altre cose tipo i pesci o roba simile.
La classe la riporto qui intera, che tanto è piccola ed autoesplicativa:
public class Mare implements Entity {
private Image fondo;
public Mare() throws SlickException {
fondo = new Image("./data/fondo marino.png");
}
public void update(GameContainer container, StateBasedGame game, int delta) {
// Per ora non faccio nulla
}
public void render(GameContainer container, StateBasedGame game, Graphics g) {
fondo.draw(0, container.getHeight() - fondo.getHeight());
}
public Rectangle getBoundingBox() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
Il metodo getBoundingBox lancia un'eccezione, tanto non penso lo chiameremo mai. Tanto sapete come costruire una Box a partire dalle dimensioni di un'immagine.
Usare il Mare
In StatoGioco, aggiungeremo una variabile:
private Mare mare;
e lo costruiamo in enter. In realtà non è necessario, lo si può costruire anche nella init:
mare = new Mare();
Tanto per essere precisi, nella update aggiorniamo anche il Mare:
mare.update(container, game, delta);
Nella render, invece, vogliamo disegnare il Mare sotto a tutti gli altri. Quindi mettiamo la chiamata prima di tutte le altre:
mare.render(container, game, g);
Il colore del cielo
Come ultima cosa, andiamo in Applicazione e, nel metodo initStatesList aggiungiamo questa riga:
container.getGraphics().setBackground(new Color(154, 226, 255));
In questo modo tutto ad ogni giro, prima di disegnare qualsiasi altra cosa, Slick provvederà a cancellare lo schermo con l'azzurrino specificato dalla terna RGB che vedete qui sopra. Poi noi ci disegniamo sopra il Mare, e infine la Nave, i Sottomarini e le Bombe.
Nota
Mi sono accorto che, nella initStatesList, non c'è bisogno di chiamare la this.enterState(0), perché per qualche ragione si entra automaticamente nello stato StatoGioco quando viene aggiunto. Investigherò!
<< Collisioni | Giochino Sottomarino | Esplosioni e oscillazioni >>
Guide