cerca
Giochino del Sottomarino
modifica cronologia stampa login logout

Wiki

Tools

Categorie

Help

edit SideBar

Giochino del Sottomarino

<< Configurazione del progetto con Netbeans | Giochino Sottomarino | Sottomarini e bombe >>

 :: Giochino del Sottomarino - Progetto ::

Update e Render

In linea generale e teorica, tutto ciò che "vive" all'interno di un videogioco può vedere la propria attività divisa in due parti distinte: la progressione della logica e la rappresentazione a schermo. Lo stesso GameState di Slick presenta questa dualità tramite i metodi che sono segnati così:

 public void update(GameContainer container, StateBasedGame game, int delta);
 public void render(GameContainer container, StateBasedGame game, Graphics g);

La classe Graphics è quella che si trova in org.newdawn.slick.Graphics. Se NetBeans ritiene altrimenti, non dategli retta:)

Update

In Update si aggiorna la logica dell'entità di gioco. Si analizza l'input, le eventuali condizioni ambientali, si eseguono i calcoli, si muta di stato, si stabilisce in base a tutto ciò e ad altro ancora la propria posizione sullo schermo.

Per ottenere questo obiettivo è necessario avere la nozione del tempo: quanto tempo è passato dall'ultima volta che ho dovuto prendere decisioni, in altre parole, dalla update scorsa?

La firma del metodo ci dice che c'è il parametro delta, il quale rappresenta il numero di millisecondi trascorso dall'ultimo update.

Nella costruzione dell'applicazione avevamo scritto:

 app.setVSync(true);

Questo significa quindi che, se tutto va liscio, tra un update e l'altro dovrebbe passare praticamente lo stesso numero di millisecondi. Nel caso di una frequenza di aggiornamento di 60Hz, allora avremo 1000 / 60 = 16 millisecondi circa. delta varrà 16.

Gli altri parametri della chiamata servono per recuperare informazioni sulla finestra di gioco etc. etc.

Render

Durante la render si disegna effettivamente a schermo. Si prenderanno le immagini etc. e in base alle informazioni trovate nella update (ma anche in base ad altri criteri) si deciderà che cosa disegnare, a schermo, e dove.

Facciamo il caso di un sottomarino nemico: nella update aggiornerà la propria posizione in funzione della sua velocità e del tempo trascorso. Nella render disegnerà la propria figura sullo schermo a seconda della posizione calcolata nella update

Entity

Dal momento che questi due metodi sono così importanti, ho deciso che ci occorre un'interfaccia che li racchiuda, e che tutte le entità del gioco debbano implementare questa interfaccia. Siccome parlo di entità, con molta fantasia l'interfaccia si chiamerà Entity:

 public interface Entity {
   public void update(GameContainer container, StateBasedGame game, int delta);
   public void render(GameContainer container, StateBasedGame game, Graphics g);
 }

La Nave

La Nave deve essere un'entità, e quindi implementerà l'interfaccia Entity.

Che cosa deve sapere una nave? Deve sapere la propria posizione, e deve sapere in che direzione sta andando, e anche a che velocità. La posizione della Nave verrà governata dal giocatore, in un modo che vedremo poi. Quindi, in astratto, alla nave diremo: "vai a destra", oppure: "vai a sinistra", o ancora: "fermati".

Alla luce di questa dettagliata descrizione, possiamo cominciare a buttare giù la nostra classe Nave

 public class Nave implements Entity {

   private float x;
   private float y;
   private float speed;

Quando costruisco la Nave le dico dove deve posizionarsi nel mondo, in coordinate schermo.

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

   this.speed = 0f;
 }

Poi ci metto i metodi che servono per governare la Nave dall'esterno:

 public void goLeft() {
   this.speed = -100f / 1000f;
 }

 public void goRight() {
   this.speed = 100f / 1000f
 }

 public void stop() {
   this.speed = 0f;
 }

Quando dico alla Nave di fermarsi, chiamo stop e ok, la velocità viene impostata a 0. Se invece le dico di andare a sinistra o a destra, la velocità è settata in modo diverso.

Per esempio, per andare a destra la velocità è data da 100f / 1000f. Velocità = spazio / tempo, quindi vuol dire che la nave percorrerà 100 pixel nello spazio di 1000 millisecondi (1 secondo). Prima infatti dicevo che il metodo update passa il delta in millisecondi.

Passiamo allora al metodo update:

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

   x += speed * delta;

 }

Facile, no? Moltiplico per il tempo ed ottengo lo spostamento dall'ultima update.

E come la disegno?

Già, come la disegno? In attesa che trovi delle immagini adatte, ho deciso di sfruttare le forme geometriche che Slick ci offre.

Occorre dichiarare una nuova variabile:

 private Rectangle box;

che chiamo box perché poi sarà usata come una scatola. CTRL-SHIFT-I dovrebbe far importare automaticamente questo file, dove è definito il Rectangle:

 import org.newdawn.slick.geom.Rectangle;

Nel costruttore aggiungo sta riga:

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

in cui dico che la box partirà da x e y, passati come parametro al costruttore; sarà larga 200 e alta 32. Sto praticamente duplicando le variabili x e y, ma forse poi ci verranno buone così.

Nella update aggiungo questa riga:

 box.setLocation(x, y);

e così facendo aggiorno la posizione della scatola in funzione di x e y calcolati nell'update. Beh, in realtà solo la x viene calcolata, la y per ora rimane ferma.

Quindi, per disegnarla, vado nel metodo render e aggiungo sta riga:

 g.draw(box);

et voilà!

Usiamo la Nave

Torniamo al nostro StatoGioco. Ci creiamo una variabile

 private Nave player;

Andiamo al metodo enter e tiriamo su la Nave:

 player = new Nave(100, 100);

posizionandola quindi nel punto 100, 100 dello schermo

Nel metodo update dello StatoGioco chiamiamo la update della Nave:

 player.update(container, game, delta);

Nel metodo render chiamiamo la render della Nave:

 player.render(container, game, g);

Se facciamo partire il progetto, vedremo disegnato un rettangolo di 200x32 pixel nella posizione 100, 100:) Ovviamente ancora non si muove. Per prova, provate a mettere la speed iniziale della nave ad un valore diverso da 0 per vederla muovere:)

La Nave la voglio muovere io

Ok. Torniamo allo StatoGioco. Creiamo una variabile così:

 private Input input;

e nel metodo setInput mettiamo sta riga:

 this.input = input;

Così facendo abbiamo detto allo stato di gioco di tenere via un riferimento al sistema di input fornito dalla libreria Slick.

L'idea basica del movimento è questa: se premo la freccia a destra, muovo la nave a destra. Se premo la freccia a sinistra, muovo la nave a sinistra. Se non premo niente, la nave sta ferma dov'è.

Andiamo nel metodo update dello StatoGioco. Come facciamo a sapere se un tasto è premuto? Così:

 if (input.isKeyDown(Input.KEY_LEFT)) {
    player.goLeft();
 } else if (input.isKeyDown(Input.KEY_RIGHT)) {
    player.goRight();
 } else {
    player.stop();
 }

Stiamo chiedendo al sistema di input lo stato del tasto KEY_LEFT e lo stato del tasto KEY_RIGHT, e facciamo esattamente quello che volevamo. Provate adesso a fare andare il gioco e vedrete che muoverete la nave con le freccette:)

<< Configurazione del progetto con Netbeans | Giochino Sottomarino | Sottomarini e bombe >>


Guide