Chiudiamo il discorso dei data manager analizzando un nuovo modulo che ci aiuterà nel gestire al meglio il nostro videogioco. Con questi ultimi due articoli abbiamo l’obbiettivo di deviare il carico di lavoro dal main principale per spostarlo in sottosistemi autonomi. Naturalmente la cosa sarebbe più semplice se si fosse progettato l’esempio sin dall’inizio in questo modo; questo non vuol dire che non si può rimediare in un secondo momento, ma solamente che è più difficile riorganizzare il tutto.
Alla fine di questa guida il nostro main sarà molto più leggibile, anche se molte cose che volevo aggiungere (come per esempio un sottoprogramma che gestisce i salvataggi ed i caricamenti in base al file oggetto passato) non sono presenti per problemi di tempo. Andando avanti rilascerò i vari miglioramenti, compreso il codice con le modifiche che abbiamo apportato fin qui.
Non andando oltre, vi presento quindi il bibliotecario, che raccoglie per noi varie funzionalità:
Codice
import cPickle,sys,os,glob,pygame from pygame.locals import * class impostazioni(): def __init__(self): """inizializzazione di default del file impostazioni""" self.larghezza_schermo = 640 self.altezza_schermo = 480 self.full = False self.bool_hw = False self.bool_buff = False self.bool_opengl = False self.depth = 32 self.frequenza = 44100 self.dimensione = -16 self.canali = 2 self.buffer = 4096 def __str__(): return "impostazioni.pkl" class ilBibliotecario(): @classmethod def init_screen(cls): """inizializzazione dello schermo""" try: imp = cls.carica_imp("impostazioni.pkl") pygame.mixer.pre_init(imp.frequenza, imp.dimensione, imp.canali, imp.buffer) pygame.init() if imp.full == False and imp.bool_buff == False and imp.bool_hw == False and imp.bool_opengl == False: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo)) elif imp.full == False and imp.bool_buff == False and imp.bool_hw == False and imp.bool_opengl == True: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),OPENGL,imp.depth) elif imp.full == False and imp.bool_buff == False and imp.bool_hw == True and imp.bool_opengl == False: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),HWSURFACE,imp.depth) elif imp.full == True and imp.bool_buff == False and imp.bool_hw == True and imp.bool_opengl == False: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN | HWSURFACE,imp.depth) elif imp.full == False and imp.bool_buff == True and imp.bool_hw == True and imp.bool_opengl == False: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),DOUBLEBUF | HWSURFACE,imp.depth) elif imp.full == True and imp.bool_buff == True and imp.bool_hw == True and imp.bool_opengl == False: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN | DOUBLEBUF | HWSURFACE,imp.depth) elif imp.full == True and imp.bool_buff == False and imp.bool_hw == False and imp.bool_opengl == False: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN,imp.depth) elif imp.full == True and imp.bool_buff == True and imp.bool_hw == False and imp.bool_opengl == True: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN | DOUBLEBUF | OPENGL,imp.depth) elif imp.full == False and imp.bool_buff == True and imp.bool_hw == False and imp.bool_opengl == True: screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),DOUBLEBUF | OPENGL,imp.depth) return screen except IOError,pygame.error: print ("Impossibile inizializzare display") exit() @staticmethod def tps(orologio, fps): """funzione per il tempo di gioco""" temp = orologio.tick(fps) tps = temp /1000. return tps @staticmethod def carica_imp(stringa): """funzione per il caricamento del file impostazioni""" try: stream = open("data/"+stringa, "r") pk = cPickle.Unpickler(stream) imp = pk.load() stream.close() return imp except IOError: print ("Impossibile caricare il file") exit() @staticmethod def salva_imp(stringa): """funzione per il salvataggio del file impostazioni""" try: stream = open("data/"+stringa, "w") pk = cPickle.Pickler(stream) imp = impostazioni() pk.dump(imp) stream.close() pk.clear_memo() except IOError: print ("Impossibile salvare il file") exit() @staticmethod def aggiorna_imp(w,h,full,buff,hw,opengl): """funzione per l'aggiornamento del file impostazioni""" try: stream = open("data/impostazioni.pkl", "w") pk = cPickle.Pickler(stream) imp = impostazioni() imp.larghezza_schermo = w imp.altezza_schermo = h imp.full = full imp.bool_hw = hw imp.bool_buff = buff imp.bool_opengl = opengl pk.dump(imp) stream.close() pk.clear_memo() except IOError: print ("Impossibile aggiornare il file") exit()
Analisi
Credo che in questo pezzo di codice non ci sia nulla di nuovo a parte i due decoratori utilizzati @classmethod e @staticmethod. Il primo serve per passare la classe stessa alla funzione definita, dando quindi la possibilità di accedere alle sue funzioni membro. Sarebbe infatti impossibile utilizzare carica_imp all’interno di init_screen se non utilizziamo questo decoratore. Staticmethod invece definisce una funzione che non ha bisogno del primo argomento implicito.
Per il resto, il codice si spiega da sé, quindi non mi dilungherò in un’approfondita analisi.
Conclusioni
Come ho già specificato nell’introduzione, si può adattare questo modulo per salvare e caricare oggetti a piacimento. Infatti non ho inserito i salvataggi durante la sessione di gioco, ma con questo esempio dovrebbe essere facile capirne l’implementazione.
La cosa importante da capire è che, creando dei moduli esterni da collegare al nostro file principale, ci permette di rendere più leggera la revisione e l’ampliamento del nostro codice. Questi due piccoli gestori sono due ottimi punti di partenza per provare a fare qualcosa di più concreto.
Si può utillizzare la stessa tecnica per gestire il menù di gioco e la sessione di gioco. Mi dispiace di non aver affrontato (per il momento) questo punto, ma rimedierò presto con nuovi esempi, tempo permettendo.
Di seguito lascerò i sorgenti che utilizzano sia il collezionista che il bibliotecario. Mi raccomando soltanto di non fare troppo caso ai nomi (visto che potete benissimo cambiarli) e alle possibili ottimizzazioni, ma bensì cercate di testare voi stessi queste “tecniche” per avere un risultato migliore.
Sorgenti
- Esempio con collezionista e bibliotecario: http://dl.dropbox.com/u/16546001/AD/Loading%20cb.rar