Creare Videogiochi - Game Developer

Versione completa: Tutorial 25 - Xml Handling
Al momento stai visualizzando i contenuti in una versione ridotta. Visualizza la versione completa e formattata.
Tutorial 25: Xml Handling

[Immagine: 025shot.jpg]

Dimostriamo il caricamento e il salvataggio delle configurazioni tramite file XML
Autore: Y.M. Bosman <yoran.bosman@gmail.com>
Questo demo ci mostra un sistema completo per la gestione delle configurazioni. Il sorgente è facilmente integrabile nelle vostre applicazioni.
Codice PHP:
#include <irrlicht.h>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;

#ifdef _IRR_WINDOWS_
#pragma comment(lib, "Irrlicht.lib")
#endif 
Classe SettingManager.
Questa classe carica e salva le impostazioni dell'engine e gestisce le sue opzioni.
La classe fa uso di irrMap, si tratta di un array assoiciativo che usa un albero di tipo red-black per creare una mappa di chiavi, durante il percorso vedremo come usarlo.
Codice PHP:
class SettingManager
{
public:

    
// Costruttore che imposta i settaggi di default
    
SettingManager(const stringwsettings_file): SettingsFile(settings_file), NullDevice(0)
    {
        
// Il device a null per Irrlicht, caricheremo i settaggi prima della creazione del device quindi ecco nulldevice
        
NullDevice irr::createDevice(irr::video::EDT_NULL);

        
//DriverOptions trattandosi di una mappa irrlicht possiamo inserire i 
        //valori in due modi chiammando insert(key,value) o usando [key] operator
        //il [] operator sovrascrive i valori se preesistenti
        
DriverOptions.insert(L"Software"EDT_SOFTWARE);
        
DriverOptions.insert(L"OpenGL"EDT_OPENGL);
        
DriverOptions.insert(L"Direct3D9"EDT_DIRECT3D9);

        
//opzioni dulla risoluzione
        
ResolutionOptions.insert(L"640x480"dimension2du(640,480));
        
ResolutionOptions.insert(L"800x600"dimension2du(800,600));
        
ResolutionOptions.insert(L"1024x768"dimension2du(1024,768));

        
//i nostri defaults preferiti
        
SettingMap.insert(L"driver"L"Direct3D9");
        
SettingMap.insert(L"resolution"L"640x480");
        
SettingMap.insert(L"fullscreen"L"0"); //0 significa false
    
}

    
// Distruttore, potremmo salvare i settings automaticamente nel exit 
    // del programma nel nostro caso qua droppiamo semplicemente il nulldevice
    
~SettingManager()
    {
        if (
NullDevice)
        {
            
NullDevice->closeDevice();
            
NullDevice->drop();
        }
    }; 
Carichiamo il file xml dal disco, riscriviamo le impostazioni di default il file xml che andiamo a carica ha la seguente struttura, i settaggi sono annidati nel nodo principale
<?xml version="1.0"?>
<mygame>
<video>
<setting name="driver" value="Direct3D9">
<setting name="fullscreen" value="0">
<setting name="resolution" value="1024x768">
</video>
</mygame>
Codice PHP:
bool load()
    {
        
//se il device fallisce non tentiamo di caricare niente
        
if (!NullDevice)
            return 
false;

        
irr::io::IXMLReaderxml NullDevice->getFileSystem()->createXMLReader(SettingsFile);  //creiamo il lettore xml
        
if (!xml)
            return 
false;

        const 
stringw settingTag(L"setting"); //cerchiamo questo tag nel xml
        
stringw currentSection//tracciamo la sezione corrente
        
const stringw videoTag(L"video"); //costante per il tag videotag

        //finché c'è qualcosa da leggere
        
while (xml->read())
        {
            
//controllo il tipo di nodo
            
switch (xml->getNodeType())
            {
                
//trovo un nuovo elemento
                
case irr::io::EXN_ELEMENT:
                {
//Sono nella zona vuota o mygame, cerco il tag video e lo rendo la sezione attiva
                    
if (currentSection.empty() && videoTag.equals_ignore_case(xml->getNodeName()))
                    {
                        
currentSection videoTag;
                    }
                    
//sono nella sezione video, trovo un setting da parserizzare
                    
else if (currentSection.equals_ignore_case(videoTag) && settingTag.equals_ignore_case(xml->getNodeName() ))
                    {
                        
//leggo la chiave
                        
stringw key xml->getAttributeValueSafe(L"name");
                        
//se c'è una chiava da impostare
                        
if (!key.empty())
                        {
                        
//imposto il setting nella mappa con il suo valore,
                        //l'operatore [] sovrascrive i valori preesistenti o
                        //li inserisce come nuova chiave
                            
SettingMap[key] = xml->getAttributeValueSafe(L"value");
                        }
                    }

                    
//..
                    // Qui potete inserire la gestione dei vostri tags e sezioni
                    //..
                
}
                break;

                    
//Finito l'elemento
                
case irr::io::EXN_ELEMENT_END:
                    
//Alla fine della sezione video resettiamo il tag
                    
currentSection=L"";
                break;
            }
        }

        
// droppiamo il lettore xml alla fine di tutto
        
xml->drop();

        return 
true;
    }

    
// Scriviamo sul file xml usando il nulldevice.
    
bool save()
    {

        
//se il device era fallito non salviamo nulla
        
if (!NullDevice)
            return 
false;

        
//creo il componente per scrivere l'xml
        
irr::io::IXMLWriterxwriter NullDevice->getFileSystem()->createXMLWriterSettingsFile );
        if (!
xwriter)
            return 
false;

        
//scrivo la parte obbligatori dell'header necessario per ogni xml
        
xwriter->writeXMLHeader();

        
//inizio con l'elemento mygame, potete cambiare "mygame" come volete
        
xwriter->writeElement(L"mygame");
        
xwriter->writeLineBreak();                  //new line

        //inizio la sezione con i settaggi video
        
xwriter->writeElement(L"video");
        
xwriter->writeLineBreak();                  //new line

        // getIterator ci da il puntatore al primo nodo della mappa
        // ad ogni iterazione incremento l'iterator che ci da il nodo successivo
        // finché non arrivo alla fine e scrivo i valori tramite le chiavi dei nodi
        
map<stringwstringw>::Iterator i SettingMap.getIterator();
        for(; !
i.atEnd(); i++)
        {
            
//scrivo gli elementi così <setting name="key" value="x" />
            //il secondo parametro da un elemento vuoto senza figli, un attributo
            
xwriter->writeElement(L"setting",trueL"name"i->getKey().c_str(), L"value",i->getValue().c_str() );
            
xwriter->writeLineBreak();
        }
        
xwriter->writeLineBreak();

        
//chiudo la sezione video
        
xwriter->writeClosingTag(L"video");
        
xwriter->writeLineBreak();

        
//..
        // Qua possiamo inseirre una sezione sound, ecc..
        //..

        //chiudo la sezione mygame
        
xwriter->writeClosingTag(L"mygame");

        
//drop dello scrittore xml
        
xwriter->drop();

        return 
true;
    }

    
// Impostiamo i parametri del nostro manager
    
void setSetting(const stringwname, const stringwvalue)
    {
        
SettingMap[name]=value;
    }

    
// scrittura di valori interi sulla mappa
    
void setSetting(const stringwnames32 value)
    {
        
SettingMap[name]=stringw(value);
    }

    
// Ricevo i valori come stringa
    
stringw getSetting(const stringwkey) const
    {
        
//la funzione find restituisce il puntatore al nodo della mappa se trovo
        //la chiave, altrimenti restituisce null, il nodo della mappa ha una
        //funzione getValue e getKey, con la chiave restituiamo node->getValue()
        
map<stringwstringw>::NodeSettingMap.find(key);
        if (
n)
            return 
n->getValue();
        else
            return 
L"";
    }

    
//
    
bool getSettingAsBoolean(const stringwkey ) const
    {
        
stringw s getSetting(key);
        if (
s.empty())
            return 
false;
        return 
s.equals_ignore_case(L"1");
    }

    
//
    
s32 getSettingAsInteger(const stringwkey) const
    {
        
//cast implicito di stringa invece di stringw perché strtol10 non accetta stringe troppo larghe
        
const stringc s getSetting(key);
        if (
s.empty())
            return 
0;

        return 
strtol10(s.c_str());
    }

public:
    
map<stringws32DriverOptions//opzione per il config del driver
    
map<stringwdimension2duResolutionOptions//opzione per la risoluzione 
private:
    
SettingManager(const SettingManagerother); // solo definitoo
    
SettingManageroperator=(const SettingManagerother); // solo definito

    
map<stringwstringwSettingMap//config corrente

    
stringw SettingsFile// posizione del file, tipicamente il
    
irr::IrrlichtDeviceNullDevice;
}; 
Contesto per l'applciativo e variabili globali
Codice PHP:
struct SAppContext
{
    
SAppContext()
        : 
Device(0),Gui(0), Driver(0), Settings(0), ShouldQuit(false),
        
ButtonSave(0), ButtonExit(0), ListboxDriver(0),
        
ListboxResolution(0), CheckboxFullscreen(0)
    {
    }

    ~
SAppContext()
    {
        if (
Settings)
            
delete Settings;

        if (
Device)
        {
            
Device->closeDevice();
            
Device->drop();
        }
    }

    
IrrlichtDeviceDevice;
    
IGUIEnvironmentGui;
    
IVideoDriverDriver;
    
SettingManagerSettings;
    
bool ShouldQuit;

    
//settings dialog
    
IGUIButtonButtonSave;
    
IGUIButtonButtonExit;
    
IGUIListBoxListboxDriver;
    
IGUIListBoxListboxResolution;
    
IGUICheckBoxCheckboxFullscreen;
}; 
Tipico event receiver.
Codice PHP:
class MyEventReceiver : public IEventReceiver
{
public:
    
MyEventReceiver(SAppContext a) : App(a) { }

    
virtual bool OnEvent(const SEventevent)
    {
        if (
event.EventType == EET_GUI_EVENT )
        {
            switch ( 
event.GUIEvent.EventType )
            {
                
//gestione pressione bottoni
                
case EGET_BUTTON_CLICKED:
                {
                    
//Salviamo tutto se premuto il tasto salva
                    
if ( event.GUIEvent.Caller == App.ButtonSave )
                    {
                        
//se c'è una selezione la scriviamo
                        
if ( App.ListboxDriver->getSelected() != -1)
                            
App.Settings->setSetting(L"driver"App.ListboxDriver->getListItem(App.ListboxDriver->getSelected()));

                        
//se c'è una selezione la scriviamo
                        
if ( App.ListboxResolution->getSelected() != -1)
                            
App.Settings->setSetting(L"resolution"App.ListboxResolution->getListItem(App.ListboxResolution->getSelected()));

                        
App.Settings->setSetting(L"fullscreen"App.CheckboxFullscreen->isChecked());


                        if (
App.Settings->save())
                        {
                            
App.Gui->addMessageBox(L"settings save",L"settings saved, please restart for settings to change effect","",true);
                        }
                    }
                    
// click sul pulsante cancel/exit, esco dall'applicazione
                    
else if ( event.GUIEvent.Caller == App.ButtonExit)
                    {
                        
App.ShouldQuit true;
                    }
                }
                break;
            }
        }

        return 
false;
    }

private:
    
SAppContext App;
}; 
Funzione che crea la finestra di dialogo con cui mostriamo le impostazioni correnti, le modifichiamo e le salviamo su file xml
Codice PHP:
void createSettingsDialog(SAppContextapp)
{
    
// togliamo l'alpha dalla gui
    
for (irr::s32 i=0i<irr::gui::EGDC_COUNT ; ++i)
    {
        
irr::video::SColor col app.Gui->getSkin()->getColor((irr::gui::EGUI_DEFAULT_COLOR)i);
        
col.setAlpha(255);
        
app.Gui->getSkin()->setColor((irr::gui::EGUI_DEFAULT_COLOR)icol);
    }

    
//creo la finestra per le impostazioni video
    
gui::IGUIWindowwindowSettings app.Gui->addWindow(rect<s32>(10,10,400,400),true,L"Videosettings");
    
app.Gui->addStaticText (L"Select your desired video settings"rects32 >(10,2020040), falsetruewindowSettings);

    
// aggiungo la listbox per la scelta dei driver
    
app.Gui->addStaticText (L"Driver"rects32 >(10,5020060), falsetruewindowSettings);
    
app.ListboxDriver app.Gui->addListBox(rect<s32>(10,60,220,120), windowSettings1,true);

    
//aggiungo le opzioni disponibili per la scelta del driver selezionato
    
map<stringws32>::Iterator i app.Settings->DriverOptions.getIterator();
    for(; !
i.atEnd(); i++)
        
app.ListboxDriver->addItem(i->getKey().c_str());

    
//imposto il driver selezionato
    
app.ListboxDriver->setSelected(app.Settings->getSetting("driver").c_str());

    
// aggiungo la listbox per la scelta della risoluzione
    
app.Gui->addStaticText (L"Resolution"rects32 >(10,130200140), falsetruewindowSettings);
    
app.ListboxResolution app.Gui->addListBox(rect<s32>(10,140,220,200), windowSettings1,true);

    
//aggiungo le opzioni per la risoluzione scelta
    
map<stringwdimension2du>::Iterator ri app.Settings->ResolutionOptions.getIterator();
    for(; !
ri.atEnd(); ri++)
        
app.ListboxResolution->addItem(ri->getKey().c_str());

    
//imposto la risoluzione selezionata
    
app.ListboxResolution->setSelected(app.Settings->getSetting("resolution").c_str());

    
//aggiungo la checkbox per il fullscreen
    
app.CheckboxFullscreen app.Gui->addCheckBox(
            
app.Settings->getSettingAsBoolean("fullscreen"),
            
rect<s32>(10,220,220,240), windowSettings, -1,
            
L"Fullscreen");

    
//alla fine il pulsante salva
    
app.ButtonSave app.Gui->addButton(
            
rect<s32>(80,250,150,270), windowSettings2,
            
L"Save video settings");

    
//pulsante exit/cancel
    
app.ButtonExit app.Gui->addButton(
            
rect<s32>(160,250,240,270), windowSettings2,
            
L"Cancel and exit");

La funzione main. Crea tutti gli oggetti e il gestore del xml.
Codice PHP:
int main()
{
    
//creo un nuovo contesto per l'applicazione
    
SAppContext app;

    
//creo i parametri per la creazione del device che potranno essere sovrascritte 
    
SIrrlichtCreationParameters param;
    
param.DriverType EDT_SOFTWARE;
    
param.WindowSize.set(640,480);

    
// Provo a caricare il config. Lascio come esercizio il salvataggio del file 
    // nella cartella locale dell'applicativo, il posto più logico dove salvarlo
    // per il gioco. Per gli altri sistemi operativi vi rimando al manuale
    
app.Settings = new SettingManager("../../media/settings.xml");
    if ( !
app.Settings->load() )
    {
        
// ...
        // Qui possiamo aggiungere le nostre eccezioni, per il momento continuiamo perché abbiamo i default nel costruttore del SettingManager
        // ...
    
}
    else
    {
        
//carico dal disco i settaggi dal file xml

        //mappo i setting sul driver e testo che siano validi il DriverOptions
        //contiene la rappresentazione in stringa mappata sul E_DRIVER_TYPE enum
        //e.g "direct3d9" diventa un 4, guardate la classe DriverOptions per 
        //maggiori dettagli
        
map<stringws32>::Nodedriver app.Settings->DriverOptions.findapp.Settings->getSetting("driver") );

        if (
driver)
        {
            if ( 
irr::IrrlichtDevice::isDriverSupportedstatic_cast<E_DRIVER_TYPE>( driver->getValue() )))
            {
                
// il driver selezionato è supportato, quindi lo uso.
                
param.DriverType static_cast<E_DRIVER_TYPE>( driver->getValue());
            }
        }

        
//impostazioni della risoluzione, come fatto sopra per il video
        
map<stringwdimension2du>::Noderes app.Settings->ResolutionOptions.findapp.Settings->getSetting("resolution") );
        if (
res)
        {
            
param.WindowSize res->getValue();
        }
        
//vado in fullscreen
        
param.Fullscreen app.Settings->getSettingAsBoolean("fullscreen");
    }

    
//creo il device irrlicht dai settaggi
    
app.Device createDeviceEx(param);
    if (
app.Device == 0)
    {
        
// Aggiungete la gestione delle eccezioni nel caso di fallimento del driver
        
exit(0);
    }

    
app.Device->setWindowCaption(L"Xmlhandling - Irrlicht engine tutorial");
    
app.Driver  app.Device->getVideoDriver();
    
app.Gui     app.Device->getGUIEnvironment();

    
createSettingsDialog(app);

    
//imposto l'event receiver in modo da rispondere agli eventi della gui
    
MyEventReceiver receiver(app);
    
app.Device->setEventReceiver(&receiver);

    
//entriamo nel main loop
    
while (!app.ShouldQuit && app.Device->run())
    {
        if (
app.Device->isWindowActive())
        {
            
app.Driver->beginScene(truetrueSColor(0,200,200,200));
            
app.Gui->drawAll();
            
app.Driver->endScene();
        }
        
app.Device->sleep(10);
    }
    
//la nostra app distruggerà il device nel suo distruttore
    
return 0;


Versione pdf scaricabile da QUI