Sviluppare un gioco in Python: Data Manager 2

Introduzione

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

1import cPickle,sys,os,glob,pygame
2from pygame.locals import *
3 
4class impostazioni():
5    def __init__(self):
6        """inizializzazione di default del file impostazioni"""
7        self.larghezza_schermo = 640
8        self.altezza_schermo = 480
9        self.full = False
10        self.bool_hw = False
11        self.bool_buff = False
12        self.bool_opengl = False
13        self.depth = 32
14        self.frequenza = 44100
15        self.dimensione = -16
16        self.canali = 2
17        self.buffer = 4096
18 
19    def __str__():
20        return "impostazioni.pkl"
21 
22class ilBibliotecario():
23    @classmethod
24    def init_screen(cls):
25        """inizializzazione dello schermo"""
26        try:
27            imp = cls.carica_imp("impostazioni.pkl")
28            pygame.mixer.pre_init(imp.frequenza, imp.dimensione, imp.canali, imp.buffer)
29            pygame.init()
30            if imp.full == False and imp.bool_buff == False and imp.bool_hw == False and imp.bool_opengl == False:
31                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo))
32            elif imp.full == False and imp.bool_buff == False and imp.bool_hw == False and imp.bool_opengl == True:
33                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),OPENGL,imp.depth)
34            elif imp.full == False and imp.bool_buff == False and imp.bool_hw == True and imp.bool_opengl == False:
35                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),HWSURFACE,imp.depth)
36            elif imp.full == True and imp.bool_buff == False and imp.bool_hw == True and imp.bool_opengl == False:
37                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN | HWSURFACE,imp.depth)
38            elif imp.full == False and imp.bool_buff == True and imp.bool_hw == True and imp.bool_opengl == False:
39                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),DOUBLEBUF | HWSURFACE,imp.depth)
40            elif imp.full == True and imp.bool_buff == True and imp.bool_hw == True and imp.bool_opengl == False:
41                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN | DOUBLEBUF | HWSURFACE,imp.depth)
42            elif imp.full == True and imp.bool_buff == False and imp.bool_hw == False and imp.bool_opengl == False:
43                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN,imp.depth)
44            elif imp.full == True and imp.bool_buff == True and imp.bool_hw == False and imp.bool_opengl == True:
45                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),FULLSCREEN | DOUBLEBUF | OPENGL,imp.depth)
46            elif imp.full == False and imp.bool_buff == True and imp.bool_hw == False and imp.bool_opengl == True:
47                screen = pygame.display.set_mode((imp.larghezza_schermo, imp.altezza_schermo),DOUBLEBUF | OPENGL,imp.depth)
48 
49            return screen
50 
51        except IOError,pygame.error:
52            print ("Impossibile inizializzare display")
53            exit()
54 
55    @staticmethod
56    def tps(orologio, fps):
57        """funzione per il tempo di gioco"""
58        temp = orologio.tick(fps)
59        tps = temp /1000.
60        return tps
61 
62    @staticmethod
63    def carica_imp(stringa):
64        """funzione per il caricamento del file impostazioni"""
65        try:
66            stream = open("data/"+stringa, "r")
67            pk = cPickle.Unpickler(stream)
68            imp = pk.load()
69            stream.close()
70            return imp
71 
72        except IOError:
73            print ("Impossibile caricare il file")
74            exit()
75 
76    @staticmethod
77    def salva_imp(stringa):
78        """funzione per il salvataggio del file impostazioni"""
79        try:
80            stream = open("data/"+stringa, "w")
81            pk = cPickle.Pickler(stream)
82            imp = impostazioni()
83            pk.dump(imp)
84            stream.close()
85            pk.clear_memo()
86 
87        except IOError:
88            print ("Impossibile salvare il file")
89            exit()
90 
91    @staticmethod
92    def aggiorna_imp(w,h,full,buff,hw,opengl):
93        """funzione per l'aggiornamento del file impostazioni"""
94        try:
95            stream = open("data/impostazioni.pkl", "w")
96            pk = cPickle.Pickler(stream)
97            imp = impostazioni()
98            imp.larghezza_schermo = w
99            imp.altezza_schermo = h
100            imp.full = full
101            imp.bool_hw = hw
102            imp.bool_buff = buff
103            imp.bool_opengl = opengl
104            pk.dump(imp)
105            stream.close()
106            pk.clear_memo()
107        except IOError:
108            print ("Impossibile aggiornare il file")
109            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

Press ESC to close