Sviluppare un gioco in Python: Data Manager.

Introduzione

Per il momento presenterò una serie di minicapitoli per migliorare l’ultimo esempio che abbiamo visto. C’è infatti la necessita di gestire le cose con più chiarezza, per avere un ambiente un pò diverso mentre si lavora.

In questa guida vedremo come implementare un semplice data manager, che io ho chiamato “il collezionista”. Il suo compito è quello di recuperare e caricare tutti i file di gioco di cui abbiamo bisogno, come immagini, suoni, sprites ecc… Per il momento è abbastanza completo ed ho testato la sua compatibilità anche su diversi sistemi operativi (Win e Linux, dovrebbe essere compatibile anche per OSx). In più, una volta capito il meccanismo, è facile modificare le cose a proprio piacimento.

Codice

import os,pygame,glob,sys
#from pygame.locals import *

class ilCollezionista():

    def __init__ (self, store, cartelle):
		"""Gestisce il caricamento dei dati dalle cartelle di gioco"""
		self.radice = os.getcwd()

		try:
			self.cartelle = ["\\"+x for x in cartelle]
			self.store = ["\\"+x for x in store]

			self.images = {}
			self.sounds = {}

			for x in self.store:
				temp_x = self.radice+x
				formati = ["jpg","JPG","png","PNG"]
				for y in self.cartelle:
					temp_y = temp_x+y
					os.chdir(temp_y)
					temp_g = glob.glob('*.*')
					for t in temp_g:
						if t[-3:] in formati:
							self.images[t[:-4]] = pygame.image.load(t).convert_alpha()
						else:
							self.sounds[t[:-4]] = pygame.mixer.Sound(t)
		except OSError:
			print ("Errore nel percorso delle cartelle")
			sys.exit()
		else:
			os.chdir(self.radice)

    def cerca (self,stringa):
		"""Cerca l'elemento passato e restituisce il suo valore"""
		if self.images.has_key(stringa):
			return self.images[stringa]
		elif self.sounds.has_key(stringa):
			return self.sounds[stringa]
		else:
			return "File non presente"

    def cerca_sprites(self,nome,h,w,num):
		"""Cerca l'immagine sprite indicata e carica i suoi frame"""
		if num is None or num == 1:
			imm1 = self.cerca(nome)
			imm1_w, imm1_h = imm1.get_size()
			immagini = [imm1.subsurface((x*w,y*h,w,h)) for y in range(int(imm1_h/h)) for x in range(int(imm1_w/w))]
			return immagini

		else:
			immagini = [self.cerca(nome+str(x)) for x in range(1,num)]
			return immagini

Analisi

Come possiamo notare abbiamo bisogno dei moduli os, pygame, glob e sys. A seconda di come volete utilizzare questo data manager (molto semplice), potete decommentare la seconda riga, in quanto per il momento non è necessaria; mentre per alcune operazioni, come la creazione dello schermo, è quasi indispensabile (se non si vuole menzionare direttamente pygame). Questo modulo si occupa solo dello storage dei file che vogliamo caricare, quindi non è di utilità, per ora.

Questa classe, ilCollezionista, prevede di essere inizializzata con due parametri:

  • store : indica la cartella/e (in pratica è una stringa che viene passata) dove possiamo trovare tutti i file, come per esempio “data”.
  • cartelle : indica appunto le cartelle dove i file risiedono e devono essere cercati (in questo caso sarà una lista di stringhe).

Badate bene che store non deve essere necessariamente una cartella soltanto, ma anche più di una. Se volete questa opzione però, dovete gestire gli eventuali errori previsti per le sottocartelle che non sono presenti in entrambe le cartelle radice. Per ora la cosa è gestita dal try più esterno, che in caso di errore ci segnala che non possiamo raggiungere determinati percorsi. Quindi se avete una cartella “immagini” sia in “data” che in “documenti”, il caricamento sarà corretto, ma se la sottocartella “immagini” è presente solo in “data” e non nello store “documenti”, lancerà un eccezione perché il percorso “documenti/immagini” non è presente (questo errore è inerente a come sono stati strutturati i vari cicli).

Ora però facciamo un passo indietro: alla riga 8 memorizziamo la cartella dove stiamo lavorando. Di norma è la cartella dove è presente il file che state eseguendo, da qui procederemo al caricamento dei vari file. Questo passo è necessario per ristabilire le condizioni iniziali una volta finite le nostre operazioni.

Successivamente inseriamo solo la tabulazione che ci consente di scorrere nelle cartelle. Windows non fa distinzioni  tra ‘\\’ e ‘/’, quindi basta aggiungere quest’ultime ed il gioco è fatto (Grazie a Cesare per il suggerimento).

Di seguito creiamo due dizionari, per ora, ma se ne possono gestire quanti si vuole. Per l’esempio seguente, basta memorizzare le immagini (images) e i suoni (sounds). Le sprites saranno trattate come immagini.

I vari for non fanno altro che scorre dalla cartella radice in store e infine nelle cartelle da noi indicate. Qui intercettano tutti i file con le estensioni indicate in “formati” per collezionare le immagini, mentre tutto quello che trovano oltre quelle estensioni saranno suoni e quindi caricati in differente modo. Per entrambi però adottiamo lo stesso metodo di memorizzazione:

  • Nome_File_senza_estensioni -> Oggetto_pygame_caricato

Il nome del file senza estensione sarà quindi la nostra key (stringa) nel dizionario, mentre il suo valore sarà proprio l’oggetto pygame caricato. Una volta finito ripristiniamo la “current work directory”, per non far saltare meccanismi di caricamento impostazioni e salvataggi, che vedremo nei prossimi articoli con qualche miglioramento.

Conclusa questa operazione, in images avremo tutte le immagini che ci occorrono, mentre in sounds tutti i suoni. Ora non ci resta che definire i due metodi con i quali utilizzeremo queste risorse caricate, ma non acora accessibili.

  • cerca : cerca la stringa passata (che sarà il nome del file senza estensioni) nei due dizionari. Quindi non si fanno distinzioni tra immagini e suoni, ma semplicemente sarà restituito il valore necessario (l’oggetto pygame caricato), se presente.
  • cerca_sprites : questo metodo è più specifico. Se aguzzate un pò gli occhi, potete riconoscere in esso la funzione del caricamento immagini che avevamo utilizzato negli esempi precedenti per caricare le sprites. L’unica differenza del suo funzionamento ora, è che fa riferimento al dizionario di immagini che abbiamo creato in precedenza durante l’inizializzazione della classe collezionista.

Conclusioni

Il collezionista torna molto utile, le sue funzioni sono basilari e di molta importanza. Non resta che importarlo nel main per utilizzarlo, ma non affronterò questo passo prima di avervi presentato un altro modulo, che sto ancora finendo di costruire.

Sicuramente non vi risparmierà la scrittura di molte righe di codice, ma migliorerà di molto la leggibilità ed il mantenimento a lungo termine. Infatti se vi serve di modificare il metodo di caricamento delle immagini, dovrete solo cambiare il collezionista e non andare a spulciare tutto il main, una cosa non da poco.

Nella speranza di essere stato ancora di utilità, vi do appuntamento al prossimo articolo.

PS: per eventuali chiarimenti o delucidazioni, scrivete pure nei commenti!

Press ESC to close