Creare videogiochi con java (parte 1)

In questo primo di una serie di articoli andremo a studiare le basi della libreria GTGE, una libreria java completamente gratuita per creare videogiochi 2d portabili e di impatto. La libreria è distribuita sotto la Creative Commons License

GTGE (Golden T Game Engine) è una libreria per creare giochi 2D di qualità che sfrutta tutte le caratteristiche del linguaggio java, semplicità e portabilità in primis. GTGE utilizza infatti le API standard del linguaggio di SUN e ne rende ancora più semplice l’utilizzo. Chiunque abbia un minimo di esperienza con java riuscirà ad ottenere in poco tempo ottimi risultati con il minimo sforzo!

GTGE è una libreria molto vasta e difficilmente in questa serie di articoli riuscirò ad illustrare tutto. Vi consiglio perciò di scaricare la documentazione ufficiale, veramente completa e generalmente ben fatta. Sempre sul sito ufficiale trovate poi una serie di giochi (creati con GTGE, ovviamente) di cui potete liberamente scaricare i sorgenti. Studiare i sorgenti di altri giochi è il modo migliore per capire come funzionano e per imparare tecniche e trucchi di programmazione da usare nel proprio codice (senza però cadere nel plagio, occhio! 😉 ).

In questo primo appuntamento vedremo le basi di GTGE con diversi esempi di codice. Innanzitutto scaricatevi la libreria da http://www.goldenstudios.or.id. Ne trovate diverse versione, io vi consiglio l’archivio “tutto in uno”, che comprende la libreria, la documentazione e una serie di tutorial. Dopo averlo scompattato cercate il file golden_0_2_3.jar (al posto di 0, 2 e 3 potrebbero esserci altri numeri, a seconda della versione scaricata). Per compilare ed eseguire programmi che usano GTGE dovrete ‘far vedere’ questo file jar al compilatore ed alla java virtual machine (almeno in versione 1.4). Se usate un IDE (Integrated Development Environment) come JBuilder, basterà andare in

ed indicargli dove trovare la libreria. Eclipse non l’ho mai usato ma presumo che il meccanismo sia simile. Se invece volete compilare ed eseguire da linea di comando, potete aggiungere la libreria impostando correttamente il classpath:

Attenzione! Se usate linux, probabilmente lanciando i precedenti comandi otterrete qualche errore. Questo perchè il carattere ; (punto e virgola) in linux serve a separare più comandi lanciabili con la stessa istruzione. In questo caso, usate il carattere : (due punti):

N.B.: In questa serie di articoli non si illustra java da zero, quindi un minimo di conoscenza dovrete già averla. In particolare, dovete già conoscere la sintassi di Java e la programmazione ad oggetti.

 

Struttura della libreria

Le varie classi che compongono GTGE sono suddivise in diversi package e sotto package. I package principali, dentro i quali si sviluppa tutta la libreria, sono 3:

  1. com.golden.gamedev: questo package contiene il cuore di GTGE, cioè tutte quelle classi che inizializzano l’ambiente di gioco e che si occupano della visualizzazione e dell’aggiornamento dello schermo.
  2. com.golden.gamedev.engine: questo package contiene i riferimenti agli engine (letteralmente ‘motori’) audio, video, ecc. Permettono ad esempio di rilevare il click del mouse o la pressione di un tasto della tastiera, accedere a risorse esterne (come file con l’animazione da caricare) ed ad altre utili funzionalità.
  3. com.golden.gamedev.object: questo package infine, contiene le classi di tutti quegli oggetti visualizzabili sullo schermo come sprite (animati e non), sfondi, gruppi di sprite ecc. Si trovano inoltre classi che semplificano la gestione della mappa ed il rilevamento delle collisioni.

 

Classe principale del gioco

Prima di cominciare a scrivere codice, analizziamo un momento la classe com.golden.gamedev.Game. La classe principale di ogni nostro gioco dovrà necessariamente estendere Game. Poichè questa è una classe astratta, dovremo definire tutti i suoi metodi astratti, che sono:

  • public abstract void initResources(): in questo metodo vengono caricate tutte le risorse necessarie al gioco, come immagini, file audio ecc. e vengono inizializzati i vari oggetti. Viene chiamato una sola volta, appena la classe viene istanziata.
  • public abstract void update(long elapsedTime): si occupa di aggiornare in tempo reale il gioco, aggiornando la posizione degli oggetti, cambiando il frame di un animazione ecc.. Il valore long passato indica i millisecondi che devono passare tra un aggiornamento ed il successivo.
  • public abstract void render(Graphics2D g): questo metodo si occupa di mettere tutto a schermo. Il doppio buffering è già implementato.

 

L’esecuzione del gioco passa attraverso questi passaggi:

In realtà oltre a questi metodi ne vengono chiamati molti altri automaticamente, ma di questo non ci interessiamo. L’importante è sapere come funziona il ciclo principale del gioco, in modo da evitare di scrivere porcherie 😛 .

 

La prima esecuzione

GTGE può far eseguire il vostro gioco in 3 modalità

  1. a schermo intero (modalità consigliata)
  2. in una finestra non ridimensionabile
  3. all’interno di un’applet

Per il momento lasciamo da parte l’applet. Come ogni programma java, anche il nostro gioco inizierà la sua esecuzione dal metodo main. Contrariamente a quanto ci si potrebbe aspettare, per giocare non basta istanziare la sotto classe di Game creata, ma occorrerà un passaggio in più, come si può vedere in questo esempio:

L’oggetto gameLoader si occupa di caricare il gioco, avviando il motore grafico e tutto il necessario. Il metodo setup accetta tre parametri: un istanza della sotto classe di Game che abbiamo appena definito, la risoluzione del gioco (in questo caso 640*480) e un valore booleano. Se questo booleano vale true, il gioco è avviato in modalità schermo intero, altrimenti in modalità finestra.  Dopo aver fatto questo, si può far partire il gioco vero e proprio chiamando il metodo start.

N.B.: nel caso la modalità schermo intero non sia supportata, il gioco verrà automaticamente messo in una finestra dopo aver segnalato il problema all’utente.

Provate a compilare ed eseguire il codice qui sopra: il risultato è una finestra vuota di 640*480 pixel.

 

Sprite

In GTGE uno Sprite è un oggetto a cui è associata un’immagine e una serie di caratteristiche, come la posizione, la dimensione, l’eventuale velocità di spostamento ecc.. Ne esistono diversi tipi:

  • com.golden.gamedev.object.Sprite: è la versione più semplice di Sprite, rappresenta un oggetto non animato che tuttavia può essere mosso.
  • com.golden.gamedev.object.AnimatedSprite: rappresenta uno Sprite formato da una serie di immagini che formano l’animazione. Estende Sprite.
  • com.golden.gamedev.object.sprite.AdvanceSprite: rappresenta sempre uno Sprite animato ma offre funzionalità avanzate per la gestione delle animazioni. Estende AnimatedSprite.
  • com.golden.gamedev.object.sprite.VolatileSprite: sempre uno Sprite animato, ma stavolta l’animazione viene visualizzata un’unica volta anzichè essere mandata in loop. E’ particolarmente utile nel caso di esplosioni o simili. Estende AdvanceSprite.

 

Vediamo adesso come inserire uno sprite nel nostro gioco. Per il momento vediamo solo la classe Sprite, nei prossimi appuntamenti analizzeremo anche gli sprite animati.

Innanzitutto dobbiamo procuraci un immagine (formati supportati: png, gif e jpg). Nella cartella tutorials/src/resources presente nell’archivio scaricato dal sito di GTGE trovate alcuni file (sia immagini che audio) che potranno tornarvi utili. In questa serie di articoli farò spesso riferimento a quei file.

Sprite ha diversi costruttori. Questo è quello che verrà utilizzato praticamente sempre:

dove:

  • image è un riferimento all’oggetto che contiene l’immagine da mettere a schermo.
  • x e y rappresentano il punto a partire dal quale verrà visualizzata l’immagine. L’angolo in alto a sinistra del monitor è il punto (0, 0).

 

Per caricare un immagine dobbiamo usare il metodo getImage, ereditato da Game. L’unico parametro è una stringa con il percorso (path) dell’immagine da caricare. Se il path non è valido oppure il file “puntato” non è un immagine verrà lanciata un’eccezione ed il gioco verrà terminato.

Dopo aver creato lo Sprite (all’interno di initResources) dobbiamo fare in modo che venga di volta in volta aggiornato e visualizzato. Per fare questo dobbiamo fargli chiamare i metodi update e render. Questi metodi non sono gli stessi di Game, anche se la firma è identica. Tutto ciò che vogliamo far visualizzare deve chiamare i propri update e render all’interno dei metodi omonimi della sotto classe di Game che abbiamo creato.

Se abbiamo molti Sprite (ma anche altri oggetti) da gestire, questo metodo può risultare abbastanza noioso, complesso e ridondante e potrebbe essere causa di errori poi difficili da scoprire (è facile dimenticarsi di fare il render di uno Sprite quando ne stiamo gestendo qualche centinaio!). Più avanti vedremo come semplificarci la vita, raggruppando i vari Sprite in gruppi (gruppi che ci serviranno anche per gestire le collisioni).

Ecco intanto come inserire un semplice Sprite nel nostro gioco:

Se provate a compilare ed eseguire questo codice, noterete che lo sfondo dell’area di gioco risulterà spesso “rovinato”. Questo perchè non abbiamo ancora impostato uno sfondo vero e proprio e pertanto l’engine si arrangerà mostrando ciò che sta “sotto” la finestra. Vediamo adesso come rimediare a questo “inconveniente”.

 

Background

La classe com.golden.gamedev.object.Background rappresenta un generico “sfondo” per il nostro videogioco. Tutti gli Sprite (ed altri oggetti) che fanno parte della stessa area di gioco dovrebbero sempre essere associati ad un oggetto Background. Ad un semplice oggetto Background però, non possiamo associare un colore od un immagine: praticamente… e’ inutile!

In realtà’ non è così’. Da questa classe infatti derivano diverse altre classi “XXXBackground” che si adattano bene alle diverse esigenze del programmatore. Alcune di queste classi sono:

  • com.golden.gamedev.object.background.ColorBackground: semplice sfondo, riempito con un unico colore.
  • com.golden.gamedev.object.background.ImageBackground: usa come sfondo un immagine.
  • com.golden.gamedev.object.background.TileBackground: questo sfondo suddivide la mappa in “piastrelle” (tile), ognuna delle quali ammette più livelli di oggetti. Ogni “piastrella” è un pezzo di immagine.
  • com.golden.gamedev.object.background.ParallaxBackground: questo è uno sfondo creato sovrapponendo diversi altri sfondi.

 

Tutte queste classi estendono (direttamente od indirettamente) Background. Vediamo un semplice esempio di utilizzo di uno sfondo:

In questo esempio l’oggetto _background viene impostato come “sfondo” di _sprite attraverso il metodo setBackground. In questo modo possiamo gestire più facilmente gli sfondi che rappresentano una zona di gioco maggiore della singola schermata. Successivamente tornerò meglio su questo punto.

 

Rilevare i tasti premuti (mouse e tastiera)

La classe Game fornisce metodi per rilevare sia la pressione di un tasto sulla tastiera sia i click del mouse. Questi metodi sono:

  • public boolean keyDown(int keyCode): restituisce true fintanto che il tasto indicato rimane premuto.
  • public boolean keyPressed(int keyCode): restituisce true se il tasto indicato è stato premuto una volta, non importa se poi rimane premuto.
  • public boolean click(): restituisce true se l’utente ha cliccato col tasto sinistro del mouse.
  • public boolean rightClick(): restituisce true se l’utente ha cliccato col tasto destro del mouse.

 

Dopo aver rilevato il click del mouse possiamo chiamare getMouseX e getMouseY (ereditati da Game) per ottenere le coordinate del punto in cui si trovava il cursore al momento del click. Ogni tasto è identificato in java da una costante intera. Questa costante dovrà essere passata a keyDown e/o keyPressed, che restituiranno true se il tasto identificato da quell’intero è (stato) premuto.

Queste costanti si trovano nella classe java.awt.event.KeyEvent. Se per esempio vogliamo far sparare il personaggio del nostro gioco alla pressione della barra spaziatrice, dovremo fare:

Alcune costanti sono:

  • VK_UP: freccia su
  • VK_DOWN: freccia giù
  • VK_LEFT: freccia sinistra
  • VK_RIGHT: freccia destra
  • VK_SPACE: barra spaziatrice
  • VK_CONTROL: tasto CTRL (sia destro che sinistro)
  • VK_ALT: tasto ALT
  • VK_ESCAPE: tasto ESC
  • VK_M: tasto M
  • VK_7: tasto 7 (non del tastierino numerico)

 

Per l’elenco completo date un’occhiata alla documentazione della classe KeyEvent.

 

Muovere uno Sprite

La classe Sprite mette a disposizione diversi metodi per muovere gli oggetti sullo schermo. I più utilizzati sono sostanzialmente questi due:

  • public void setSpeed(double vx, double vy): imposta la velocità di movimento dello Sprite. Un valore positivo di vx fa muovere lo Sprite verso destra, un valore positivo di vy lo fa muovere verso il basso. Valori negativi lo fanno andare verso sinistra o verso l’alto.
  • public void move(double dx, double dy): muove lo Sprite di dx pixel in orizzontale e dy pixel in verticale. Per il segno dei parametri, valgono le considerazione di setSpeed

 

La differenza tra i due metodi è che move muove lo Sprite dei pixel indicati e poi questo si ferma mentre setSpeed continua a muovere lo Sprite fino a che entrambe le velocità vx e vy non vengono azzerate (quindi il movimento continuerà anche se smettete di premere il bottone, a meno che non abbiate dato le istruzioni del caso).

Per orientarvi sul valore da assegnare a vx e vy tenete conto di questa proporzione: se una velocità viene impostata a n significa che lo sprite si muoverà di (n * 1000) pixel al secondo in quella direzione. Esistono numerosi altri metodi che gestiscono il movimento degli Sprite: alcuni utilizzano gli angoli, altri aumentano/diminuiscono una od entrambe le velocità ecc.. Se volete approfondire, date un’occhiata alla documentazione ufficiale.

Ecco un semplice esempio:

 

Conclusioni

In questo primo appuntamento abbiamo visto le basi della creazione di videogiochi con GTGE: struttura della libreria, Sprite e loro semplice gestione, rilevamento dei tasti premuti. Nel prossimo articolo vedremo come creare Sprite animati (con le classi AnimatedSprite ed AdvanceSprite) e come gestire le collisioni.
Alla prossima allora! 🙂

Una risposta

  1. aprile 8, 2013

    […] programmazione di videogiochi 2D con java. Dopo aver visto un’introduzione generale a GTGE nel primo articolo, in questo secondo appuntamento vedremo come creare degli Sprite animati e come inserire dei […]

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *