mercoledì 29 giugno 2011

5 consigli pratici per sviluppare videogiochi (parte 3)

Dopo aver parlato di quanto sia importante partire con qualcosa di piccolo e realizzabile e aver chiari e sottomano gli strumenti da utilizzare, introduciamo il terzo dei cinque suggerimenti per scrivere un videogioco:

Consiglio #3: Siate autosufficienti


Uno degli errori più comuni, notabile ad esempio nei forum e nelle comunità online in genere, è l’approccio di questo tipo:

ciao a tutti, ho una grande idea per un gioco! Si tratta in pratica di … (sostituire con specifiche a piacere, purchè contengano riferimenti ad almeno 5 giochi AAA anche, anzi meglio, di natura differente tra loro)! Io e un mio amico possiamo occuparci della grafica, anche se non siamo molto bravi. Ci servirebbe un programmatore per l’engine, un paio per il gioco e uno per gli editor! E poi grafici, che più siamo meglio è XD! Anche musicisti sono bene accetti! ROTFL

Inutile dire che un messaggio come questo non riceverà alcuna risposta positiva, salvo forse quella di un altro personaggio simile, che tuttavia rilancerà proponendo il suo di gioco. Il più delle volte, dopo qualche saggia risposta, il titolare del post scrive qualcosa del tipo:

beh vabbè... pensavo di trovare degli appassionati qui... cmq fa lo stesso, ciao

e l’ennesimo avatar viene abbandonato per sempre a quota 2 post.

Ora, perchè questo approccio è sbagliato? Perchè mostra entusiasmo? No di certo, anche se l’autore se ne va offeso proprio con questa idea in testa. Perchè viola il primo consiglio? Certo, lo viola eccome, ma non si tratta nemmeno di questo.

E’ un approccio sbagliato perchè l’autore cerca qualcuno che gli faccia qualcosa. Non propone nulla di concreto, non mostra alcun prototipo o abilità. Piuttosto, quel che ne emerge sono idee sparse e ben confuse. Chi lo seguirà lo farà senza sapere che il loro viaggio non li porterà da nessuna parte. Con queste premesse il fallimento è inevitabile. Sempre.

Lo sviluppatore indie/amatoriale deve essere autosufficiente. Deve saper produrre, da solo, almeno il prototipo funzionante del suo gioco. Poco importa che ci siano quadrati blu e rossi in uno schermo bianco e nero. Poco importa che sia sviluppato in C++ con un elegante modello ad oggetti o con il Game Maker della situazione. Poco importa che abbia una colonna sonora originale o qualche effetto scaricato dalla rete.

Dovete essere in grado di fare da soli, dalla A alla Z, il vostro gioco, perchè nessuno realizzerà la vostra idea. La gente è già piena delle proprie, di idee. Non ha tempo nè voglia per pensare a quelle altrui.

Un’idea da sola non basta per ottenere collaboratori seri. Occorre un prototipo, che mostri in un tempo solo sia l’idea, sia la volontà e le capacità per realizzarla. Voi per primi dovete avere le mani sporche di terra, altrimenti nessuno vi aiuterà a scavare per quanto possiate urlare “ehi, ho in mente di fare una grande buca, proprio qui!”.

C’è invece la possibilità che, urlandolo da dentro la buca, qualcuno si incuriosisca, si rimbocchi le maniche e vi raggiunga.

Post correlati:

lunedì 27 giugno 2011

5 consigli pratici per sviluppare videogiochi (parte 2)

Nella prima parte di questo prontuario abbiamo visto quanto sia importante ragionare ed agire in piccolo, tenendo a bada le idee. Questa seconda parte si focalizza su un aspetto molto pratico: gli strumenti da usare.

Consiglio #2: usa lo strumento con cui ti trovi meglio


Lo strumento non è in realtà uno solo. Parlando di videogiochi abbiamo una vasta gamma di ambienti e di tool da padroneggiare. Tra questi però ne spiccano almeno tre che meritano attenzione particolare: il linguaggio, le librerie, l’IDE.

1) Il Linguaggio
Prendete un forum di programmazione, uno qualsiasi. Andate nella sezione “Beginner” e cercate il primo post (sì, ce ne è solitamente più di uno) intitolato “Quale linguaggio devo studiare per fare un videogioco?”. All’interno troverete una massa di avatar in guerra, ognuno proclamando il tale linguaggio come migliore per questo o l’altro punto, e additando altri linguaggi come inadatti, brutti, obsoleti o altri giudizi negativi a caso. Una vera guerra di religione. In mezzo a questo marasma vedrete però anche pochi (e inascoltati) illuminati scrivere un semplice messaggio di pace: usa quello che conosci.

Che sia C++, Java, Python, D, C, C#, Pascal oppure, sì, addirittura Visual Basic, ognuno di essi ti potrà portare là dove vuoi andare. Hanno tutti in comune il fatto di essere linguaggi di programmazione, il che vuol dire che hanno superato alcuni esami e che sono stati ritenuti sufficientemente espressivi (la potenza è altra cosa) per descrivere e risolvere problemi informatici. Tutti possono svolgere calcoli, eseguire cicli, confrontare valori. L’unica differenza sta in te. Usa quello che conosci meglio, o che ti diverte di più, o che hai voglia di usare. Se sei fortunato, tutte e tre le risposte punteranno allo stesso linguaggio.

Ricorda però: un videogioco è un programma complesso. Assicurati di padroneggiare almeno i costrutti e le nozioni di programmazione di base prima di cimentarti in un progetto simile. Partire scarsamente equipaggiati porta solo alla frustrazione.

2) Le librerie
Il secondo strumento da scegliere è dato dal malloppone di codice di terze parti che dovrai includere nel tuo progetto. Da questo non si scappa. Anche se vuoi scrivere tutto da zero dovrai per forza partire da qualcosa, fosse anche solo il sistema operativo e le sue API per interfacciarsi a tastiera, mouse e schermo.

Potresti avere bisogno di un vero e proprio game engine: una libreria che ti metta a disposizione funzioni per gestire la grafica, la fisica e la logica del tuo gioco. Questa scelta non è facile come appare. I game engine sono grossi. La loro conoscenza è costosa in termini di tempo. Richiedono esperienza per essere usati al meglio e, spesso, nozioni extra per capire quel che stanno facendo al posto nostro, in modo da risolvere gli inevitabili problemi.

Spaventato da tutto questo, potresti decidere di non utilizzare un game engine. Magari potresti limitarti ad usare un graphic engine, un motore cioè che gestisca solo la parte grafica, e implementare da te gli aspetti di fisica e logica. O una qualsiasi combinazione di questi tre elementi. Il comune denominatore è sempre e solo uno: qualunque cosa tu decida di usare, assicurati di conoscerla.

Se anche non volessi usare alcun engine dovresti comunque scegliere una libreria grafica, ammesso che il tuo gioco coinvolga la grafica. Ce ne sono diverse, per scopi e target diversi. Dovresti conoscerne almeno un paio già da ora, tipo SDL, OpenGL, SFML, DirectX, Allegro o PyGame. Alcune di queste sono pure API grafiche, altre gestiscono anche altri aspetti del sistema. Se stai pensando ad un gioco testuale potresti aver bisogno delle ncurses o altra libreria derivata. Se non sai di cosa hai bisogno... beh, hai un problema a monte. Risolvi prima quello.

Scegli tutto il codice di terze parti di cui hai bisogno e che vuoi usare. Assicurati di conoscerlo, almeno nelle sue componenti fondamentali e nei suoi scopi, e scrivi subito un hello world in cui tutte le librerie compilano e linkano correttamente. Non vuoi sorprese più avanti. Hai pensato alla GUI? Non sottovalutarla, è spesso causa di morte del progetto.

3) L’ambiente di sviluppo
Hai un IDE preferenziale? Il tuo IDE deve accordarsi al linguaggio così come le tue librerie, ma non solo. L’IDE è qualcosa di più. E’ il luogo in cui passi la maggior parte del tuo tempo di sviluppatore. Devi poterlo chiamare casa.

L’IDE migliore non è necessariamente quello più completo e ricco. Anzi.

Il rapporto di uno sviluppatore col proprio IDE è qualcosa di intimo. A volte sfugge alla logica. Ad esempio, c’è stato un periodo in cui il mio IDE preferito era Dev-C++ della Bloodshed. Era già un colabrodo allora e crashava almeno due volte al giorno, ma aprirlo mi dava la netta sensazione di essere a casa mia. Non c’era nulla di ostile in lui. Ancora oggi, sebbene non lo utilizzi più da un pezzo, mi trasmette quelle sensazioni.

Al contrario, IDE più blasonati come Eclipse e NetBeans non sono mai riusciti a conquistarmi.

L’IDE è personale. Ciò significa che si deve adattare anche al nostro carattere. C’è chi si affeziona alle cose o ai marchi e, fatta una scelta, vi rimane fedele. Ci sono i fanboy di questa o quella tecnologia. Ma c’è anche chi si stufa della solita minestra e ama cambiare.

L’IDE è casa tua, abbiamo detto. C’è chi ama i castelli e le ville, chi invece le capanne e le tende. O chi in estate vuole stare in tenda e in inverno in villa.

Non state lavorando per qualcuno. Non state nemmeno lavorando, diamine! Vi state divertendo! Non dovete seguire alcuno standard commerciale, a meno che non lo vogliate. Non date retta a chi vi dice che dovete usare un certo IDE piuttosto che un altro.

L’IDE è un bel vestito messo su un insieme di tecnologie di base: editor di testi, debugger e programmi di compilazione. Iniziate il vostro progetto in un ambiente per voi confortevole e in cui avete voglia di stare per un po’.

4) Tool ausiliari
Non mi dilungherò sugli altri strumenti, ma probabilmente avrete bisogno di un editor di immagini e/o di un ambiente di modellazione 3D. Forse di un editor audio. Se siete veramente organizzati e pignoli avrete magari preso in considerazione anche un sistema di versioning. In tutti questi casi la regola è sempre la stessa: prendete ciò che conoscete.

Tutto questo ha una conseguenza implicita: dovete conoscere le cose. Il che significa informarsi, cercare, provare, sporcarsi le mani, fare esperimenti.

Che è forse il vero consiglio di questo articolo.

Post correlati:

5 consigli pratici per sviluppare videogiochi (parte 1)

venerdì 24 giugno 2011

5 consigli pratici per sviluppare videogiochi (parte 1)

Se programmare è divertente, programmare videogiochi è il massimo. La brutta notizia è che programmare videogiochi è anche dannatamente difficile.

Al di là delle complessità tecniche, il peggior nemico di un aspirante game developer è sicuramente egli stesso. Se avete mai tentato questa strada, sapete che sto parlando della pericolosità intrinseca di idee, emozioni ed entusiasmi.

Le idee, alimentate dagli immediati entusiasmi, nascono come funghi, si sviluppano in modo incontrollato, si accavallano, si soffocano l’una con l’altra. Ci eccitano, ci emozionano, ci spingono a farle crescere (e, quindi, cambiare) prima ancora di averle realizzate. Ci distraggono.

Ci sovrastano.

Ci fanno abbandonare il progetto.

Tutto questo, naturalmente, ha origine nella nostra testa, da cui la teoria che noi stessi siamo il nemico da sconfiggere.

Propongo qui la prima di 5 regole pratiche, quasi zen, che distillano quelle che ritengo essere le best practices dello sviluppatore. Io le ho violate tutte indiscriminatamente più e più volte, e posso quindi affermare che non seguirle non porta a nulla di buono.

Consiglio #1: Pensa piccolo (e riduci)


Il primo punto è, forse, anche il più difficile da realizzare, nonostante l’aspetto innocuo. Richiede infatti una notevole forza di volontà unita a capacità di analisi e sintesi.

Supponiamo che vi sia venuta l’idea per un gioco. Non è grave, c’è di peggio nella vita. La vera disgrazia è se l’idea vi eccita oltre misura. E’ buon sintomo di qualcosa di troppo grosso ed elaborato, qualcosa che probabilmente non vedrà mai la luce. Pensate alla vostra capacità di sviluppatori come ad un foro in una lastra d’acciaio. Quanto misura di diametro? 2 cm? 10 cm? 30 cm? Bene. Pensate ora alla vostra idea come ad un blocco di marmo (notare: un blocco, non una sfera). Quanto misura di lato? 1 metro? 3 metri? 100 metri? A voi la conclusione, sappiate solo che la vostra idea sta dall’altra parte di quella lastra d’acciaio, e dovete farla passare dal vostro foro.

Il lavoro da fare, naturalmente, è ridurre l’idea, eliminando tutto quel che vi sta intorno, spogliandola fino a mostrarne solo il cuore. Questo processo vi lascierà con un blocco di marmo molto più piccolo e diverso dall’originale, forse ancora squadrato e spigoloso, ma già quasi in grado di passare dal foro.

Ci sono due modi per affrontare questo passo. Il primo, più difficile e che richiede maggiore esperienza, è partire già da un’idea di base semplice, aggiungervi qualcosa per farla diventare interessante e passarla poi attraverso il setaccio per eliminare nuovamente tutto quel che c’è di troppo. Come già detto, occorre esperienza e una alta comprensione (a priori) dei propri limiti. Il più delle volte, di conseguenza, la situazione di partenza non è questa, ma l’esatto contrario: partire da un’idea folle e fuori controllo (ma che allo stato attuale giudicherete perfettamente fattibile, purtroppo).

A questo punto, quanto e come ridurre è dato principalmente dalle vostre attuali capacità. Se, ad esempio, non avete mai fatto nulla in 3D, ragionare su un’idea che prenda Doom 3 come punto di partenza è follia. Ma se dobbiamo fare un esempio tanto vale esagerare. Diciamo che la vostra idea sia proprio questa: Doom 3. Qualcosa di simile, magari ambientato nel medioevo, con magie al posto dei cannoni al plasma e castelli al posto delle basi spaziali. Vediamo come applicare il principio di riduzione.

Prendete il vostro Doom 3 medievale e togliete il medioevo, le magie e i castelli. Vi rimane un tizio grosso che gira per un labirinto, inseguito però da mostri che sbucano da tutte le parti!

Ora togliete i mostri e sostituite il tizio grosso con qualcosa che sapete di poter gestire, anche graficamente. Siate onesti con voi stessi. A meno che non siate dei buoni grafici/modellatori (ma siete già dei programmatori, quindi non prendiamoci in giro...) sarete costretti a ridurre il vostro uomo ad una figura geometrica. Una sfera.

Pensateci un po’ su, poi fatevi un favore personale e togliete la sfera. Metteteci un cubo. Ah, molto meglio.

Ed ecco la vostra idea, ridotta all’osso. Un cubo che si muove in un labirinto (di pareti vuote e monocolore). Vi verrà la fortissima tentazione di aggiungere “randomicamente generato” alla parola “labirinto”. Resistete, se possibile. Almeno per ora.

Ci deve essere però un qualche pericolo in questo labirinto, qualcosa che renda il girovagare del vostro cubo avvincente. Ma avete tolto diavoli e mostri. C’è però un nemico invisibile, che non richiede uno sforzo eccessivo per essere gestito: il tempo.

Fuga dal labirinto in una corsa contro il tempo. Ecco, l’idea è stata ridotta all’osso. Certo, è cambiata, quasi non somiglia all’originale ed è sicuramente meno attraente e meno ricca, ma... sapete metterla in pratica? Sapete scrivere questo gioco? Se sì, scrivetelo. Se no, continuate a ridurre. Potete eliminare una buona fetta della complessità passando dal 3D al 2D, ad esempio.

Quando riuscirete davvero a realizzare la vostra idea ridotta (chi ha detto mutilata?!?...) avrete fatto un passo da gigante nel mondo della programmazione di videogiochi.

Concludo citando Rodain Joubert, il realizzatore di Desktop Dungeons:

If you can abstract a game to its building blocks – stripping away concerns of engineering, art, or any of the other stuff that come later – you’ll be able to see what makes other games great and apply that sort of knowledge to your own projects
Rodain Joubert, Desktop Dungeons

giovedì 23 giugno 2011

HowTo: Rendere multilingua la propria iPhone App

Una delle feature più utili nelle applicazioni per dispositivi mobili è sicuramente la localizzazione (o internazionalizzazione) dell’interfaccia, ovvero la possibilità di mostrare messaggi agli utenti nella loro lingua. Sì insomma, il multilingua.

Se state sviluppando una App per l’iPhone e non sapete come inserire questa funzionalità, ecco un brevissimo tutorial. Brevissimo perchè, come vedrete, si tratta di un’operazione estremamente semplice proprio perchè ben integrata in iOS.

Step 1) Creare i file Localizable.strings


Nella directory del vostro progetto XCode dovreste avere una cartella xx.lproj, dove xx è il codice ISO a due caratteri della nazione predefinita, solitamente ‘en’. Supponendo che vogliate rendere la vostra App bilingua inglese-italiano, create nello stesso punto una cartella e rinominatela it.lproj.

Ora create due file di testo, rispettivamente in en.lproj e in it.lproj, e chiamateli entrambi Localizable.strings.

Includete i due file (vuoti) nel progetto da XCode. Se state usando la versione 4, potete includerli sotto il gruppo Supporting Files.

Step 2) Inserire le stringhe di messaggi


Ora aprite il Localizable.strings nella directory en.lproj e aggiungete la riga seguente:

“Label_Text” = “Test Message”;

Mentre nel file in it.lproj scrivete:

“Label_Text” = “Messaggio di Test”;

Step 3) Collegare le stringhe agli oggetti di interfaccia


Create ora una label in una qualche vista della vostra App e, nel ViewDidLoad, inserite questo codice:

[self.label setText:NSLocalizedString(@”Label_Text”, @””)];

Lanciate la App et voilà! Il vostro messaggio verrà visualizzato nella lingua corretta a seconda delle impostazioni di localizzazione del vostro telefono (o del simulatore).

Il funzionamento è intuitivo: dentro i file Localizable.strings potete mettere delle coppie di stringhe chiave/valore (separate da ‘;’) che richiamerete poi utilizzando la funzione di libreria <code>NSLocalizedString</code> che accetta in input come primo parametro la chiave della stringa da visualizzare e come secondo un commento alla stringa stessa. L’utilità del secondo parametro è legata a caratteristiche più avanzate di questo comando. Potete ignorarlo e passargli una stringa vuota, oppure scoprirne di più nella documentazione ufficiale, nel caso siate interessati ad approfondire.

Buon coding!

Riferimenti:


Documentazione NSLocalizedString

lunedì 20 giugno 2011

HowTo: Serializzare strutture su file binari in C++

La serializzazione dei dati è un aspetto spesso cruciale in una applicazione. E’ utile sia per lo svolgimento del normale flusso logico del programma (salvare e caricare record di dati, ad esempio) sia per scopi di debug e diagnostica (file di log di errori ed eccezioni).

Se serializzare (scrivere su file) dati in puro testo è però operazione piuttosto comune e ben nota, fare lo stesso con file binari è spesso un’opzione poco esplorata. I file di testo, o ASCII, hanno principalmente tre grandi vantaggi: sono semplici, umanamente comprensibili e facilmente modificabili all’esterno dell’applicazione con un qualsiasi editor di testo. Presentano però anche alcuni svantaggi, in particolare la difficoltà nel salvare strutture dati complesse, e ancor più rileggerle. Per questi scenari occorrono infatti dei parser di stringhe la cui complessità va rapidamente fuori controllo.

Se da una parte potremmo uscirne elegantemente utilizzando XML, dall’altra dobbiamo poter contare anche su una alternativa altrettanto valida: utilizzare file binari.

Il file binario presenta l’indubbia scomodità di essere illeggibile a occhio nudo e modificabile solo tramite le procedure scritte nel nostro programma. Accanto a questi due limiti, però, bisogna elencare le sue caratteristiche positive:

  • Possibilità di serializzare e rileggere facilmente strutture complesse
  • Dimensioni ridotte del file prodotto (rispetto al corrispettivo ASCII)

Volendo poi, la caratteristica di essere illeggibili e difficilmente modificabili può divenire, a volte, una cosa positiva. Pensate di scrivere un videogioco e di serializzare i dati dei vostri livelli su file: non sarebbe il massimo se chiunque potesse aprire il file con notepad e cambiare il vostro curatissimo design!

Lo scenario


Ma torniamo a noi. Il C++ ci mette a disposizione la libreria standard fstream per lavorare su file, sia di testo che binari.

Supponiamo di voler serializzare una struttura dati così definita:

struct PlayerData
{
   unsigned int energy;
   unsigned int score;
   double x;
   double y;
   double z;
}

La nostra struct PlayerData contiene le informazioni di base del giocatore di un ipotetico sparatutto in 3D: l’energia, il punteggio e le tre coordinate di spazio che definiscono la sua posizione nel mondo.

Se volessimo salvare questi dati utilizzando un file di testo saremmo costretti a scegliere qualche strategia, tipo: un valore su ogni riga, utilizzo di un carattere separatore, valori in posizioni assolute nel file ecc.

Oltre a portare a facili errori, queste strategie sono anche rigide ai cambiamenti, avendo natura posizionale.

Vediamo invece come possiamo raggiungere lo stesso obiettivo con un file binario.

Scrittura binaria


Iniziamo con l’includere la nostra libreria standard:

#include <fstream>

L’oggetto che dobbiamo istanziare è di tipo ofstream (output file stream). Ecco il codice:

PlayerData data;
data.energy = 100;
data.score = 12090;
data.x = 34.56778;
data.y = -87.24573;
data.z = 487.2198;

ofstream file;
file.open(“player.dat”, ios_base::binary);

if(file.good())
{
   file.write((char*)&data, sizeof(PlayerData));
}
else
{
   cout << “Errore durante l’apertura in scrittura del file player.dat!” << endl;
}

file.close();

Una volta popolata una struttura PlayerData andiamo ad aprire il nostro file specificandone il nome (l’estensione .dat è arbitraria: potete chiamarlo anche .pippo ;)) e, cosa importante, la modalità binaria.

Se la open va a buon fine la good() restituirà true e andremo a gestire la scrittura vera e propria. Come vedete è solo una riga! Ma può apparire un po’ criptica a prima vista. Partiamo quindi col vedere come è dichiarata la funzione write della classe ofstream:

ostream& write ( const char* s , streamsize n );

Accetta in input due argomenti: una stringa di caratteri e un valore che ne indica la lunghezza. Piuttosto lineare, ed infatti è la stessa funzione che useremmo per scrivere un semplice file di testo.

Quel che facciamo è un piccolo trucchetto: passiamo sì come primo parametro un puntatore, ma non ad un array di caratteri, bensì alla nostra struct. Per farlo digerire alla write la rassicuriamo tramite un cast di tipo, dicendole di trattarlo come fosse un array di caratteri. La seconda fase è recuperare la lunghezza (in byte) della nostra struttura. Per fare questo utilizziamo la funzione sizeof. Stiamo barando, ma solo un po’: dopo tutto il nostro è un flusso di byte, e un byte ha la stessa dimensione di un char!

Lettura binaria


Se la scrittura è stata piuttosto diretta, ci aspettiamo che anche la lettura lo sia. Ancora una volta vi invito a pensare a come risolvere la cosa con i file di testo...

L’oggetto che useremo sarà questa volta un ifstream (input file stream). Esiste in effetti anche fstream, di derivazione da ifstream e ofstream, e sarebbe possibile utilizzarlo direttamente, specificando di volta in volta se accedere in lettura o in scrittura, ma solitamente preferisco eliminare situazioni di ambiguità. Se devo leggere uso ifstream, se devo scrivere ofstream.

Ecco il codice per rileggere il nostro file player.dat e popolare con i suoi dati la nostra struttura:

PlayerData data;

ifstream file;
file.open(“player.dat”, ios_base::binary);

if(file.good())
{
   file.read((char*)&data, sizeof(PlayerData));
}
else
{
   cout << “Errore durante l’apertura in lettura del file player.dat!” << endl;
}

file.close();

cout << “Player energy: “ << data.energy << endl;
cout << “Player score: “ << data.score << endl;
cout << “Player x: “ << data.x << endl;
cout << “Player y: “ << data.y << endl;
cout << “Player z: “ << data.z << endl;


Ora che abbiamo già visto nel dettaglio la write, la lettura della read è piuttosto semplice. Quel che accade è una piccola magia: i byte vengono letti dal file e vanno a popolare correttamente i singoli campi della nostra struttura. Dopo aver chiuso lo stream, non ci rimane che verificare la lettura stampando a video i valori contenuti in data.

Buon coding!

Riferimenti:


Documentazione fstream

Forse ti interessa anche...


venerdì 17 giugno 2011

HowTo: Usare XML nel proprio programma con TinyXML

XML è bello e andrebbe usato ogni qual volta se ne ha l’occasione. Il problema spesso è avere a disposizione un parser che sia:

  • Piccolo 
  • Semplice
  • Facile da includere nel nostro progetto 

Nel mondo Open Source, putroppo, almeno una di queste tre caratteristiche è spesso assente. Vi segnalo però TinyXML, una libreria C++ veramente minuta che mantiene quel che promette.


Step 1) Scarichiamo TinyXML e includiamola nel nostro progetto C++


Il download è uno zip contenente due header e quattro file di codice. Non vi sono binari. Eh, ma allora è da ricompilare! Non proprio. Cioè, liberi di farlo, ma potete semplicemente includere i file nel vostro progetto: non richiederanno flag o altre impostazioni aggiuntive per la compilazione. Se proprio non vi va di avere qualche Kb di sorgente di terzi potete sempre creare una libreria statica (.a o .lib) e includere poi quella. Personalmente preferisco il primo metodo.


Step 2) Utilizziamo TinyXML


Tutto quel che occorre per accedere alle classi della libreria è contenuto in tinyxml.h, da cui:

#include “tinyxml.h”

La documentazione online offre numerosi esempi d’uso, ma si concentra più sulla creazione di nuovi documenti che sulla lettura di documenti esistenti. Scelta dell’autore, ma credo sia esigenza più frequente la seconda che non la prima. Inoltre, la ricchezza degli esempi può al tempo stesso risultare spaesante. In questo tutorial vediamo quindi come aprire un documento in lettura ed esplorarne la struttura. Il documento di esempio ha questa forma:

<items>
   <item id=”1”>
      <name>Item01</name>
   </item>
   <item id=”2”>
      <name>Item02</name>
   </item>
</items>

La nostra root è il nodo items, che contiene una serie di elementi item. Ogni elemento item ha un attributo id e una proprietà name.

NB: TinyXML, per mantenere la sua semplicità, deve compiere qualche rinuncia. Una di queste è il supporto ai DTD, quindi non abbiamo strumenti per verificare la validità formale del nostro XML.

Vediamo ora come fare per aprire il nostro file (items.xml) e andare a beccare l’item con id 2 per stamparne a video il nome.

TiXmlDocument doc("items.xml");
if(doc.LoadFile())
{
   TiXmlElement* root = doc.RootElement();
   TiXmlNode* item_node = root->IterateChildren(null);

   while( item_node )
   {
      TiXmlElement* item_element = item_node->ToElement();
      int item_id = 0;
      item_element->QueryIntAttribute("id", &item_id);

      if(item_id == 2)
      {
         // stampo a video il valore name di questo nodo
         TiXmlElement* name_property = item_element->FirstChild(“name”)->ToElement();
         std::cout << name_property->GetText() << endl;

         // ed esco dal while
         break;
      }

   item_node = root->IterateChildren( item_node );
   }
}

TiXmlDocument è la classe principale della libreria. Sintetizza il nostro documento xml fisico, il cui path è richiesto nel costruttore. Il metodo LoadFile è autoesplicativo e ha un booleano come valore di ritorno per verificare che l’operazione sia andata a buon fine.

Una volta che il nostro documento xml è caricato in memoria nell’oggetto doc, possiamo iniziare a giocare con i metodi della classe. In particolare dobbiamo accedere alla root (RootElement()), ottenere il primo dei nodi figli (IterateChildren(null)) e iniziare ad iterare tra tutti i figli di root finchè non troviamo quello che ci interessa. La IterateChildren accetta in input il puntatore al nodo corrente e restituisce il nodo successivo, o null in assenza di ulteriori nodi. Nella nostra prima chiamata le passiamo null, ad indicare che vogliamo accedere al primo elemento. Le chiamate successive (ultima istruzione del while) contengono invece il puntatore all’elemento corrente.

La prima istruzione del while è un cast da TiXmlNode* a TiXmlElement*, una specializzazione di TiXmlNode che ne estende le funzionalità. La documentazione dei due metodi è il posto migliore in cui scoprire le differenze e magari sbirciare l’albero di ereditarietà.

TiXmlElement ha diversi metodi per leggere un attributo, in particolare per interpretarne il valore secondo un tipo predefinito. Nel nostro caso gli id sono tutti interi, quindi usiamo senza timori QueryIntAttribute, che necessita di un puntatore ad intero per restituire il risultato della lettura.

Una volta raggiunto il nodo con l’id che ci interessa usiamo il metodo FirstChild(const char* value) per accedere al primo nodo figlio con il nome specificato: name, nel nostro caso. Otteniamo quindi il relativo puntatore e accediamo al testo tramite GetText(), stampando il valore di name a video con una buona vecchia cout.

Buon coding!


Riferimenti:


TinyXML home

Forse ti interessa anche...