Lo sviluppo di giochi in ambiente Android
Nel progredire del mondo mobile, il settore di più grande impatto è stato quello videoludico. Attualmente, i giochi per dispositivi mobili rappresentano il 33% delle applicazioni scaricate ed il 66% del fatturato registrato sui marketplaces.
Il mercato videoludico ha fornito in questi anni un contributo rilevante alla divulgazione su larga scala delle tecnologie dell’ informazione e della comunicazione. Per le generazioni più giovani, infatti, il videogioco rappresenta spesso il primo modo per avvicinarsi all’informatica.
Lo sviluppo di un videogioco è caratterizzato dall’utilizzo massiccio di effetti grafici e sonori ed un elevato livello di interattività con l’utente, al fine di catturare parzialmente o completamente l’attenzione di quest’ultimo per scopi ludici e di divertimento.
Per potersi cimentare in questa impresa è necessario conoscere la piattaforma Android e dare uno sguardo alla sua architettura. Fatto ciò risulta molto utile gestire la creazione di un gioco con i game-engine più comuni.
Un game engine è un frame work software progettato specificamente per la creazione e lo sviluppo di videogiochi. Esso è il cuore del gioco e la scelta del game engine è una delle fasi più importanti dello sviluppo di un videogame.
Dal motore di gioco dipende una serie di caratteristiche che il gioco può possedere o meno come, meccanismi di rendering per la grafica 2D o 3D, meccanismi per l’implementazione della fisica di gioco, meccanismi per rilevare le collisioni fra diversi oggetti. Alcuni dei framework più utilizzati fra gli sviluppatori di videogame sono:
jMonkeyEngine
AndEngine
Libgdx
jMonkeyEngine è un game engine open-source utilizzato per la realizzazione di giochi 3D in linguaggio Java. Questo motore grafico fa uso delle librerie Java LWJGL (Lightweight Java Game Library), per permettere agli sviluppatori di accedere ad OpenGL (Open Graphics Library), OpenCL (OpenComputing Language) ed OpenAL (Open Audio Library).
Per sfruttare appieno le librerie jMonkeyEngine si fa uso del suo ambiente di sviluppo ideale: l’SDK (Software Development Kit) jMonkeyEngine. Esso è basato sulla piattaforma NetBeans e fornisce i plugin unici per la creazione di contenuti di gioco. Essendo derivato da NetBeans ha accesso a tutti gli strumenti di sviluppo di quest’ultimo.
Uno degli aspetti più interessanti di jMonkeyEngine è il fatto di poter realizzare applicazioni non soltanto per i dispositivi Android ma anche per altre piattaforme come desktop e Web.
Come jMonkeyEngine, anche AndEngine è un game engine open-source utilizzato per la realizzazione di giochi ma, a differenza del primo, esso consente esclusivamente la grafica 2D. La libreria AndEngine permette anche a persone inesperte il facile apprendimento nello sviluppo di un videogioco ma, come controparte, le API non offrono ampie possibilità di personalizzazione come invece accade per altre librerie quali Libgdx.
AndEngine supporta le seguenti estensioni che forniscono diverse funzionalità:
- PhysicsBox2DExtension, che permette l’uso della fisica Box2D.
- TMXTiledMapExtension, che permette la lettura dei file generati in formato TMX per la realizzazione di mappe.
- MultiplayerExtension, supporta il multiplayer.
A differenza dei primi due, Libgdx non è un engine bensì è un frame work open-source per la realizzazione di giochi sia 2D sia 3D ed una delle sue caratteristiche principali è la possibilità di sviluppare giochi per diverse piattaforme di destinazione. Attualmente supporta Windows, Linux, Mac OSX, Android, iOS e HTML5. Sostanzialmente è possibile scrivere il codice una sola volta ed esportarlo sulle diverse piattaforme senza alcuna modifica.
Libgdx è progettato per dare allo sviluppatore libero accesso a tutto ciò di cui ha bisogno: file system, dispositivi di input, dispositivi audio ed OpenGL tramite un’ interfaccia OpenGL ES 2.0 e 3.0. Ha un set di API che aiutano nelle comuni attività di sviluppo di un gioco: rendering di sprite, costruzione di interfacce utente, riproduzione di effetti sonori.
Nel successivo paragrafo si effettuerà un’analisi più dettagliata di Libgdx.
Libgdx è un framework open-source per lo sviluppo cross-platform di giochi su tecnologia Java. In realtà, oltre a Java, il framework fa uso anche del linguaggio C sia per compiere quei compiti che sono critici per le performance, sia per incorporare librerie C di terze parti che forniscono tool di supporto.
Una delle caratteristiche principali di Libgdx è la possibilità di esportare il proprio progetto su diverse piattaforme. Per fare ciò fornisce un’API comune che astrae completamente i dettagli implementativi delle varie piattaforme supportate che attualmente sono: Windows, Linux, Mac OS X, Android, iOS e HTML5.
Libgdx non è un game engine. I game engine sono composti, generalmente, da una insieme di tool che vincolano lo sviluppatore. In questo modo Libgdx lascia allo sviluppatore una totale libertà sugli strumenti da utilizzare ed offre la possibilità di utilizzare operazioni di basso livello qualora fosse necessario.
Quando si crea un’applicazione con Libgdx si vanno ad implementare N progetti di cui un progetto core che sfrutta l’API fornita e che contiene gran parte del codice, ed N-1 progetti, uno per ogni piattaforma utilizzata, che contengono la restante parte di codice utile all’avvio dell’applicazione sulle diverse piattaforme.
Con Libgdx è possibile eseguire e debuggare il codice su desktop come una normale applicazione Java. Ciò ha un duplice vantaggio:
- Consente l’utilizzo di tutti gli strumenti e delle funzionalità supportate dalla Java Virtual Machine
- Riduce sensibilmente i tempi di sviluppo e di debug.
Per fare in modo che si possa scrivere un unico codice di base, però, è necessario che tutti i progetti abbiano libero accesso alle risorse che dovranno essere utilizzate. Abbiamo anche detto che la maggior parte del codice di un’applicazione Libgdx è scritta nel progetto core e, quindi, si potrebbe pensare di avere una directory interna a questo progetto contenente suoni, immagini e quant’altro possa essere condiviso fra le varie piattaforme. Tuttavia Android ha delle regole molto ferree per quanto riguarda l’utilizzo delle risorse e per tale motivo si utilizza la cartella assets del progetto Android per la condivisione dei dati.
I legami fra i diversi progetti sono gestiti tramite Gradle. Gradle è un sistema di creazione (build) e gestione (management) delle dipendenze. La gestione delle dipendenze avviene tramite un file che specifica i nomi e le versioni delle librerie che uno sviluppatore ha bisogno di avere nella propria applicazione. L’aggiunta, la rimozione o la modifica della versione di una libreria può essere ottenuta semplicemente modificando alcune righe di questo file. Il sistema di build aiuta con la creazione della propria applicazione e non è legato al particolare IDE (Integrated DevelopmentEnvironment) utilizzato.
Prima di Gradle era necessario configurare libgdx ogni volta che si creava un nuovo progetto. Tale configurazione consisteva di una serie di passi, per caricare le librerie e per creare le dipendenze fra i diversi progetti, che potevano costituire un procedimento abbastanza complesso e spesso fonte di errori. Il sistema di build di Gradle automatizza questo procedimento e permette a tutte le piattaforme di condividere la cartella assets del progetto Android.
Un’applicazione in Libgdx ha un ciclo di vita ben definito che permette allo sviluppatore di avere un controllo totale sugli stati dell’applicazione stessa, quali: creazione, sospensione e riavvio, raffigurazione delle immagini e smaltimento delle risorse utilizzate. Tale ciclo di vita è rappresentato nella figura seguente:
Per gestire gli eventi che cambiano lo stato di un’applicazione, lo sviluppatore non deve far altro che creare una classe Java che implementa l’ interfaccia ApplicationListener di Libgdx. Questa contiene tutti i metodi necessari allo scopo:
- create(), viene chiamato una sola volta quando l’applicazione è creata. Quando ciò accade l’applicazione carica tutto ciò di cui ha bisogno per permettere all’utente di giocare: texture, sprite, spritebatch, stage…
- resize(int width, int height), viene chiamato ogni volta che lo schermo del gioco viene ridimensionato ed il gioco non è nello stato di pausa. Accetta due parametri, width ed height, che rappresentano rispettivamente i nuovi valori di larghezza ed altezza dello schermo
- render(), è un ciclo infinito che viene ripetutamente chiamato dall’applicazione durante la fase di gioco. Termina solo quando l’applicazione stessa termina. Gli aggiornamenti logici, come la nuova posizione sullo schermo che deve assumere un’immagine, vengono inseriti in questo metodo
- pause(), è un metodo che serve a gestire lo stato di sospensione del gioco e viene invocato in modi diversi in funzione della piattaforma di destinazione. In Android, viene invocato quando è premuto il tasto Home o se è in arrivo una chiamata; nelle piattaforme desktop, invece, viene invocato prima della chiamata al metodo dispose() ovvero quando l’applicazione deve essere terminata
- resume(), è utilizzato solo nella piattaforma Android e permette il riavvio se l’applicazione si trovava precedentemente nello stato di pausa
- dispose(), viene invocato quando l’applicazione deve terminare e serve per liberare memoria
Libgdx è composto da diversi moduli ognuno dei quali fornisce un diverso servizio per ogni step di una tipica architettura di gioco. La figura seguente mostra questi moduli.
- Input, fornisce un modello per la gestione dei dispositivi di input su tutte le Supporta tastiera, touchscreen, accelerometro e, dove disponibile, mouse.
- Graphics, consente il disegno delle immagini sullo schermo tramite OpenGL ES.
- Files, consente l’accesso ai file su tutte le piattaforme.
- Audio, facilita la registrazione e la riproduzione di suoni su tutte le piattaforme.
- Networking, fornisce i metodi per eseguire operazioni di rete, come ad esempio semplici richieste HTTP o comunicazione client/server con Socket TCP.
Le diverse piattaforme di destinazione di un progetto in Libgdx presentano diversi dispositivi di input. Sui dispositivi desktop l’utente invia comandi tramite tastiera o mouse; sui dispositivi Android, invece, il mouse viene sostituito da un touchscreen capacitivo e la tastiera spesso manca. Inoltre, i dispositivi Android spesso dispongono anche di un accelerometro e di un sensore di campo magnetico.
Libgdx tratta mouse e touchscreen allo stesso modo ed a seconda del dispositivo di input è possibile eseguire un’ interrogazione ciclica dello stato del dispositivo (polling) oppure registrare un listener che si occuperà di ricevere gli eventi di input in ordine cronologico (event handling). Il primo metodo è sufficiente alla gestione di semplici giochi arcade; il secondo metodo, invece, è necessario quando nel videogioco sono coinvolti element i dell’ interfaccia utente come i tasti, poiché questi si basano su sequenze di eventi come tocco/rilascio del tasto. La gestione di tutti i dispositivi di input è affidata all’ interfaccia di Input.
L’ interfaccia Graphics fornisce informazioni riguardo il display del dispositivo o la finestra dell’applicazione, a seconda che si tratti di piattaforma Android o desktop rispettivamente, nonché informazioni e accesso al contesto OpenGL corrente. Più nello specifico fornisce informazioni riguardo la dimensione dello schermo, la densità dei pixel e le proprietà del frame-buffer come la profondità di colore ovvero la quantità di bit necessari per rappresentare un colore in un singolo pixel del frame-buffer, appunto, di un dispositivo video. Come per altri comuni moduli di Libgdx, l’accesso è fornito tramite l’utilizzo di campi statici della classe Gdx.
Il modulo Graphics permette la realizzazione di giochi 2D o 3D. In particolare, per il 2D mette a disposizione il package scene2d che consente un’agevole costruzione di interfacce GUI grazie ad una gerarchia di attori.
Tali attori sono definiti nella classe Actor del package scene2d ed è possibile impostare posizione, dimensione, punto di origine, scala, rotazione e colore.
La seconda delle tre classi che compongono il cuore di scene2d è la classe Group che a sua volta è un attore e può avere attori figli. Quando viene apportata una modifica (rotazione, scala…) ad un oggetto group questa si ripercuote su tutti gli oggetti figli. Infine, la classe Stage che è un InputProcessor che gestisce il disegno degli attori e quando riceve eventi di input li invia agli attori appropriati. La gestione della grafica è affidata all’ interfaccia Graphics.
Il modulo Files in libgdx fornisce i metodi per leggere, scrivere, copiare, spostare e cancellare file. Ognuna delle piattaforme di destinazione di libgdx gestisce i file in modo differente. Su un sistema operativo di una piattaforma desktop (Windows, Linux, Mac OS X) i file possono essere riferiti tramite percorsi relativi alla directory corrente di lavoro o tramite percorsi assoluti e, generalmente, possono essere letti o scritti da tutte le applicazioni. In Android la situazione è un po’ più complessa: ci sono tre possibili modi per memorizzare i file:
- all’ interno della cartella assets del progetto Android. In questo caso i file sono delle risorse alle quali è possibile accedere in sola lettura;
- in una directory di archiviazione interna all’applicazione ed accessibile solo dall’applicazione. In questo caso i file sono accessibili in lettura e scrittura;
- in una memoria esterna (ad esempio una scheda SD). Tale memoria non è sempre disponibile e, di conseguenza, i file memorizzati al suo interno devono considerarsi come Se si utilizza questo metodo è necessario aggiungere un permesso nel file AndroidManifest.xml per poter accedere in scrittura.
Un file in libgdx è rappresentato da un’ istanza della classe FileHandle. Questa classe fornisce un tipo (type) che definisce dove il file è memorizzato. I diversi valori che può assumere questo tipo sono: classpath, internal, local, external e absolute. Tutte le piattaforme di destinazione possono far uso de i diversi tipi eccetto HTML5 che utilizza esclusivamente il tipo internal. Il tipo classpath permette di memorizzare file in sola lettura ma non viene utilizzato spesso. I file internal e local sono file in sola lettura memorizzati nella directory di lavoro delle piattaforme desktop o nella cartella assets della piattaforma Android. Se il file non viene trovato fra quelli di tipo internal o local verrà cercato fra quelli di tipo classpath. I file external sono relativi alla scheda SD su Android o alla home directory sulle piattaforme desktop. Infine i file absolute specificano i loro path completi ma, per motivi di portabilità, questi file vengono utilizzati solo quando strettamente necessario.
I tipi di archiviazione descritti potrebbero non essere disponibili per alcune piattaforme e libgdx fornisce due metodi per conoscere la disponibilità o meno di memoria interna o esterna:
boolean isExtAvailable = Gdx.files.isExternalStorageAvailable(); boolean isLocAvailable = Gdx.files.isLocalStorageAvailable();
La gestione dei file è affidata all’ interfaccia Files.
Libgdx fornisce metodi per la riproduzione di piccoli effetti sonori (Sound) o di brani musicali (Music) direttamente dal disco. Tutti gli accessi ai serviz i audio avvengono tramite l’ interfaccia Audio la cui istanza è ottenuta con la seguente istruzione:
Audio audio = Gdx.audio;
I Sound sono piccoli file audio, generalmente non più di 1MB (qualche secondo) di dimensione, che vengono riprodotti per specifici eventi di gioco (per esempio il salto di un personaggio o lo sparo di una pistola). Tali effett i sonori possono essere memorizzati in diversi formati e libgdx supporta mp3, ogg e wav. Nel caso in cui la dimensione del file è maggiore di 1MB libgdx fornisce un’istanza della classe Music che permette di effettuare lo streaming del file direttamente dal disco anziché caricarlo completamente nella RAM come accade per i Sound. Le istruzioni seguenti consentono di caricare sound e music dalla memoria interna di una piattaforma:
Sound sound = Gdx.audio.newSound(Gdx.files.internal("data/mysound.mp3")); Music music = Gdx.audio.newMusic(Gdx.files.internal("data/mymusic.mp3"));
Una volta ottenuta un’istanza della classe Music o Sound è possibile riprodurre il brano tramite il metodo play(), settarne il volume tramite il metodo setVolume(float volume), ripetere la riproduzione col metodo setLooping(boolean isLooping), mettere in pausa e terminare la riproduzione tramite i metodi pause() e stop() rispettivamente. Al termine dell’esecuzione dell’applicazione tutte le istanze di Music e Sound devono essere eliminate per liberare memoria e ciò avviene tramite il metodo dispose().
Infine, Libgdx include alcune classi per le operazioni di rete cross-platform. Le operazioni fornite dall’ interfaccia Net possono essere semplici richieste GET e POST http o operazioni per la comunicazione tramite socket TCP in un’architettura client/server. L’interfaccia Net prevede l’utilizzo di sei classi Java:
- java, è un’ interfaccia che fornisce stream di input e di output per lavorare con le socket;
- java, è una classe usata per configurare le socket TCP lato client;
- java, è una classe usata per creare le socket TCP lato server, essa fornisce il metodo standard accept() che blocca il server in attesa che si colleghi un client;
- java, è una classe usata per configurare le socie TCP lato server;
- java, è una classe che fornisce metodi per vedere qual è il codice di stato di un tipico messaggio di risposta http;
- java, è una classe che fornisce metodi utili per le richieste http.
La creazione di socket TCP lato client e lato server avviene tramite le seguenti istruzioni:
Socket socket = Gdx.net.newClientSocket(Protocol p, String host, int port, SocketHints hints); ServerSocket server = Gdx.net.newServerSocket(Protocol protocol, int port, ServerSocketHints hints);
per inviare richieste http si usa il seguente metodo dell’ interfaccia Net:
Gdx.net.sendHttpRequest(HttpRequest httpRequest, HttpResponseListener httpResponseListener);
Per maggiori dettagli riguardo l’ interfaccia Net è possibile visitare la documentazione ufficiale.
Conclusioni
Un aspetto di fondamentale importanza nell’uso di libgdx è la possibilità di esportare i proprio giochi anche sulle altre piattaforme supportate aumentando la visibilità sul mercato. Questa è una delle ragioni per cui si è scelto di approfondire questo framework.
Seguendo questo link, troverete un gioco sviluppato con libgdx per tutti gli manti della musica e per i geni dell’udito.