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

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

Press ESC to close