venerdì 30 dicembre 2011

Programmazione: Ecco perché dovresti usare i private accessors

Ah, gli accessors. Per molti programmatori in erba sono quasi degli sconosciuti. Per i più navigati (e pigri) poco più che delle seccature. Per alcuni IDE, un modo come un altro per generare warnings e riempirvi il codice di fastidiose sottolineature.

E' tuttavia pratica comune ammettere che gli accessors pubblici hanno un loro perché, tirando fuori il principio di Incapsulamento dell'OOP. Ma cosa si può dire di un accessor privato?

Prendiamo ad esempio il seguente frammento di codice C++:

class Player
{
   public:
   
   void hurt(int damage){
      this->hp -= damage;
   }

   void heal(int amount){
      this->hp += amount;
   }

   private:

   int hp;
}

Il membro hp è privato e la classe Player vi accede in modo diretto all'interno dei suoi metodi. Che c'è di male? Beh, niente. Tutto lecito. Il compilatore va avanti e il programma funzia. Ma vediamo come apparirebbe con l'utilizzo di accessors privati:

class Player
{
   public:
   
   void hurt(int damage){
      setHp(getHp()-damage);
   }

   void heal(int amount){
      setHp(getHp()+amount);
   }

   private:

   int hp;

   private:

   void setHp(int value){
      this->hp = value;
   }

   int getHp(){
      return this->hp;
   }

}

E' vero, c'è del codice in più. E' vero, codice in più == bug in più. Ma ci sono anche ottimi motivi per operare in questo modo. Elenchiamoli:

  • Tutte le modifiche in un punto solo
    Sappiamo che questo vale per gli accessors pubblici, perché non dovrebbe valere per quelli privati? Ogni modifica al membro hp verrà fatta dentro setHp(), riducendo la complessità di debug e manutenzione del codice (un solo breakpoint).
  • Validazione
    E' possibile operare logiche aggiuntive in un unico punto. Ad esempio si potrebbe controllare che gli hp non scendano sotto lo zero, o che non salgano sopra una certa soglia, mettendo tutti i controlli nella setHp() anziché spargerli nelle varie hurt(), heal() e così via.
  • Unit test?
    Con qualche accorgimento (tipo un metodo wrapper pubblico sugli accessor privati, magari da commentare dopo la fase di test) possiamo scrivere dei test più sicuri ed efficienti, perché andranno a spremere con tutte le logiche possibili un unico metodo. (I moderni tool di unit testing solitamente hanno poi escamotage per raggiungere anche i metodi privati, senza chiederci di sporcare il codice con wrapper vari.)
  • Incapsulamento
    Ancora una volta, l'essere privato non elimina i vantaggi del pubblico. E' vero, stiamo "nascondendo" dettagli implementativi alla stessa classe che li implementa, il che può apparire sciocco. Ma non è mai sciocco racchiudere funzionalità operanti su un membro in un unico punto.
  • Ridefinizione nelle classi derivate
    Ok, qui bariamo un attimo e rendiamo i nostri accessors virtual e protected. Le classi derivate possono ridefinire l'accesso al membro. Se questa esigenza fosse sorta dopo un po' e non avessimo avuto gli accessors privati saremmo stati costretti quantomeno ad un pericoloso refactoring.
  • Undo e Redo
    Altro figlio dell'avere le modifiche in un solo punto è la possibilità di aggiungere con facilità funzionalità di undo e redo.

I vantaggi qui elencati sono solo esempi. Scommetto che lavorandoci se ne potrebbero trovare altri.

Buon coding...e Buon Anno!

L'articolo originale da cui ho tratto lo spunto per questa riflessione è pubblicato sul blog Joel on Software

mercoledì 28 dicembre 2011

Dwarf Fortress: come ti sopravvivo al primo anno (parte 1)

Questo post poteva anche appartenere alla serie "Il gioco indie che forse non conoscete...", ma chi non ha mai sentito parlare di Dwarf Fortress? Chi non ha mai provato a giocarci e, dopo solo tre minuti, l'ha cestinato per via della sua inconcepibile complessità? E chi, tra questi, ha mantenuto quell'amaro in bocca dovuto alla sensazione di aver appena rinunciato ad una esperienza di gioco potenzialmente molto valida?


Ebbene, se siete tra i tanti frustrati che ci hanno provato ma ne sono stati sconfitti, questo post tenterà di farvi tornare la voglia per un nuovo tentativo. Sì, perché qui si parla della base di partenza per creare una fortezza e mantenerla in vita per il primo anno di gioco.

E' vero, ci sono tanti tutorial su Dwarf Fortress, quindi perché anche questo?

Beh, DF è un gioco grosso (ma rende meglio all'americana: HUGE!) e, quasi fosse fisiologico, anche i tutorial che vanno ad occuparsene finiscono per diventarlo. Inoltre sono spesso scritti da guru del gioco, persone che ormai ne padroneggiano i segreti più occulti e che hanno perso di vista quella sensazione di muro che si trova davanti il novizio.

Io no. Ho perso le mie prime fortezze in molti modi: per fame, per sete, per rabbia dei nani, per fantasmi (!), per cause ignote ma che mi lasciavano nani morti ammazzati in giro per le sale e per altri divertimenti assortiti.

Dopo diversi tentativi ho però trovato la formula per l'equilibrio, quel tanto che basta a far vivere e prosperare la fortezza fino all'arrivo della prima carovana di mercanti, e anche un po' più in la. Tutto si riassume in due parole: cibo e alcolici. Fate in modo di produrne in quantità regolari (ma anche abbondanti) e avrete risolto i bisogni primari dei vostri nani.

Produrre cibo e alcol sottintende avere una o più fattorie (farm plot) funzionanti. Per fare una fattoria occorrono però una serie di passi intermedi che vanno dal tagliare gli alberi allo scavare in un terreno adatto (o irrigare). Fondamentale sarà anche tenere un conto preciso delle scorte.

In questa prima parte prenderemo solo confidenza con i meccanismi di base del gioco.
Incredibilmente, per essere un gioco tanto complesso, le meccaniche sono essenzialmente tre o quattro, e si ripetono sempre uguali.

All'inizio troverete i vostri nani (sette, naturalmente) attorno al wagon, il carro con cui sono giunti al luogo della spedizione. Sul wagon ci sono un sacco di robe utili, ma per ora non ce ne preoccupiamo.

Fermi tutti!


Il gioco parte in stato di pausa, come vedete nella parte alta a sinistra della schermata. Lo stato di Pausa è fondamentale e ci passerete gran parte del tempo. Occorre quindi padroneggiarlo. Si entra in pausa in due modi: o per volontà, premendo Space dal menu principale, o in automatico, quando si entra in un sottomenu. Durante la pausa il tempo si blocca (ma dai!), i nani non fanno nulla e non accade niente di divertente, ma potete navigare tra i ricchi sottomenu in santa pace e assegnare ordini e direttive ai vostri nani, o capire cosa non va e con chi. Per ora allenatevi ad entrare ed uscire dalla pausa, acquisendo coscienza di essa.

Potete dare ordini ai nani premendo 'd' (Designations), come vedete nella parte destra dello schermo. Da qui si accede ad un sottomenu con i vari ordini, da scavare (Mine), a tagliare alberi (Chop down trees), a raccogliere erbe (Gather plants) ecc.

Sì, ma come?


Ogni voce ha un suo tasto associato, che occorre premere. Mine, ad esempio, ha la 'd'. Per selezionare l'azione scavare, quindi, occorre premere 'd' e poi ancora 'd'. A questo punto la X gialla nella schermata di mappa con i nani diventa un cursore. Dobbiamo selezionare l'area interessata dall'azione, specificandone il vertice alto a sinistra e basso a destra. Per farlo portiamo il cursore (con le frecce) nel punto desiderato (alto a sinistra) e premiamo Enter. Quindi ci portiamo nel secondo vertice (basso a destra) e premiamo Enter di nuovo. L'area viene evidenziata. Nel caso dello scavare, l'area evidenziata rappresenta la fetta di terra/roccia da eliminare. Naturalmente ha senso solo se fatta in una zona con una parete rocciosa, oppure sotto terra.

Altro menu importante è quello delle costruzioni (Buildings). Ci si accede col tasto 'b' e vengono mostrate le possibili costruzioni. Ci torneremo, nel frattempo esplorate pure i vari sottomenu, specialmente quello dei laboratori (Workshops).

Chi fa cosa?


E' importante capire che non comandate direttamente i singoli nani, ma specificate cosa deve essere fatto (è un gestionale). I nani hanno poi delle statistiche e il sistema invia i nani più skillati nel lavoro scelto. A volte capita che nessun nano sappia fare la cosa scelta. In questo caso occorre agire sul singolo nano, abilitandolo al lavoro specifico (Labor). Ma questa è un'altra storia. Se avete scelto di giocare subito (consigliato) anziché preparare il viaggio con cura avrete a disposizione sette nani capaci di svolgere i compiti basilari.

Su e giù


Altra abilità importante che dovete sviluppare come giocatori di Dwarf Fortress è il concetto di Z-Level e come muoversi tra essi. DF è un gioco in 2D, ma simula il 3D lavorando con i livelli, come un programma di fotoritocco. Premendo '>' si scende di un livello (si va in profondità), mentre con '<' si risale. Quando i vostri nani inizieranno a scavare (o a costruire verso l'alto, perché no) diventerà essenziale navigare tra i livelli. Fate esercizio già da ora e vedrete come i livelli più bassi appaiano ora quasi del tutto neri (solida roccia), magari con qualche falda acquifera qua e la, mentre quelli più alti vadano a perdere di dettaglio man mano che si sale. Se vi perdete niente paura: con F1 si torna al wagon.

Coraggio dunque, iniziate la vostra avventura nanica e prendete la mano con i comandi di base e la mentalità del gioco, che nella prossima puntata si parte con la strategia di sopravvivenza.

mercoledì 30 novembre 2011

HowTo: usare la fotocamera su Android

Domanda: sto facendo una mia app per Android. Devo permettere all'utente di scattare una foto per farci poi alcune cose, come faccio?

Risposta: chiedi in prestito la camera ad Android.

Il concetto di base è quello di sfruttare il meccanismo degli Intent per chiedere al sistema di prestarci la sua gestione della fotocamera. Gli Intent sono una caratteristica veramente interessante di Android e chiunque decida di fare sul serio con questa piattaforma dovrebbe studiarseli per bene. Volendo comunque riassumere in due righe cosa sono e perché ci servono, possiamo dire che gli Intent sono lo strumento a disposizione delle applicazioni per esprimere la propria necessità di fare qualcosa (scattare una foto, mostrare una pagina web, vedere un video...), anche senza specificare il come farla. Di questo si delega il sistema stesso, che sceglie l'applicazione più consona a soddisfare l'esigenza.

La nostra esigenza, ovviamente, è scattare una foto e averla poi a disposizione. Una volta impostata la nostra Activity e l'azione che deve scatenare la richiesta, come pigiare un button, non ci resta che scrivere il poco codice che serve.

NB: prima di iniziare, preciso che tutto il codice qui indicato è esente da qualsiasi gestione delle eccezioni, anche da quelle comandate, in favore della leggibilità. Non è un grosso problema, dato che l'IDE (e Java) stesso vi obbligheranno a gestire le Exceptions necessarie.

private static final int CAMERA_REQUEST = 100; // un numero a nostro piacimento 
File tmpFotoFile = null;

private void launchCamera(){

   // Fase 1
   //

   tmpFotoFile = File.createTempFile("myappprefix", null);

   // Fase 2
   //

   Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
   cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(tmpFotoFile));
   startActivityForResult(cameraIntent, CAMERA_REQUEST);
}

Come vedete il codice si compone di due fasi:
  1. dobbiamo creare un'area di memoria in cui salvare i dati della foto che andremo a scattare. Volendo potremmo anche creare un file fisico, ma per questo tutorial supponiamo che non vogliate (o possiate) scrivere sulla SDCard. Il nostro file temporaneo in memoria è gestito da tmpFotoFile.
  2. ora possiamo chiedere al sistema di lanciare la sua gestione della fotocamera. Per fare questo creiamo la giusta Intent, dicendogli che ci serve qualcosa per ottenere una ACTION_IMAGE_CAPTURE. Decoriamo ulteriormente la Intent con una informazione extra: l'output, caro Android, mettimelo per cortesia in tmpFotoFile. Come vedete è tutto estremamente generico, proprio perché adattabile anche ad altri contesti. Fatto questo, lanciamo la nuova Activity risultante dall'Intent specificata in modo da attenderne (e gestirne) il risultato.

A questo punto siamo già in grado di lanciare la gestione della fotocamera e scattare le foto. Alla chiusura dell'Activity della fotocamera, però, non abbiamo più il controllo sui dati della foto scattati. Per fare questo ci occorre gestire l'override di un metodo della classe base Activity, onActivityResult, intercettando il corretto codice di request. Ecco come:

byte[] bitmapdata;
ImageView preview;

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
   
   if (requestCode == CAMERA_REQUEST) {
     Bitmap datifoto = android.provider.MediaStore.Images.Media.getBitmap(this.getContentResolver(), Uri.fromFile(tmpFotoFile));
     preview.setImageBitmap(datifoto);
    
     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     datifoto.compress(CompressFormat.JPEG, 70, bos); 
     bitmapdata = bos.toByteArray();
       
     tmpFotoFile.delete();
  }
}


Ci facciamo restituire la Bitmap leggendone i dati dal nostro file temporaneo. Con questa, volendo, andiamo ad impostare una ImageView che ci fornisce l'anteprima dell'immagine (la ImageView deve essere opportunamente presente nella nostra Activity e referenziata attraverso findViewById(...)).

La seconda cosa che vogliamo fare è ottenere una immagine jpeg compressa partendo dalla grossa bitmap ottenuta. Lo facciamo con le tre righe che seguono l'impostazione della preview, nelle quali specifichiamo il grado di compressione (70, nell'esempio) tenendo conto che 0 significa massima compressione e 100 massima qualità.

Infine, puliamo la memoria eliminando il nostro file temporaneo, che ci sembra sempre cosa buona e giusta.

Non ci resta altro da fare che aggiungere nel Manifest le uses-permission necessarie a far funzionare il tutto, ovvero:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
<uses-feature android:name="android.hardware.camera" />

Buone foto!

venerdì 25 novembre 2011

Povero italiano!

Grazie ad internet abbiamo modo di accedere alle idee e ai pensieri di migliaia di nostri simili, tutti i giorni. Ciò è bene. Il male è vedere come molta di questa gente si esprime. Non saprei dire se la colpa sia di internet, quel che è certo è che la Rete ha fatto emergere una inquietante incapacità del giovane italiano medio nel maneggiare la propria lingua.

Scriviamo veramente male. Quel che è peggio è che ci stiamo abituando a tollerare certi orrori. I forum sono pugnalate agli occhi. I commenti nei blog nascondono spesso calci rotanti al dizionario. Gli stessi post, che ti aspetteresti editati con un minimo di cura, spesso non sono migliori.

Non parlo di qualche refuso. Non entro nel merito di accenti gravi o acuti. Ma il verbo avere, su! Quella benedetta acca sarà anche muta ma mica invisibile!

Le virgole non sono orpelli grafici, non se ne vanno a zonzo da sole per la frase; amano invece stare in compagnia, sempre attaccate alla parola che le precede, ma staccate dalla successiva, per aiutare il benedetto periodo ad acquisire un senso.

Il punto! Cosa c'è di più bello che mettere un bel punto al termine della frase? E' come fare una release. Ecco, ho finito un pensiero. Punto!

I due punti poi! Meraviglia della sintesi. Riassumono tutto un pensiero appena espresso. Il fatto che se ne vedano pochi è triste, ma anche significativo: nessun pensiero da riassumere, evidentemente.

Il punto e virgola... Carneade! Chi era costui? A differenza di virgole e punti, seminati qua e là e abbandonati a loro stessi, i punti e virgola sono proprio ignorati, spesso oggetto di oscure supposizioni.

Ad un maschio gli, ad una femmina le. Cavoli, persino i presentatori (ma ho sentito addirittura dei doppiatori) in tv ad una signora gli...

Volendo passare anche sopra la punteggiatura e le piccolezze particellari, non si fa un grande affare. Già, perché ci si costringe a guardare i verbi e le loro coniugazioni, e sono dolori. Il congiuntivo non è una malattia degli occhi! Villaggio ci ironizzava su vent'anni fa, con i suoi venghi e dichi, ma è ancora tutto tristemente attuale.

Le doppie! La z prolifica, la regola del zio, zia, zie è un patrimonio culturale ormai andato perduto. Le k e le x hanno guadagnato posizioni di potere persino nelle parole più intime della nostra povera lingua.

Ma tutto questo è ancora niente.

La cosa più inquietante è la palese confusione che regna nelle menti. Quando non si è capaci di formulare una frase di senso compiuto, io penso, è ora di iniziare a preoccuparsi sul serio, e di cose serie.

lunedì 21 novembre 2011

Quando il programmatore è anche musicista... MMA!

Una delle esigenze più comuni per i poveri mortali che gettano ore del proprio tempo libero nello studio di uno strumento musicale è avere a disposizione delle basi su cui suonare. Uno si studia un lick, ci si esercita a bocce ferme, cioè suonandolo solo sul proprio strumento, arriva a padroneggiarlo e poi, quando anche il metronomo gli da ragione, avrebbe ben piacere di sentire l'effetto che fa se suonato su un accompagnamento. Sacrosanto.

Ebbene, mi sono trovato spesso nella situazione ma ho sempre dribblato il problema vuoi con la band (complicato...), vuoi con veloci registrazioni di me stesso su una drum machine (macchinoso e poco scalabile... cambio accordi? cambio velocità?), vuoi arrangiandomi con qualche mezzo di fortuna, come editare accordi su un software per tabulazioni come PowerTab (uff...).

Ieri, complice una domenica nebbiosa e pigra, non avevo voglia di fare nulla di tutto ciò, solo di suonare il mio dannato lick su una base. E allora Google. E scopro che un settore di software apparentemente banale è in realtà pieno di applicazioni commerciali e di pochissimo free. Già mi prudevano le mani e nasceva l'idea per un progetto, quando mi sono imbattuto (per fortuna) in questo MMA: Musical MIDI Accompaniment.

E' in python, quindi portabile su diverse piattaforme. Per l'occasione navigavo con una macchina Windows, ma al più presto lo installerò anche sulla mia Fedora. Cercavo il modo quick and dirty per fare quel che mi serviva e... beh, più quick e, soprattutto, più dirty sarebbe stato difficile trovare!

MMA è software per un programmatore. Editare la propria base è proprio come scrivere un programma, e l'editor in sé è niente più che un editor di testo. Esatto, potete scrivere la vostra base anche con gedit, o notepad, proprio come fosse un listato C. Poi, tra le (poche) voci di menu che mi trovo? Build, naturalmente. Il risultato della build è un midi.

Ecco un esempio di base:

Tempo 120
Groove Tango

1  C
2  F
3  G7
4  C  / Em
5  C
6  /
7  F
8  C

L'impatto è stato un po' una sorpresa, un po' un ritrovarsi a casa propria, come potete immaginare. Per un programmatore nulla di più intuitivo che scrivere codice, fare build e verificare il risultato. Rimane da capire se anche gli utenti non del mestiere si trovino bene con questa modalità operativa.

Per quanto mi riguarda, un software assolutamente da segnalare! E ora scusatemi, ho dei fraseggi da debuggare...

mercoledì 9 novembre 2011

Il blog che forse non conoscete: 1 Month Game!

Ci risiamo. Sembra proprio che la cosa di cui gli sviluppatori amatoriali sentono maggiormente la mancanza sia la condivisione. Condivisione dei propri sforzi, dei propri stimoli, delle proprie idee. Deve essere così, altrimenti non si spiegherebbero i successi di tutti i contest posti nella forma fai un gioco in X ore/giorni. E' chiaro che non è la sfida di fare un gioco in un certo lasso di tempo a convincere migliaia di sviluppatori a tapparsi in casa a produrre codice, sprite e musiche. La sfida e l'agonismo ci sono, ma in minima parte. Quel che realmente decreta il successo di tali iniziative è senza dubbio la gran voglia di condividere non solo il frutto delle proprie fatiche, ma le fatiche stesse. E tra persone che le capiscono e le apprezzano, cioè tra altri sviluppatori con lo stesso obiettivo.

E' bello vedere emergere lo spirito di animale sociale in una categoria, la nostra, tacciata troppo spesso (e sempre più senza ragione) di luposolitarismo e asocialità. Ed è bello anche un blog che mi è capitato di trovare un po' per caso, tra una catena non più producibile di tweet e link: 1 Month Game.

Non si tratta di un contest ma di un vero blog a cura di Sophie Houlden in cui, seguendo alcune semplici regole, chiunque può postare il devlog del proprio progetto. Unica regola: terminare in un mese!

Per i più timidi rimane comunque un bel posto dove passare un po' di tempo, confrontandosi con quel che producono gli altri. I progetti sono infatti facili da seguire grazie alle linee guida di Sophie che impongono titoli dei post che indicano numero di settimana e giorno di sviluppo, da Week 1 - Day 1 a Week 4 - Day 7.

Come sempre, leggere e capire cosa fanno gli altri (e come) rimane un'esperienza e un'opportunità impagabile, di cui forse siamo un po' assuefatti oggi, ma che continua a stupire quelli che quando internet ancora non c'era...

venerdì 4 novembre 2011

Quanta ne sai di C e C++?

Il saggio sa di non sapere. Il programmatore... beh... non sempre si dimostra saggio! Il programmatore in erba semplicemente non sa di cosa sta parlando, quindi ben che vada da fiato alle trombe e spara qualche orrore. Va corretto, ma con misericordia; in fondo è solo rumore ingenuo.


Più grave è la sindrome che troppo spesso colpisce i programmatori più esperti, o che si reputano tali. Sembra infatti che provino un certo gusto nel dare ai meno fortunati questuanti da forum mezze risposte, talvolta allusive, vedo e non vedo, che costringono i supplicanti a nuove richieste di spiegazioni. Il programmatore un-po'-più-esperto di solito riconosce questi personaggi come programmatori in erba evoluti, ma non ancora completi.

Ma attenzione. Anche il programmatore un-po'-più-esperto, quello che a prima vista può apparire saggio, che scrive tutorial, che dimostra pazienza, ha quasi certamente delle lacune. Il saggio sa di non sapere, dicevamo. Il difetto principale del programmatore è che, pur sapendo di non sapere (nei casi più fortunati), non lo ammette. Perché è cattivo? No di certo. Perché la programmazione è un lavoro di problem-solving, e la mente del programmatore è allenata ad affrontare quotidianamente sfide. Al mattino, quando si siede al pc, è conscio di affrontare qualcosa per la quale non ha ancora la soluzione, ma fiducioso sul fatto che a sera avrà la sua routine funzionante. Quindi ok, posso anche non sapere qualcosa, ma quel che so è sufficiente. Più che sufficiente. Sono un esperto.

...ne sei sicuro? Sì, dico a te, te che con il C hai passato gli anni dell'università, che hai già visto migliaia e migliaia di righe in C++, che credi di digerire poliformismo e allocazioni dinamiche come acqua fresca, te che in questo momento stai scrivendo questo post (io?) sì, sì, proprio te! Scendi dal piedistallo e vatti a fare questo test: Deep C!

Poi ne riparliamo!

venerdì 14 ottobre 2011

Il gioco indie che forse non conoscete: One and One Story!

E' con piacere che vi segnalo One and One Story, un puzzle game sviluppato dal giovanissimo e italianissimo Mattia Traverso (utente come molti di noi di IndieVault), e fruibile tramite Armor Games.


Il gioco propone una serie di livelli con modalità di gameplay mutevoli e a tratti piuttosto impegnative, dove lo scopo è sempre il medesimo: far congiungere i due personaggi sullo schermo, un lui e una lei.

A fare da cornice alle loro unioni troviamo scenari con blocchi da spostare, salti, spuntoni e mortali cadute da evitare. Quel che affascina, però, non è tanto il gameplay in sé (comunque molto ben curato), quanto l'atmosfera che permea l'intero gioco.

Sia la grafica, che la musica, che i testi che compaiono tra un livello e l'altro, immergono il giocatore in una romantica malinconia, a tratti tragica, che sfocia in un finale, a parere del sottoscritto, molto profondo. Mi ha ricordato un altro piccolo capolavoro di cui si era parlato su queste pagine, Appy 1000mg, con il quale condivide l'aura malinconica (e un finale per certi aspetti simile).

Il web è già pieno di recensioni su questo One and One Story, ed è giusto così. Questa vuole solo essere una eco del tam tam che si sta diffondendo, che più che altro dice a tutti gli sviluppatori, giovani e non, che sì, si può fare!

lunedì 10 ottobre 2011

Il programma Free che forse non conoscete: StoryBook!

Ancora una segnalazione per gli amanti della scrittura creativa. Questa volta si tratta di un utile tool per pianificare ed organizzare la propria fatica letteraria, in modo tale da non perdersi nel susseguirsi cronologico degli avvenimenti. Apparentemente è tutto qui, ma in questo StoryBook (java, multipiattaforma) c'è ben altro.


Il vero aiuto viene dato dalla struttura schematica a cui ci obbliga il software. Il suo ottuso xxx field is mandatory è da leggersi in realtà come domanda molto meno sciocca: stai forse pensando di andare avanti a scrivere questa parte senza sapere esattamente dove si svolge? E per dove si intende veramente dove: luogo, città, nazione. E quando si svolge? E con chi? Guarda che il personaggio che stai usando è privo di qualsiasi descrizione. Ah, non ha nemmeno la data di nascita. E' forse Egli un dio?

Insomma, ci aiuta a riflettere e a ragionare, e ci ricorda di prestare attenzione ai dettagli.

Presenta poi diversi report con cui visualizzare i capitoli e le scene che li compongono (sì, perchè ogni tuo capitolo è diviso in scene vero? e in quella scena ci sono quei personaggi e quegli oggetti, vero? E di ognuno di questi conosci forma, colore e odore, vero?). Ci sono le anagrafiche dei personaggi (divisi in principali e secondari), con diversi (ma non troppi) campi da compilare, delle locations, degli oggetti importanti.

A prima vista può sembrare tanto lavoro in più, ma non è così. La fase di analisi, qualunque sia la vostra religione a riguardo, va in qualche modo fatta. Per chi ama fare scalette è lo strumento principe: nulla fuori posto, tutto cronologicamente coerente, possibilità di pianificare intere trilogie prima di iniziare a scriverne una sola pagina. Per chi, al contrario, non ama andare troppo avanti con la trama per lasciarsi stupire dalla piega degli eventi, StoryBook rimane un valido aiuto. Anche solo concentrandosi sul capitolo che abbiamo davanti, infatti, induce a ragionare e a schiarire le idee.

Va sottolineato che non si tratta di un editor di testi. Non scriverete il vostro romanzo dentro StoryBook, che non presenta campi appositi per questo (a meno di non voler usare i campi note, ma francamente non credo sia stato progettato per questo). Resta da capire se lo si possa utilizzare anche in altri ambiti, magari nella progettazione di software...

venerdì 30 settembre 2011

HowTo: Integrare SQLite nel proprio gioco Java

Come tutti i programmi, anche i giochi necessitano di dati. Abbiamo già parlato in precedenza di alcuni metodi per gestire (scrivere e rileggere) tali dati, utilizzando file xml piuttosto che file binari o ASCII. Ci sono naturalmente anche altri modi, e uno di questi è senza dubbio SQLite, un piccolo e leggero database relazionale su file.

Un database? Non sarà un po' troppo?

Dipende. Sicuramente se volessimo scomodare un vero server di database come MySQL o SQLServer occorrerebbe una giustificazione più che valida. Ma SQLite è, come lascia intendere il nome, piccolino. Piccolino ma, attenzione, adatto a gestire grandi mole di dati, soprattutto in lettura. Pensate alla comodità di avere le varie entità del vostro gioco serializzate in modo ordinato in tabelle di una base dati. La tabella Livelli, con i dati relativi ad ogni livello di gioco, la tabella Nemici, con tutte le statistiche delle varie tipologie di nemici, la tabella Oggetti, con tutti gli item presenti nel vostro modo, e perché no, la tabella Partite, con i salvataggi del giocatore. E così via. Tutti dati che dovreste comunque gestire in una qualche forma di file.

I vantaggi del relazionale.

Una volta modellata la base dati potrete poi sfruttare il principale vantaggio del suo utilizzo: le relazioni. La tabella NemiciPerLivello potrebbe contenere in modo semplice le coppie Livello-Nemico con la posizione all'interno del livello del tale nemico, ad esempio. In poche parole avete tra le mani, in forma gratuita, la potenza di chiavi primarie, chiavi esterne, vincoli di check e quant'altro.

I vantaggi del linguaggio di query.

Recuperare (ma anche salvare, aggiornare o eliminare) i dati diventa semplice. Volete popolare il livello con una lista di oggetti casuali ma che siano di valore compreso tra min e max? Complicato con xml, un delirio con ASCII o binario, ma semplice ed immediato con una query SQL come:

SELECT * FROM ITEMS WHERE ITEMVALUE >= @min AND ITEMVALUE <= @max

Già, ma come lo integro nel mio game?

Ed eccoci al punto. Per questo articolo prenderò come linguaggio di esempio Java. Essendo un linguaggio orientato per motivi storici ad applicazioni enterprise (da ufficio, per intenderci) ha già una buona integrazione con oggetti esterni come le basi dati. Per prima cosa occorre scaricare il pacchetto (.jar) contenente SQLite: al momento in cui scrivo ne trovate una copia qui.

(Per gli amici C++: SQLite è scritto in C, quindi la sua integrazione con C/C++ è assolutamente fattibile. Ne esiste anche la versione managed per .NET).

Questo pacchetto va poi inserito nel vostro progetto ed incluso tra le librerie in uso (in Eclipse si fa il tutto da Progetto->Proprietà->Java Build Paths->Librerie->Aggiungi JAR).

A questo punto siete pronti per scrivere codice! Vi servono solo 2 righe, che sono queste:


Class.forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite:db/miodatabase.sqlite");


dove db/miodatabase.sqlite è il percorso + nome del vostro database, relativo alla root del progetto.

E come lo creo il database?

E qui viene il bello. Con file di testo semplici ci basta Blocco Note. Con file binari ci occorre un editor di qualche tipo, che spesso dobbiamo scrivere noi. Con file xml può andare un qualunque editor di testi, ma per documenti importanti può diventare necessario un editor apposito, ancora una volta custom e a nostre spese.

Per SQLite invece è pieno il mondo di programmi di amministrazione. Pensateli come fossero i vostri editor per i dati, perché è proprio quel che sono. C'è ad esempio un plugin per Firefox, SQLite Manager, comodissimo e ben realizzato, che vi fa creare ed editare il vostro database da browser. Check it out!

Un esempio di lettura.

Concludiamo con un piccolo esempio che mostra quanto diventi semplice ottenere i dati (per la loro creazione avrete probabilmente usato un tool come quelli citati qui sopra).

List<quest> ret = new ArrayList<quest>();
Connection conn = DriverManager.getConnection("jdbc:sqlite:db/miodatabase.sqlite");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from quests where npc = " + npcId + " and done = 0");
while(rs.next()){
ret.add(new Quest(rs.getInt("id"),
rs.getString("text"),
rs.getString("type"),
rs.getString("onsuccess"),
rs.getString("onwait"),
rs.getInt("reward_gold")));
}
rs.close();
conn.close();

Una volta creato il vostro oggetto Connection potrete interrogare la base dati attraverso un oggetto Statement. La query in questo esempio preleva tutte le Quest di un ipotetico gioco di ruolo fornite da un personaggio non giocante con un certo id. Si riempie quindi un oggetto di gioco Quest con i dati prelevati e lo si aggiunge alla lista da restituire. Quando il set di dati finisce (rs.next() ritorna false quando non trova più nulla da leggere) si chiude la connessione. Abbiamo ora una lista di oggetti Quest pronti per essere gestiti dal motore di gioco!

Con questo spero di avere aggiunto un'ulteriore freccia alla faretra dello sviluppatore in erba. Considerate che esistono anche i database e che non è vietato utilizzarli. Specie se se ne restano confinati in un unico, piccolo file binario.

Buon coding!

lunedì 26 settembre 2011

Il gioco indie che forse non conoscete: Whack Your Boss!

Se c'è un giorno della settimana in cui giocare a questo gioco assume gusto e senso del tutto particolari, beh, è proprio il lunedì. Cioè oggi. Quindi non mi dilungherò in verbose esposizioni.


Whack your boss di Tom Winkler ha il gameplay di un trova le differenze da settimana enigmistica, ma con una dose massiccia di splatter e humor sanguinolento. Alcune trovate sono veramente esilaranti, sebbene inadatte ad un pubblico troppo sensibile (e, naturalmente, al vostro capo).

Coraggio dunque: se credete di aver già pensato a tutti i possibili modi per far la festa al vostro superiore, scoprirete di esservi sbagliati alla grande. Scommetto che d'ora in poi guarderete la tazza sulla vostra scivania con occhi diversi...

lunedì 19 settembre 2011

Tutorial: scriviamo un gioco in C++... Tris! (parte 6)

Nell'ultima puntata, risalente ormai a quasi due mesi fa (vergogna!), eravamo quasi arrivati al gioco completo. Quasi...

Il nostro gioco infatti funziona, ma non è molto interessante poichè il computer non ragiona sulle proprie mosse, ma si limita a posizionare simboli a caso nelle celle vuote. Vincere contro di lui è fin troppo facile.

Quel che ci manca è di fornire il computer di una sorta di intelligenza, o almeno di quella che basta per capire come controbattere alle nostre mosse e magari metterci in difficoltà.

Nel nostro caso specifico non si tratta di una gran difficoltà, e con un po’ di ragionamento si può giungere ad una soluzione casereccia o addirittura scomodare qualche algoritmo famoso come il minimax.

Il punto focale di questo post non è però il come scrivere la logica, ma il come organizzarne la gestione. Quel che faremo sarà creare un meccanismo per cui un tipo di intelligenza artificiale tra molte disponibili venga prelevato ed utilizzato ad ogni partita. Ognuno implementerà la propria logica per gestire le mosse.

Il come scrivere queste logiche dipende dal programmatore, dal suo intuito, il suo gusto, la sua formazione. It's up to you, come dicono quelli là.

Utilizzeremo due design pattern: Strategy e Factory. Strategy ci servirà per implementare le diverse AI (che nel nostro caso saranno due), mentre una factory ci fornirà una implementazione concreta ad ogni partita, secondo una logica random.

Più facile a vedersi che a dirsi. Iniziamo quindi con la classe base astratta StrategyAI.

#ifndef STRATEGYAI_H
#define STRATEGYAI_H

enum GridSymbols
{
EGS_X, EGS_O, EGS_NONE
};

class StrategyAI
{
public:
StrategyAI();
virtual ~StrategyAI();

virtual void PlaceSymbol(GridSymbols grid[3][3]) = 0;

protected:
private:
};

#endif // STRATEGYAI_H

Come vedete è occorso un po’ di refactor: abbiamo spostato qui la definizione dell’enumerativo dei simboli presente nella classe PlayGameState. Per il resto si tratta di una classettina molto semplice; contiene infatti un solo metodo virtuale puro atto a posizionare la mossa del computer all’interno della griglia. Le classi concrete che implementeranno StrategyAI dovranno implementare proprio questo metodo.

Una prima classe di esempio è DummyStrategyAI, che altro non fa che simulare quel che già avevamo, ovvero un riempimento casuale della griglia. Ecco la sua implementazione (ne ometto la dichiarazione)

#include "../include/DummyStrategyAI.h"
#include <cstdlib>

//-----------------------------------------------------------------------------
DummyStrategyAI::DummyStrategyAI() : StrategyAI()
{
//ctor
}

//-----------------------------------------------------------------------------
DummyStrategyAI::~DummyStrategyAI()
{
//dtor
}

//-----------------------------------------------------------------------------
void
DummyStrategyAI::PlaceSymbol(GridSymbols grid[3][3])
{
int gridx, gridy;

bool ok = false;
while(!ok)
{
gridx = rand()%3;
gridy = rand()%3;

if(grid[gridy][gridx] == EGS_NONE)
{
ok = true;
grid[gridy][gridx] = EGS_O;
}
}
}

Assieme alla DummyStrategyAI ho implementato anche una DefenderStrategyAI, una semplice AI che tende a bloccare i tentativi di tris del giocatore umano, pur non giocando d’attacco. Non riporto il codice di questa classe perchè non introduce nulla di nuovo.

Molto più interessante è vedere invece come queste classi vengono usate dalla PlayGameState.

Il metodo Reset è ora diventato così:

//-----------------------------------------------------------------------------
void
PlayGameState::Reset()
{
m_SymbolsPlacedCount = 0;

m_PlayerWins = m_ComputerWins = false;

if(m_pWinText)
{
SDL_FreeSurface(m_pWinText);
}
m_pWinText = 0;

for(int r=0; r<3; ++r)
{
for(int c=0; c<3; ++c)
{
m_Grid[r][c] = EGS_NONE;
}
}

if(m_pAI)
{
delete m_pAI;
m_pAI = 0;
}

StrategyAIFactory factory;
m_pAI = factory.CreateStrategyAI();
}

Come potete vedere c’è ora un nuovo membro, m_AI, che altro non è che un puntatore a StrategyAI. Tramite la factory otteniamo una istanza concreta di StrategyAI ad ogni chiamata della Reset, ovvero ad ogni inizio gioco. Il suo utilizzo poi è molto semplice: nel metodo HandleMouseInput, dove prima eseguivamo la logica random, ora abbiamo una sola riga:

m_pAI->PlaceSymbol(m_Grid);

Piuttosto intuitivo, no?

Non ci resta che dare un’occhiata alla StrategyFactory. La sua definizione:

#ifndef STRATEGYAIFACTORY_H
#define STRATEGYAIFACTORY_H

#include "StrategyAI.h"

class StrategyAIFactory
{
public:
StrategyAIFactory();
virtual ~StrategyAIFactory();

StrategyAI* CreateStrategyAI();

protected:
private:
};

#endif // STRATEGYAIFACTORY_H

… e la relativa implementazione:

#include <cstdlib>
#include "../include/StrategyAIFactory.h"
#include "../include/DummyStrategyAI.h"
#include "../include/DefenderStrategyAI.h"

//-----------------------------------------------------------------------------
StrategyAIFactory::StrategyAIFactory()
{
//ctor
}

//-----------------------------------------------------------------------------
StrategyAIFactory::~StrategyAIFactory()
{
//dtor
}

//-----------------------------------------------------------------------------
StrategyAI*
StrategyAIFactory::CreateStrategyAI()
{
int prob = rand() % 100;

if(prob < 50) return new DummyStrategyAI();
else return new DefenderStrategyAI();
}

Banalmente andiamo a scegliere con una probabilità del 50% tra le nostre due StrategyAI implementate. Ovviamente possiamo scrivere tutte le StrategyAI che vogliamo, arricchendo così il gioco di sfumature diverse e dando l’idea che il computer non si comporti seguendo sempre uno schema predefinito. Volendo, si potrebbe assegnare una faccina ad ogni Strategy e mostrarla nella schermata di gioco, umanizzando ulteriormente il computer e creando una certa aspettativa: il player inizierà a conoscere e distinguerei giocatori “forti” da quelli “scarsi”!

Con questo abbiamo finalmente concluso. TrisLick vuole essere un piccolo esempio di come realizzare qualcosa di piccolo ma concreto, lasciando aperte eventuali possibilità di sviluppo grazie ad una architettura flessibile.

Coraggio dunque, aprite il vostro editor, scaldate il compilatore e iniziate a scrivere il vostro prossimo gioco!

Buon coding!

venerdì 9 settembre 2011

A proposito di scrittura creativa...

Scrittura creativa?!?

No, non avete sbagliato blog. Sì ma, direte voi, cosa c'entra la scrittura creativa con la programmazione e i videogiochi? Vi rispondo con un'altra domanda. Cosa c'entrano il metal, i giochi di ruolo, i manga e/o la letteratura fantasy con gli argomenti tipicamente presenti in questi post? Nulla. Però chissà perchè finiscono per l'essere, tutti o in parte, presenti nella vita del programmatore medio. Chiamatele sinergie, se volete.

Ad ogni modo fatevene una ragione perchè il post di oggi riguarda proprio la scrittura creativa, l'attività cioè di scrivere per raccontare una storia in modo colorato e interessante. Se proprio volete trovarci un appiglio con la programmazione di videogiochi, pensate ad un game designer che voglia imbastire un plot per il proprio gioco, oppure smettete semplicemente di consumarvi le unghie sugli specchi e continuate a leggere sereni.

La scrittura creativa è alla base del mestiere dello scrittore. Io non sono uno scrittore e non mi permetterei mai di trattare con leggerezza un argomento tanto nobile. Ma, per sinergia, ho provato varie volte a scrivere qualcosa, come scommetto molti di voi. Lo stato d'animo costante con cui mi sono ritrovato dopo tali esperimenti è stato quasi sempre lo stesso: insoddisfazione, con pure una punta di frustrazione. Sì, perchè i miei scritti mi apparivano (appaiono) mosci, statici, poco interessanti.

Mi sono anche documentato, sapete, su come si scrive. Ho diversi libri a casa sull'argomento, che ho sempre trovato di estremo fascino (così come ho molti metodi per chitarra, ma questa è un'altra storia e un altro vagone di frustrazioni). Ogni tanto ne prendo uno e lo rileggo. Concordo sempre su tutto quel che dice l'autore, ma poi, all'atto pratico, cado negli stessi errori.

Ho capito che scrivere è una cosa dannatamente seria e complessa. Ma ho anche capito che uno dei miei errori fondamentali sta nel mancato rispetto di una semplice regola:

Mostra, non dire.

Facile, no? Anzichè essere didascalici ed elencare una serie di cose che accadono, ci si dovrebbe sforzare di mostrarle senza esplicitarle. Facciamo un esempio. Dovete parlare di un tizio di nome Mario che sta per uscire di casa quando si accorge che fuori piove. Scena semplice. Ecco come appare dicendo queste cose:

Mario si preparò per uscire di casa. Era già sull'uscio quando si accorse che stava piovendo.
Lineare. Grammaticalmente corretto (spero). Abbiamo detto quel che succede. Ora proviamo a mostrarlo:

Mario indossò la giacca blu, il regalo di Anna di un qualche Natale passato, quando ancora si facevano regali tra loro, e si diresse verso la porta d'ingresso. Stava per uscire quando l'odore della pioggia lo colse. Gettò uno sguardo alla finestra in soggiorno, in parte visibile anche da lì, torcendo un po' il collo. Goccioline scivolavano sul vetro, e ora sentiva anche gli schizzi sollevati dalle macchine. Anna avrebbe sbottato, ritta sull'uscio, dicendo che erano solo due gocce e di muoversi, che erano già in ritardo. Ma tanto Anna non c'era più. Mario prese l'ombrello e uscì.
Sono sicuro che un vero scrittore farebbe di meglio, ma spero di aver reso l'idea. In questa versione non ho mai detto che stava per uscire di casa, lo si intuisce dal fatto che indossa la giacca e va verso la porta. Non ho detto che stava piovendo, ho mostrato le gocce sul vetro e l'ombrello. L'odore della pioggia è un pò raccontato, lo ammetto, ma fate i conti con i miei limiti, per piacere.

Cosa si nota? Fisicamente è più lungo, il che potrebbe essere tanto un bene quanto un male. Quel che è bene invece è che mostrando le cose si è costretti a visualizzarsele nella mente, ma visualizzarsele per davvero, facendo uno sforzo. Si cercano dettagli. E saltano fuori cose a cui non avevamo pensato. Anna, per esempio, e il suo passato con Mario. Il lettore si pone delle domande. Chi è Anna? Stavano insieme? Erano sposati? Hanno divorziato? E' morta? Giuro che non avevo pensato ad Anna prima di provare a scrivere questo pezzetto, ma quando è stato il momento è saltata fuori con grande spontaneità. Anche la figura di Mario acquista spessore: anzichè il nome più comune in Italia diventa ora un personaggio di cui intuiamo qualcosa del carattere: forse pigro, abitudinario, amaro e un po' pedante. Bisognerebbe leggere il resto per capirlo.

Ci siete arrivati? Ve lo ripeto: bisognerebbe leggere il resto per capirlo. Dico, non è meraviglioso? 

La prima versione, quella detta, invoglia a proseguire? Non mi pare. Questa ha forse qualche chance in più, ergo mostrare le cose le rende più vive, più interessanti; e il racconto acquista corpo e immersione, oltre a trascinarci verso soluzioni non programmate.

Ora prendete un buon libro, di un autore che stimate, e provate a leggere qualche passaggio. Dice o mostra? E' un esercizio molto divertente e che, personalmente, mi fa apprezzare ancora di più la lettura. Giusto per farvi un esempio più professionale del mio, ecco come Robert Jordan introduce il personaggio di Rand al'Thor, il suo protagonista, nell'incipit della saga La Ruota del Tempo. Io avrei scritto che Rand avanzava a fatica lungo la strada battuta dal freddo vento che scendeva dai monti, e ne sarei anche stato orgoglioso. Ma ecco come se la cava Jordan, ecco il vento:

[...] Le raffiche incollarono il mantello alla schiena di Rand al'Thor e gli sbatterono contro le gambe la lana color terra, poi la fecero svolazzare dietro di lui. Rand rimpianse di non avere una giubba più pesante o una camicia in più. Quando cercava di stringersi addosso il mantello, finiva quasi sempre per farlo impigliare nella faretra che portava appesa a un fianco; e non serviva a molto neppure provare a tenerlo fermo con una mano sola, dal momento che nell'altra reggeva l'arco, con la freccia già incoccata, pronto all'uso. Una raffica particolarmente violenta gli strappò di mano il mantello. Rand lanciò un'occhiata a Tam, suo padre, che camminava dall'altro lato dell'irsuta giumenta saura, quasi ad assicurarsi che fosse sempre lì. Si sentì un po' sciocco, ma era una di quelle giornate in cui si ha bisogno di conforto. [...]

Brrr! Che freddo!

mercoledì 31 agosto 2011

Il gioco indie che forse non conoscete: Bomber Planet!

Ci sono alcuni indie che è sempre bello seguire, e tra questi quel tizio che risponde al nick di Adamatomic, o Mr.Canabalt per i più.

In occasione della ventunesima edizione del Ludum Dare, il buon Adam ha sfornato uno dei 509 giochi presentati. Bomber Planet non è il migliore nè il peggiore, possiamo anzi dire che si mescola tra i tanti, ma è uno di quelli che ho potuto provare (sì, ho provato anche quello di Notch e no, non ne parlerò) e devo dire che mi ha lasciato quel piacevole senso di deja vù.



Ricorda moltissimo un gioco che ho amato nell'Amiga 500: Bomberman. Il gameplay è pressochè identico e il titolo un chiaro richiamo, oltre alla grafica in pixel art. L'unica differenza è che, anzichè farsi strada livello dopo livello nella vecchia concezione arcade, in Bomber Planet occorre esplorare un complesso di caverne per trovare l'uscita (in tema con il titolo del contest: Escape), sfasciando blocchi di pietra e strane creature mediante bombe ad orologeria.

In definitiva un buon coffeebreak game, che ha avuto il merito di riuscire a rievocare davvero il retrogaming, almeno al sottoscritto.

mercoledì 24 agosto 2011

Il servizio web che forse non conoscete: Twitch TV!

Quando ero piccolo e andavo al mare con i miei passavo ore ed ore al bar nella zona cabinet. All'epoca i PC, o meglio i loro antenati, erano poco diffusi e io, neanche a dirlo, non ce l'avevo. Escludendo le sale giochi, che erano per i grandi e irraggiungibili con i miei mezzi di locomozione e la mia età, le uniche mie parentesi videoludiche erano rappresentate da quegli affari ingombranti e luccicosi che trovavo nei bar del mare. Ogni stabilimento ne aveva almeno due o tre, e non esistevano i video poker, ma solo i Wonder Boy, i Final Fight, i Bubble Bobble e gli Altered Beast.

I miei ovviamente non capivano perchè mai volessi stare davanti a quegli affari anzichè in acqua e al sole, e il più delle volte mi toccava obbedire e andare a sguazzare. Ma quando la guardia si abbassava ecco che tornavo in quell'angolino proibito, dove c'era sempre qualche ragazzo più grande che pigiava sui tre tasti colorati.

Giocare costava. 100 lire all'inizio dei miei ricordi, poi 200. Quando iniziò a costare 500 ero già più grande, avevo l'Amiga a casa ed ero più sensibile ad altri richiami da spiaggia. Ai tempi delle 200 lire raccattavo gli spicci in giro e riuscivo a garantirmi tre o quattro partite, ma di quelle partite (sempre troppo brevi) ricordo poco. Ricordo invece quanto fosse bello guardare gli altri giocare. E non ero il solo. Quando arrivava il giocatore quello bravo, si formava un capannello di ragazzini attorno al cabinet. Qualcuno strisciava per terra le sedie fino ai giochi e ci saliva sopra, per vedere meglio da sopra le teste degli altri.

"Dove è arrivato?" domandava qualcuno di passaggio, per il quale vedere lo schermo era ormai impossibile. "Al terzo mostro" rispondeva un altro, senza voltarsi. L'adrenalina saliva. Nuovi ragazzini si accalcavano. Il foglietto attaccato con lo scotch sulla testa del cabinet, quello con la scritta "record:" e il numero inarrivabile, riceveva gli sguardi di tutti man mano che lo score sul display cresceva.

"Sesto schema!" annunciava qualcuno. Chi non aveva mai visto il sesto schema sgomitava per raggiungere un varco visivo allo schermo. Qualcuno dal bar si lamentava per la cagnara e per un po' si procedeva bisbigliando. In spiaggia, dove c'era il sole, l'acqua e la sabbia e il mondo degli adulti era rimasto agli anni '60, giungevano le voci di corridoio più improbabili. Qualcuno stava finendo il gioco. Con quanti gettoni? Uno. Uno? Uno!

Non ho mai visto finire un gioco al mare. Però, in un paio di epiche occasioni, ho visto il barista farsi strada tra una dozzina di ragazzini in costume eccitati e saltellanti e cambiare il numero sul foglietto "record".

Ora, quei giorni sono finiti. Ma c'è un sito in cui questo può accadere di nuovo. Su Twitch TV ci sono infatti i video di migliaia di videogiocatori. Il sito è navigabile per gioco, quindi interessante per vedere il gameplay del tal gioco o ammirare l'abilità di quello bravo, o magari capire come superare un certo punto a noi impossibile.

Certo, non è proprio la stessa cosa. Anche perchè noi, da ragazzini, non sapevamo nemmeno cosa fosse il gameplay.

lunedì 22 agosto 2011

HowTo: Guitar recording in linux

La settimana di ferragosto ha sacrificato in parte le attività di codelicks, ma mi ha dato modo di fare qualche esperimento casalingo con la mia fida Fedora.

L'HowTo di oggi si discosta per un momento dalla programmazione e dai videogiochi e si concentra invece sul come riuscire a registrare la propria chitarra, con tanto di batteria, usando software free e open source disponibile nei repository.

Il mio obiettivo era infatti questo: avevo bisogno di costruire una traccia di batteria, anche minimale, e di registrare una base di accompagnamento in modo da poterci poi suonare sopra, per esercizio, assoli, improvvisazioni e quant'altro.

Ci sono riuscito, ma c'è stato bisogno del lavoro sinergico di ben 4 software! Eccoli:

JACK

Il primo software di cui avrete bisogno è JACK, un server audio che si prefigge lo scopo di intercettare l'output audio di una applicazione e indirizzarlo come input in una seconda applicazione. Sembra complicato, ma per quel che dovremo fare noi sarà sufficiente scaricare JACK e il suo client (io ne ho usato uno basato su QT) e avviarlo. Al resto penserà lui.


Hydrogen

Il secondo software ci permetterà di editare la nostra batteria: Hydrogen. Si tratta di una drum machine veramente semplice da utilizzare e al tempo stesso molto flessibile. Editando la griglia nella parte bassa possiamo piazzare i colpi col suono del pezzo in riga: cassa, rullante, charleston (aperto e chiuso), tom alto e basso, piatti ecc. Supporta naturalmente ritmi binari e ternari e la possibilità di variare facilmente la velocità, espressa in bpm.


Hydrogen supporta due concetti: pattern e song. Il primo rappresenta il mattoncino col quale costruire il brano, ovvero la song. Il pattern è solitamente piccolo, una o due battute, e lo si edita tramite la griglia in basso. La song è l'unione consecutiva di uno o più pattern, e la si crea tramite la griglia in alto, dove le righe sono i pattern.

In questo modo è veramente possibile creare basi di batteria anche complesse con il minimo sforzo, utilizzando pochi semplici pattern.

Nel mio caso specifico, ho creato un solo pattern di una battuta con un 4/4 basilare di cassa e rullante, quindi una song di una battuta.

Al termine è possibile esportare la song in formato audio.

Rakarrack

Ecco chi fa la vera magia: trasformare il nostro pc in un rack per chitarra, con amplificazione ed effettistica! Rakarrack fa esattamente questo. E' sufficiente inserire la chitarra nella presa microfono (senza alcuna preamplificazione), assicurarsi che JACK rilevi Rakarrack e suonare. Dalle casse uscirà il suono modificato dal rack di effetti caricati.


E' possibile costruirsi il proprio rack o provarne le decine di preimpostati (dai nomi evocativi come "Satriani", "Jazzy", "Metal tone" ecc.). Gli effetti possono venire spenti/accesi, regolati nei vari parametri, riordinati come sequenza, sostituiti con altri.

Nella mia esperienza ho notato però alcune cosucce:

  1. Senza specificare nelle opzioni di preferenza l'abilitazione automatica degli effetti non ottenevo alcun suono: l'abilita/disabilita manuale sembrava non funzionare
  2. Per ottenere una migliore qualità del suono ho dovuto abbassare il livello di input (-20 circa) ed aumentare leggermente quello di output (+10 circa): così facendo ho eliminato una fastidiosa e non voluta distorsione, specie sugli accordi
  3. Probabilmente dipenderà dal mio portatile e dalla sua non proverbiale potenza, ma il segnale audio in uscita ha un ritardo di circa 1 secondo.

Audacity

Ed è finalmente giunto il momento di unire il tutto! Serve un software per fare editing multitraccia. Il più semplice, quasi banale, che conosco è Audacity.


Lo apro e importo come traccia audio la song esportata da Hydrogen, quindi in effetti->ripeti la faccio ripetere per 30 volte, ottenendo un bel 4/4 di 30 battute.

Nella toolbar ho un paio di combobox in cui specificare le sorgenti di input e output. Cerco JACK e lo imposto, quindi come input seleziono rakarrack (che mi viene mostrato grazie a JACK) e come output quello di sistema.

Faccio REC e suono: la mia chitarra distorta appare come linea bella frastagliata nella nuova traccia audio!

Al termine della registrazione mi rendo conto che il tutto è fuori tempo rispetto alla batteria... ricordate il problemino del ritardo? Già...

Ma niente paura, mi posiziono sul primo attacco della mia traccia di chitarra, seleziono tutto quel che segue, taglio e sposto il cursore con le frecce sul battere di cassa corretto. Incolla e via, il brano è di nuovo perfettamente a tempo.

Tutto il resto, ovvero registrare una buona traccia, è (purtroppo) un'altra storia...

venerdì 12 agosto 2011

Sviluppare giochi per Google+


Google+ non necessita ormai più di presentazioni. Ad ormai due mesi dal suo lancio la piattaforma social di Google ha guadagnato una notevole fetta di utenti che, per chissà quale motivo, sembrano essere in maggioranza tecnici e creativi del web: programmatori, grafici, musicisti e, naturalmente, tanti sviluppatori indie.

David Glazer, un responsabile di progetto di Google, ha annunciato sul suo profilo l'apertura di un interessante blog: the Google+ Platform blog.

Scopo del Platform Blog sarà quello di introdurre gli sviluppatori volenterosi alle API di integrazione di Google+, e aprire quindi la strada ad una nuova generazione di applicazioni e, quel che interessa a noi, giochi. E' infatti quel che viene promesso in questo post del blog ufficiale di G+.

Una nuova corsa all'oro quindi. Resta da capire se G+ opterà per un sistema di scrematura in stile Apple (che non sembra comunque aver dato i risultati attesi, si vedano le FartApp...) o se lascerà aperte le frontiere di questo nuovo far west.

mercoledì 10 agosto 2011

Il gioco indie che forse non conoscete: Legends of Yore!

Legends of Yore è un altro roguelike grafico ben fatto, ricco e profondo, che giunge in un momento di grande vitalità per il genere (Desktop Dungeons, Dungeons of Dredmor, Cardinal Quest, solo per citarne alcuni). Si può scegliere tra tre personaggi: l'immancabile guerriero e la sua controparte, il mago, più l'arciere, che va a riempire lo spazio solitamente occupato da classi quali il ladro.


I tre personaggi offrono opzioni, abilità e stili di gioco differenti, risultando comunque ben calibrate. Il mondo di gioco appare solido e la grafica a 8 bit, molto curata e coerente, aumenta la piacevole sensazione di trovarsi davanti ad un gioco completo.

I livelli dei dungeon sono generati proceduralmente ogni volta che vi ci accede, mentre le "esterne" (città, villaggi, boschi, pianure ecc.) sono formate da mappe fisse.

Lo sviluppatore sembra aver ovviato al problema del field of view del personaggio optando per una risoluzione video veramente infima: si gioca in un rettangolino di finestra degno di uno smartphone, e forse non a caso: Legends of Yore è infatti disponibile, oltre che per PC, per iOS e Android.

(NB: La versione per desktop contiene anche l'opzione per visualizzare a 800x600, ma in questo caso la mancata implementazione del fields of view risulta in un'area visualizzata troppo ampia che può, in parte, togliere il gusto dell'esplorazione.)

Altro punto a favore è dato dallo sviluppo ancora in corso del progetto, che anzi rilascia continui aggiornamenti (scaricati e installati automaticamente all'avvio del gioco). Potete seguire le vicende delle Leggende sul forum del sito e sul canale twitter.

In definitiva un altro bel colpo messo a segno da un singolo indie!

lunedì 8 agosto 2011

Tutorial: Scriviamo un gioco in C++... Tris! (parte 5)

La volta scorsa abbiamo implementato le funzionalità di menu, permettendo di raggiungere la schermata di gioco, tornare indietro e uscire. La stato di gioco mostrava però solo una schermata nera... ben poco entusiasmante!

Oggi colmeremo questa lacuna, giungendo finalmente ad implementare il gioco. Fino ad ora, infatti, abbiamo analizzato e scritto solo codice di contorno. Quel che faremo in questo post sarà riempire invece di istruzioni la classe PlayGameState. La struttura di PlayGameState è naturalmente la stessa di TitleScreenGameState, ereditando entrambe da AbstractGameState. Vediamo, metodo per metodo, come cambia invece l’implementazione.

Il Costruttore


PlayGameState::PlayGameState(ApplicationMessageHandler* pMessageHandler) :
AbstractGameState(pMessageHandler)
{
//ctor
m_pX = 0;
m_pO = 0;
m_pWinText = 0;
}

Poca roba. I tre puntatori che vengono inizializzati a null sono puntatori a SDL_Surface e conterranno le immagini rispettivamente del simbolo X (del player), del simbolo O (del computer) e del rendering di un testo che mostreremo in caso di vittoria di qualcuno.

Il Distruttore


PlayGameState::~PlayGameState()
{
//dtor

SDL_FreeSurface(m_pX);
SDL_FreeSurface(m_pO);

TTF_CloseFont(m_pFont);

if(m_pWinText)
{
SDL_FreeSurface(m_pWinText);
}
}

Osservare il distruttore è un po’ come leggere i titoli di coda di un film prima di aver visto il film! Possiamo capire chi erano gli attori e intuire quali parti recitavano. Vediamo infatti la liberazione dalla memoria delle tre SDL_Surface e la chiusura di un inedito TTF_Font.

Load


int
PlayGameState::Load()
{
int result = 0;
result = LoadOptimizedImage("tris_x.bmp", &m_pX);
result += LoadOptimizedImage("tris_o.bmp", &m_pO);

m_pFont = TTF_OpenFont("arial.ttf", 24);

return result;
}

Beh, qui in effetti qualche nodo giunge al pettine, e si scopre cosa sia il famigerato m_pFont: SDL_ttf ci permette infatti di creare un oggetto in grado di disegnare del testo su una surface con un certo font e una certa dimensione, prendendo le informazioni da uno standard file ttf. Se non avete mai affrontato il problema del rendering del testo in altri contesti grafici, non sapete quanto questo sia utile!

HandleKeyboardInput


void
PlayGameState::HandleKeyboardInput(const SDLKey& key, const SDL_EventType& type)
{
if(type == SDL_KEYDOWN)
{

switch(key)
{
case SDLK_ESCAPE:
m_pMessageHandler->HandleMessage(EMT_TITLE);
break;
}

}
}

Ok, niente di nuovo, semplicemente alla pressione di ESC mandiamo alla classe principale il segnale EMT_TITLE, che servirà a cambiare lo stato corrente da Play a TitleScreen.

HandleMouseInput


void
PlayGameState::HandleMouseInput(const int x, const int y, Uint8 button, const SDL_EventType& type)
{
SDL_Rect gridRect;
gridRect.h = 282; gridRect.w = 282; gridRect.x = 19; gridRect.y = 99;

// se qualcuno ha già vinto non si gioca più
if(m_PlayerWins || m_ComputerWins) return;

// ho fatto click dentro la griglia?
if(IsPointInRect(x, y, gridRect))
{
// tasto sinistro?
if(button == SDL_BUTTON_LEFT && type == SDL_MOUSEBUTTONUP)
{
// quale quadrante?
int gridx = (x - 19) / 94;
int gridy = (y - 99) / 94;

if(m_Grid[gridy][gridx] != EGS_NONE) return;

m_Grid[gridy][gridx] = EGS_X;

if(CheckPlayerVictory())
{
m_PlayerWins = true;
if(m_pWinText)
{
SDL_FreeSurface(m_pWinText);
}
SDL_Color textColor = {0,255,0};
m_pWinText = TTF_RenderText_Solid(m_pFont, "Player Wins!", textColor);
}
else
{

// eseguo la mossa del computer

if(m_SymbolsPlacedCount < 8)
{

bool ok = false;
while(!ok)
{
gridx = rand()%3;
gridy = rand()%3;

if(m_Grid[gridy][gridx] == EGS_NONE)
{
ok = true;
m_Grid[gridy][gridx] = EGS_O;
}
}

m_SymbolsPlacedCount += 2;
}

if(CheckComputerVictory())
{
m_ComputerWins = true;
if(m_pWinText)
{
SDL_FreeSurface(m_pWinText);
}
SDL_Color textColor = {255,0,0};
m_pWinText = TTF_RenderText_Solid(m_pFont, "Computer Wins!", textColor);
}
}
}
}
}

Uh! Questo è un bel pezzo di codice! Corposo e denso di significati... in effetti si tratta della gestione del click del mouse, ovvero il cuore del gameplay. Qui è, in sostanza, il posto dove si fa il gioco.

Scopo di questa funzione è aggiornare lo stato dell’oggetto m_Grid, una griglia 3x3 di un enumerativo che descrive i tre stati in cui si può trovare una cella: vuota, con una X o con un O. Ci sono anche un paio di booleani che tengono conto del fatto che qualcuno tra player o computer possa aver vinto la partita.

Il primo controllo che viene fatto è per capire se il click del mouse rientra nell’area in cui abbiamo disegnato la griglia (come avviene nel metodo Draw, naturalmente). Le costanti hardcodate (19, 94 e 99) sono una piccola vergogna che ho voluto lasciare, un po’ per far nascere nel lettore il giusto senso di ribrezzo, un po’ per non appesantire la leggibilità del codice con troppi nomi. Rappresentano comunque la distanza della griglia dal bordo (19 pixel) e la dimensione del lato di ogni cella (94 pixel). 99 è invece l’offset verticale (sempre in pixel) da cui parte la griglia. Non perdeteci tempo, sono solo numeri.

Una volta stabilito che il click è ricaduto all’interno della griglia, si tratta di decidere in quale cella. A questo scopo vi è il piccolo calcolo che porta a valorizzare le due variabili gridx e gridy, con le quali possiamo accedere alla matrice m_Grid e, se la cella è libera, segnarla con il simbolo del player, la X. Subito dopo parte una procedura che verifica se la mossa sia stata vincente per il player e, nel caso, imposta il booleano a guardia di questo evento e crea la SDL_Surface con il testo “Player Wins!” di colore verde.

Se il player non ha ancora vinto si procede invece con la mossa del computer. Questo è il punto in cui dovremo tornare in un secondo momento, quando si tratterà di sviluppare la AI. Per ora il computer è veramente sciocco e si limita a piazzare il suo segno in un punto a caso, scegliendolo pure in modo non troppo furbo (ci sarebbe potenzialmente un deadlock, ma sono talmente poche le possibilità che il prima o poi uno spiazzo vuoto lo trova dovrebbe portare via pochi cicli).

Viene infine incrementato un contatore delle mosse: già, perchè le partite finiscono anche in parità, quando nessuno fa tris ma non esistono più celle disponibili.

Il caso di vittoria del computer è, naturalmente, speculare a quello già visto per il player.

Update


void
PlayGameState::Update()
{

}

Mmm, ok, lo ammetto... Tris è veramente un gioco povero! La funzione Update ce lo dimostra...

Draw

void
PlayGameState::Draw()
{
SDL_Surface* screen = SDL_GetVideoSurface();

// sbianco lo schermo

SDL_FillRect(screen, 0, 0xffffff);

// disegno la griglia di gioco 3x3

// linee verticali
SDL_Rect grid;
grid.h = 282; grid.w = 3; grid.x = 19+94; grid.y = 99;

SDL_FillRect(screen, &grid, 0x000000);

grid.h = 282; grid.w = 3; grid.x = 19+94+94; grid.y = 99;
SDL_FillRect(screen, &grid, 0x000000);

// linee orizzontali
grid.h = 3; grid.w = 282; grid.x = 19; grid.y = 99+94;
SDL_FillRect(screen, &grid, 0x000000);

grid.h = 3; grid.w = 282; grid.x = 19; grid.y = 99+94+94;
SDL_FillRect(screen, &grid, 0x000000);

// disegno i simboli in griglia
SDL_Rect dst;
for(int r=0; r<3; ++r)
{
for(int c=0; c<3; ++c)
{
dst.h = 94; dst.w = 94; dst.y = 99 + (r*94); dst.x = 19 + (c*94);

switch(m_Grid[r][c])
{
case EGS_X:
SDL_BlitSurface(m_pX, 0, screen, &dst);
break;

case EGS_O:
SDL_BlitSurface(m_pO, 0, screen, &dst);
break;

case EGS_NONE:
break;
}
}
}

if(m_PlayerWins || m_ComputerWins)
{
dst.h = m_pWinText->h; dst.w = m_pWinText->w; dst.x = 160 - (m_pWinText->w / 2); dst.y = 240 - (m_pWinText->h / 2);
SDL_Rect box;
box.h = dst.h * 2; box.w = 320; box.x = 0; box.y = dst.y - (dst.h / 2);
SDL_FillRect(screen, &box, 0x000000);
SDL_BlitSurface(m_pWinText, 0, screen, &dst);
}
}

Ecco svelati i segreti dietro ai numeretti hardcodati! Lascio a voi l’analisi di questa funzione, che altro non fa che disegnare la griglia (usando delle chiamate a SDL_FillRect e disegnando con esse le quattro linee necessarie) e interpretare i dati nella matrice m_Grid per piazzare i giusti simboli all’interno delle celle.

Nel caso di vittoria di qualcuno, poi, viene mostrato il testo all’interno di un box nero, al centro dello schermo.

Reset


void
PlayGameState::Reset()
{
m_SymbolsPlacedCount = 0;

m_PlayerWins = m_ComputerWins = false;

if(m_pWinText)
{
SDL_FreeSurface(m_pWinText);
}
m_pWinText = 0;

for(int r=0; r<3; ++r)
{
for(int c=0; c<3; ++c)
{
m_Grid[r][c] = EGS_NONE;
}
}
}

L’implementazione dello stato di gioco ha reso necessaria l’introduzione di un ulteriore metodo virtuale puro nella classe base: Reset. Con Reset si riportano alla situazione iniziale tutte le variabili membro dello stato. Viene chiamata da Tris ogni volta che uno stato diventa lo stato attuale, permettendo in questo caso partite successive.

Condizioni di vittoria


bool
PlayGameState::CheckComputerVictory()
{
return CheckVictoryForSymbol(EGS_O);
}

//-----------------------------------------------------------------------------
bool
PlayGameState::CheckPlayerVictory()
{
return CheckVictoryForSymbol(EGS_X);
}

//-----------------------------------------------------------------------------
bool
PlayGameState::CheckVictoryForSymbol(PlayGameState::GridSymbols sym)
{
if(m_Grid[0][0] == sym)
{
if(m_Grid[0][1] == sym && m_Grid[0][2] == sym) return true;
if(m_Grid[1][1] == sym && m_Grid[2][2] == sym) return true;
if(m_Grid[1][0] == sym && m_Grid[2][0] == sym) return true;
}
if(m_Grid[1][0] == sym)
{
if(m_Grid[1][1] == sym && m_Grid[1][2] == sym) return true;
}
if(m_Grid[2][0] == sym)
{
if(m_Grid[2][1] == sym && m_Grid[2][2] == sym) return true;
}

return false;
}

Ed ecco infine le tre funzioni che verificano se qualcuno ha vinto. Sono tre, ma in realtà è una, come vedete. Il controllo è banale: semplicemente si verificano tutte le possibilità, senza scienze particolari.

Nella prossima puntata vedremo come istruire il computer per essere un po’ meno tonto... sì, parleremo di AI!

Buon coding!

mercoledì 3 agosto 2011

Il gioco indie che forse non conoscete: Continuity!

Continuity è un originale puzzle game sviluppato dagli svedesi Ragtime Games nel 2009, vincitore di ben due prestigiosi premi: il Best Student Game alll’Independent Games Festival (2010) e il Gameplay Innovation Award all’IndieCade (2010).



Una tale abbondanza di titoli non giunge mai a caso, e Continuity non sembra aver rubato proprio nulla. Il gameplay è una via di mezzo tra un platform, un puzzle e il cubo di rubik! Già al secondo dei trentadue livelli saremo costretti a capire e padroneggiare i (pochi) meccanismi necessari a risolvere il problema. Che è lo stesso, in tutti i livelli: raccogliere la chiave rossa e raggiungere la porta rossa, nella buona vecchia tradizione del minimalismo indie.

In questi giorni è disponibile anche Continuity 2 per iPhone e iPad, ovviamente su AppStore.

lunedì 1 agosto 2011

Tutorial: Scriviamo un gioco in C++... Tris! (parte 4)

Nella puntata precedente siamo arrivati ad avere la title screen mostrata a video e il nostro gioco che reagisce alla pressione del tasto ESC, chiudendosi.

In questa nuova sessione di coding vogliamo aggiungere i due tasti Play e Quit, gestire la loro pressione via click del mouse e spostare la funzione di chiusura dal tasto ESC alla pressione di Quit.


Vogliamo inoltre gestire la pressione di Play mostrando una seconda schermata, quella di gioco! Per ora la schermata di gioco non sarà altro che una schermata nera, dalla quale usciremo con ESC tornando alla title screen.

Entreremo più in profondità nella scrittura di codice e nella progettazione dell’architettura del programma, in particolare capiremo come gestire i messaggi tra le varie parti dell’applicazione.

Prima di fare tutto questo, però, dobbiamo rivedere un attimo l’organizzazione del codice, attività che andrebbe considerata dopo ogni sessione in cui si completa un pezzo dell’applicazione e ci si accinge ad aggiungere altre parti. Quelli bravi le hanno anche dato un nome: Refactor.

Il Refactor è lo spostare funzionalità da una parte all’altra del sistema, creando magari sottosistemi o eliminandone alcuni per inglobare le loro funzioni dentro altri. A volte si riduce banalmente alla rinomina di una classe o di qualche metodo. Il fine ultimo è sempre quello di rimodellare il codice in modo che appaia uniforme e in armonia nelle sue varie parti.

Abbiamo già fatto in realtà un piccolo intervento di refactoring nella terza parte, quando abbiamo introdotto la classe SDLManager spostandoci dentro le istruzioni che inizialmente avevamo messo nel main.

Ora l’intervento sarà un attimo più strutturato. Si tratta infatti di come vogliamo gestire gli stati dell’applicazione.

La gestione degli stati


Durante il suo ciclo di vita ogni applicazione, e i giochi non fanno eccezione, passa attraverso più stati, cioè più configurazioni, o modalità operative. Nel nostro TrisLick abbiamo individuato principalmente due stati:

  1. La Title Screen, che funge anche da menu
  2. Lo stato di Play

e abbiamo determinato le condizioni di passaggio da uno stato all’altro:

  • Title Screen -> (pressione tasto Play) -> Stato Play
  • Stato Play -> (fine partita) -> TitleScreen
  • Title Screen -> (pressione tasto Quit) -> Uscita dal gioco

A seconda dello stato in cui si trova, il programma dovrà disegnare cose differenti, aggiornare oggetti differenti, trattare l’input in modo differente, e così via.

Si potrebbe risolvere il tutto con dei valori booleani e un bell’albero di if-else, o con un enumerativo e uno switch, e nel caso del nostro Tris andrebbe anche bene, essendo solo due gli stati e non avendo in programma di ampliare il gioco. Ma questa soluzione è sicuramente poco flessibile e per nulla scalabile.

Possiamo invece approfittare della relativa semplicità del nostro scenario per gestire in modo più elegante e ad oggetti questo problema, creando una gestione basata sulla macchina a stati finiti.

Essenzialmente, una macchina a stati finiti (FSM da qui in avanti, Finite State Machine) è un sistema in grado di gestire entità chiamate stati, operando le opportune transazioni da uno all’altro in base agli input ricevuti.

Per quanto ci riguarda, possiamo astrarre uno stato come qualcosa in grado di:

  • ricevere e processare input da tastiera e mouse
  • aggiornare i propri oggetti
  • disegnare i propri oggetti

La classe Tris fungerà da FSM gestendo le transazioni tra i due stati, ma avrà sempre e solo uno di loro impostato come stato corrente. Sfruttando l’ereditarietà e il polimorfismo potremo astrarre dal concreto stato attuale e ragionare solo in termini di stato astratto.

Ho perso qualcuno? Tranquilli, tra poco il codice renderà tutto molto più chiaro. Dobbiamo però introdurre ancora un concetto: la gestione dei messaggi dagli stati all’applicazione. Se spostiamo infatti la gestione dell’input dalla classe Tris alle classi di stato, come facciamo a reagire, ad esempio, alla pressione di ESC impostando un valore della classe Tris? La soluzione più ovvia sembrerebbe quella di fornire le classi di stato di un riferimento alla classe Tris, ma così facendo incapperemmo in un brutto riferimento circolare, in cui Tris conosce lo stato A e lo stato A conosce Tris.

Il modello ad oggetti ci permette, per fortuna, di essere un po’ più eleganti. Possiamo infatti creare una classe astratta ApplicationMessageHandler che contiene un metodo virtuale puro, HandleMessage. Gli stati devono conoscere questa classe, a loro infatti interessa solo comunicare un messaggio. Naturalmente, essendo ApplicationMessageHandler una classe astratta, andrà derivata da qualcuno, e questo qualcuno dovrà poi gestire il metodo HandleMessage. Credo siamo tutti d’accordo nel far implementare questa interfaccia alla nostra bella classe Tris!

Ma passiamo al codice. Iniziamo col definire la classe base (astratta) per gli stati: AbstractGameState.

#ifndef ABSTRACTGAMESTATE_H
#define ABSTRACTGAMESTATE_H

#include <SDL/SDL.h>
#include "ApplicationMessageHandler.h"

class AbstractGameState
{
public:
AbstractGameState(ApplicationMessageHandler* pMessageHandler);
virtual ~AbstractGameState();

virtual int Load() = 0;

virtual void HandleKeyboardInput(const SDLKey& key, const SDL_EventType& type) = 0;
virtual void HandleMouseInput(const int x, const int y, Uint8 button, const SDL_EventType& type) = 0;

virtual void Update() = 0;
virtual void Draw() = 0;

protected:

int LoadOptimizedImage(const char* filename, SDL_Surface** destination);

bool IsPointInRect(int x, int y, const SDL_Rect& rect);

protected:

ApplicationMessageHandler* m_pMessageHandler;

private:
};

#endif // ABSTRACTGAMESTATE_H

Come vedete si tratta di una classe con tutti i metodi virtuali puri tranne costruttore e distruttore. Tutto quel che possiamo fare con questa classe, o interfaccia, è derivarla su classi concrete. Non è una gran sorpresa dirvi che le due classi concrete saranno TitleScreenGameState e PlayGameState.

Il metodo LoadOptimizedImage non è altro che una utilità per svolgere il lavoro attualmente svolto nella classe Tris: caricare un’immagine da disco e provare a produrne la versione ottimizzata per il blitting. Ponendo questa funzione qui ce la ritroveremo gratis in tutti gli stati, migliorando di molto sia la gestione del codice, sia la sua leggibilità.

IsPointInRect è una seconda utilità che svolge un semplice calcolo: ci dice se il punto di coordinate x, y ricade all’interno di un rettangolo rect. Ci tornerà utile quando si tratterà di intercettare i click del mouse.

Notate anche l’inclusione di ApplicationMessageHandler, che iniettiamo nello stato attraverso il suo costruttore, ponendola nella variabile membro m_pMessageHandler.

L’interfaccia di gestione dei messaggi è molto semplice. Per ora la definiamo così, ci riserviamo però la possibilità di ampliarla in futuro:

#ifndef APPLICATIONMESSAGEHANDLER_H
#define APPLICATIONMESSAGEHANDLER_H

enum MessageType
{
EMT_QUIT
};

class ApplicationMessageHandler
{
public:
ApplicationMessageHandler();
virtual ~ApplicationMessageHandler();

virtual void HandleMessage(const MessageType& type) = 0;

protected:
private:
};

#endif // APPLICATIONMESSAGEHANDLER_H

Ora è il momento di creare le classi concrete degli stati. Entrambe avranno la stessa struttura, quindi riporto qui la dichiarazione della sola TitleScreenGameState a mo’ di esempio:

#ifndef TITLESCREENGAMESTATE_H
#define TITLESCREENGAMESTATE_H

#include "AbstractGameState.h"

class TitleScreenGameState : public AbstractGameState
{
public:
TitleScreenGameState(ApplicationMessageHandler* pMessageHandler);
virtual ~TitleScreenGameState();

virtual int Load();

virtual void HandleKeyboardInput(const SDLKey& key, const SDL_EventType& type);
virtual void HandleMouseInput(const int x, const int y, Uint8 button, const SDL_EventType& type);

virtual void Update();
virtual void Draw();

protected:
private:
SDL_Surface* m_pBackground;
SDL_Surface* m_pQuitButton;;
SDL_Surface* m_pPlayButton;
SDL_Rect m_QuitBlitRect, m_PlayBlitRect;
};

#endif // TITLESCREENGAMESTATE_H

L’implementazione di TitleScreenGameState è piuttosto semplice: non facciamo altro che caricare le immagini all’interno della Load, gestire l’input in HandleMouseInput e disegnare il tutto nella Draw.

#include "../include/TitleScreenGameState.h"
#include <iostream>

using namespace std;

//-----------------------------------------------------------------------------
TitleScreenGameState::TitleScreenGameState(ApplicationMessageHandler* pMessageHandler) :
AbstractGameState(pMessageHandler)
{
//ctor
}

//-----------------------------------------------------------------------------
TitleScreenGameState::~TitleScreenGameState()
{
//dtor
SDL_FreeSurface(m_pBackground);
SDL_FreeSurface(m_pPlayButton);
SDL_FreeSurface(m_pQuitButton);
}

//-----------------------------------------------------------------------------
int
TitleScreenGameState::Load()
{
int result = 0;
result = LoadOptimizedImage("title_screen.bmp", &m_pBackground);
result += LoadOptimizedImage("button_play.bmp", &m_pPlayButton);
result += LoadOptimizedImage("button_quit.bmp", &m_pQuitButton);

// mettiamo da parte nei due Rect le informazioni sulla posizione
// e dimensione dei due pulsanti. Le useremo quando occorrerà
// gestire il click del mouse

m_PlayBlitRect.h = m_pPlayButton->h;
m_PlayBlitRect.w = m_pPlayButton->w;
m_PlayBlitRect.x = 160 - (m_PlayBlitRect.w/2);
m_PlayBlitRect.y = 320;

m_QuitBlitRect.h = m_pQuitButton->h;
m_QuitBlitRect.w = m_pQuitButton->w;
m_QuitBlitRect.x = 160 - (m_QuitBlitRect.w/2);
m_QuitBlitRect.y = 320 + m_PlayBlitRect.h + 2;

return result;
}

//-----------------------------------------------------------------------------
void
TitleScreenGameState::HandleKeyboardInput(const SDLKey& key, const SDL_EventType& type)
{
if(type == SDL_KEYDOWN)
{

switch(key)
{
case SDLK_ESCAPE:
m_pMessageHandler->HandleMessage(EMT_QUIT);
break;
}

}
}

//-----------------------------------------------------------------------------
void
TitleScreenGameState::HandleMouseInput(const int x, const int y, Uint8 button, const SDL_EventType& type)
{
// click all'interno del button quit?
if(IsPointInRect(x, y, m_QuitBlitRect))
{
// tasto sinistro?
if(button == SDL_BUTTON_LEFT && type == SDL_MOUSEBUTTONUP)
{
m_pMessageHandler->HandleMessage(EMT_QUIT);
}
}
// button play?
else if(IsPointInRect(x, y, m_PlayBlitRect))
{
// tasto sinistro?
if(button == SDL_BUTTON_LEFT && type == SDL_MOUSEBUTTONUP)
{
m_pMessageHandler->HandleMessage(EMT_PLAY);
}
}
}

//-----------------------------------------------------------------------------
void
TitleScreenGameState::Update()
{

}

//-----------------------------------------------------------------------------
void
TitleScreenGameState::Draw()
{
SDL_Surface* screen = SDL_GetVideoSurface();

SDL_BlitSurface(m_pBackground, 0, screen, 0);

SDL_Rect dst = m_PlayBlitRect;
SDL_BlitSurface(m_pPlayButton, 0, screen, &dst);

dst = m_QuitBlitRect;
SDL_BlitSurface(m_pQuitButton, 0, screen, &dst);
}

Non ci resta altro che vedere come è cambiata la classe Tris. Non conterrà infatti più alcun riferimento alla SDL_Surface con la bitmap della TitleScreen; avrà invece un oggetto TitleScreenGameState da gestire.

Prestate attenzione al codice di gestione dell’input: noterete che è indipendente dallo stato in cui ci si trova, come altre parti dell’applicazione (Update e Draw). Viene infatti utilizzato il puntatore m_pCurrentState, di tipo generico AbstractGameState. E’ compito della FSM impersonata da Tris popolare correttamente questo puntatore con l’istanza di stato corretta (vedi gestione dei messaggi): al resto della classe non interessa sapere in quale stato ci si trova! Questo ci offre un buon grado di flessibilità e scalabilità: aggiungere/eliminare una schermata (uno stato) diventa molto più semplice, dovendo toccare solo pochi punti.

Ecco il nuovo codice di Tris:

#include "../include/Tris.h"
#include <iostream>

using namespace std;

//-----------------------------------------------------------------------------
Tris::Tris()
{
//ctor

m_pSDLManager = 0;
m_IsRunning = false;
m_pTitleScreenGS = 0;
m_pPlayGS = 0;
m_pCurrentState = 0;
}

//-----------------------------------------------------------------------------
Tris::~Tris()
{
//dtor

delete m_pSDLManager;
m_pSDLManager = 0;

delete m_pTitleScreenGS;
m_pTitleScreenGS = 0;

delete m_pPlayGS;
m_pPlayGS = 0;
}

//-----------------------------------------------------------------------------
int
Tris::StartApplication()
{
m_pSDLManager = new SDLManager();

int result = m_pSDLManager->Init();

if(result != 0)
{
return 1; // errore
}

m_IsRunning = true;

// carico lo stato TitleScreen
m_pTitleScreenGS = new TitleScreenGameState(this);
result = m_pTitleScreenGS->Load();

// carico lo stato Play
m_pPlayGS = new PlayGameState(this);
result += m_pPlayGS->Load();

// imposto come stato corrente lo stato di TitleScreen
m_pCurrentState = m_pTitleScreenGS;

return result;
}

//-----------------------------------------------------------------------------
void
Tris::StopApplication()
{
m_pSDLManager->Quit();
}

//-----------------------------------------------------------------------------
bool
Tris::IsRunning()
{
return m_IsRunning;
}

//-----------------------------------------------------------------------------
void
Tris::HandleInput()
{
SDL_Event event;

while(SDL_PollEvent(&event))
{
switch(event.type)
{

case SDL_KEYDOWN:
m_pCurrentState->HandleKeyboardInput(event.key.keysym.sym, SDL_KEYDOWN);
break;

case SDL_MOUSEBUTTONUP:
m_pCurrentState->HandleMouseInput(event.button.x, event.button.y, event.button.button, SDL_MOUSEBUTTONUP);
break;

}
}
}

//-----------------------------------------------------------------------------
void
Tris::Update()
{
m_pCurrentState->Update();
}

//-----------------------------------------------------------------------------
void
Tris::DrawAll()
{
SDL_Surface* screen = SDL_GetVideoSurface();

// pulisco lo schermo
SDL_FillRect(screen, &screen->clip_rect, 0x000000);

// disegno lo stato corrente
m_pCurrentState->Draw();

// svuoto il buffer (invio le istruzioni per il disegno reale a schermo)
SDL_Flip(screen);
}

//-----------------------------------------------------------------------------
void
Tris::HandleMessage(const MessageType& type)
{
switch(type)
{
case EMT_QUIT:
m_IsRunning = false;
break;

case EMT_PLAY:
m_pCurrentState = m_pPlayGS;
break;

case EMT_TITLE:
m_pCurrentState = m_pTitleScreenGS;
break;
}
}

Ora lanciamo l’applicazione et voilà, otteniamo quel che ci eravamo proposti!