Sviluppare un gioco in Python : Menù e salvataggi. Terza Parte.

Introduzione

In questo articolo vedremo la parte più lunga e complessa di questa guida, ovvero la gestione del menù.

Il codice:

class main_menu():
	def __init__(self, screen1, altezza_s, larghezza_s, ingame): #menu_opzioni, caricamento_gioco, salvataggio_gioco,inizio_gioco,screen)
		self.istanza = 0
		self.screen = screen1
		self.anim_push = 0
		self.anim_push_bool = False
		self.ing = ingame
		self.bool_ing = False
		self.count_init = False

		try:
			stream = open("data/impostazioni.pkl", "r")
			pk = cPickle.Unpickler(stream)
			imp = pk.load()
			self._Schermo_larghezza = imp.larghezza_schermo
			self._Schermo_altezza = imp.altezza_schermo
			self._full = imp.full
			self._bool_buff = imp.bool_buff
			self._bool_hw = imp.bool_hw
			self._bool_opengl = imp.bool_opengl
			stream.close()

		except IOError:
			print ("Impossibile inizializzare Pygame")
			exit()

		######
		# Menu
		self.Sfondo = pygame.image.load("data/Sfondo.jpg").convert()
		self.Sfondo = pygame.transform.scale(self.Sfondo, (self._Schermo_larghezza, self._Schermo_altezza))

		self.Continua = pygame.image.load("data/pulsanti/Continua.png").convert_alpha()
		self.Inizio = pygame.image.load("data/pulsanti/Inizio.png").convert_alpha()
		self.Carica = pygame.image.load("data/pulsanti/Carica.png").convert_alpha()
		self.Salva = pygame.image.load("data/pulsanti/Salva.png").convert_alpha()
		self.Opzioni = pygame.image.load("data/pulsanti/Opzioni.png").convert_alpha()
		self.Esci = pygame.image.load("data/pulsanti/Esci.png").convert_alpha()
		self.Continua_push = pygame.image.load("data/pulsanti/Continua_push.png").convert_alpha()
		self.Inizio_push = pygame.image.load("data/pulsanti/Inizio_push.png").convert_alpha()
		self.Carica_push = pygame.image.load("data/pulsanti/Carica_push.png").convert_alpha()
		self.Salva_push = pygame.image.load("data/pulsanti/Salva_push.png").convert_alpha()
		self.Opzioni_push = pygame.image.load("data/pulsanti/Opzioni_push.png").convert_alpha()
		self.Esci_push = pygame.image.load("data/pulsanti/Esci_push.png").convert_alpha()

		self.x = larghezza_s/2
		self.y = altezza_s/2

		self.Inizio_rect = pygame.Rect((self.x-48,self.y-24-48*2), (96,48))
		self.Carica_rect = pygame.Rect((self.x-48,self.y-24-48), (96,48))
		self.Salva_rect = pygame.Rect((self.x-48,self.y-24), (96,48))
		self.Opzioni_rect = pygame.Rect((self.x-48,self.y-24+48), (96,48))
		self.Esci_rect = pygame.Rect((self.x-48,self.y-24+48*2), (96,48))

		self.Puntatore = pygame.image.load("data/puntatore.png").convert_alpha()
		self.anim_punt = False
		self.audio_click = pygame.mixer.Sound("data/sound/beep-3.wav")
		self.canale_mouse = pygame.mixer.Channel(1)

		self.Premuto = -1
		self.Premuto_op = -1

		##############
		# Menu Opzioni
		self._640 = pygame.image.load("data/pulsanti/640.png").convert_alpha()
		self._800 = pygame.image.load("data/pulsanti/800.png").convert_alpha()
		self._1024 = pygame.image.load("data/pulsanti/1024.png").convert_alpha()
		self._1280 = pygame.image.load("data/pulsanti/1280.png").convert_alpha()
		self._AccHw = pygame.image.load("data/pulsanti/AccHw.png").convert_alpha()
		self._OpenGl = pygame.image.load("data/pulsanti/Opengl.png").convert_alpha()
		self._DoppioBuff = pygame.image.load("data/pulsanti/DoppioBuff.png").convert_alpha()
		self._Indietro = pygame.image.load("data/pulsanti/Indietro.png").convert_alpha()
		self._Applica = pygame.image.load("data/pulsanti/Applica.png").convert_alpha()
		self._FullScreen = pygame.image.load("data/pulsanti/FullScreen.png").convert_alpha()
		self._640_push = pygame.image.load("data/pulsanti/640_push.png").convert_alpha()
		self._800_push = pygame.image.load("data/pulsanti/800_push.png").convert_alpha()
		self._1024_push = pygame.image.load("data/pulsanti/1024_push.png").convert_alpha()
		self._1280_push = pygame.image.load("data/pulsanti/1280_push.png").convert_alpha()
		self._AccHw_push = pygame.image.load("data/pulsanti/AccHw_push.png").convert_alpha()
		self._OpenGl_push = pygame.image.load("data/pulsanti/Opengl_push.png").convert_alpha()
		self._DoppioBuff_push = pygame.image.load("data/pulsanti/DoppioBuff_push.png").convert_alpha()
		self._Indietro_push = pygame.image.load("data/pulsanti/Indietro_push.png").convert_alpha()
		self._Applica_push = pygame.image.load("data/pulsanti/Applica_push.png").convert_alpha()
		self._FullScreen_push = pygame.image.load("data/pulsanti/FullScreen_push.png").convert_alpha()
		self._640_a = pygame.image.load("data/pulsanti/640_attivo.png").convert_alpha()
		self._800_a = pygame.image.load("data/pulsanti/800_attivo.png").convert_alpha()
		self._1024_a = pygame.image.load("data/pulsanti/1024_attivo.png").convert_alpha()
		self._1280_a = pygame.image.load("data/pulsanti/1280_attivo.png").convert_alpha()
		self._AccHw_a = pygame.image.load("data/pulsanti/AccHw_attiva.png").convert_alpha()
		self._OpenGl_a = pygame.image.load("data/pulsanti/Opengl_attiva.png").convert_alpha()
		self._DoppioBuff_a = pygame.image.load("data/pulsanti/DoppioBuff_attivo.png").convert_alpha()
		self._FullScreen_a = pygame.image.load("data/pulsanti/FullScreen_a.png").convert_alpha()

		self._640_rect = pygame.Rect((self.x-150,self.y-200), (96,48))
		self._800_rect = pygame.Rect((self.x+150-96,self.y-200), (96,48))
		self._1024_rect = pygame.Rect((self.x-150,self.y-150), (96,48))
		self._1280_rect = pygame.Rect((self.x+150-96,self.y-150), (96,48))
		self._AccHw_rect = pygame.Rect((self.x-150,self.y-50), (96,48))
		self._OpenGl_rect = pygame.Rect((self.x+150-96,self.y-50), (96,48))
		self._DoppioBuff_rect = pygame.Rect((self.x-150,self.y+25), (96,48))
		self._Indietro_rect = pygame.Rect((self.x-150,self.y+125), (96,48))
		self._Applica_rect = pygame.Rect((self.x+150-96,self.y+125), (96,48))
		self._FullScreen_rect = pygame.Rect((self.x+150-96,self.y+25), (96,48))

		self.rect_premere = [self.Inizio_rect, self.Carica_rect,self.Salva_rect, self.Opzioni_rect,self.Esci_rect]
		self.rect_premere_opzioni = [self._640_rect,self._800_rect,
self._1024_rect,self._1280_rect,
self._AccHw_rect,self._OpenGl_rect,
self._DoppioBuff_rect,self._Indietro_rect,
self._Applica_rect,self._FullScreen_rect]

	##########################################
	# Funzione che gestisce le azioni nel menu
	def action(self,coordinate_mouse):
		if self.bool_ing == False:
			if self.canale_mouse.get_busy() != True:
				self.canale_mouse = self.audio_click.play()
			if self.istanza == 0:
				for x in range(0,len(self.rect_premere)):
					if self.rect_premere[x].collidepoint(coordinate_mouse):
						self.Premuto = x
						self.anim_push_bool = True
			elif self.istanza == 1:
				for x in range(0,len(self.rect_premere_opzioni)):
					if self.rect_premere_opzioni[x].collidepoint(coordinate_mouse):
						self.Premuto_op = x
						self.anim_push_bool = True

	############################################
	# Funzione per muovere il personaggio ingame
	def action_p (self, stringa, num):
		if self.bool_ing == True:
			self.ing.giocatore.update(stringa,num)

	#########################################
	# Funzione per chiamare il menu da ingame
	def action_esc(self):
		if self.bool_ing == True:
			self.bool_ing = False
			self.istanza = 0
		else:
			self.bool_ing = True
			self.istanza = -1

	####################################
	# Funzione per il salvataggio ingame
	def save_this(self):
		try:
			stream = open("data/save.pkl", "w")
			pk = cPickle.Pickler(stream)
			pk.dump(self.ing.giocatore.coordinate)
			stream.close()
			pk.clear_memo()
		except IOError:
			print ("Impossibile salvare il gioco")
			exit()

	####################################
	# Funzione per il caricamento ingame
	def carica_this(self):
		try:
			stream = open("data/save.pkl", "r")
			pk = cPickle.Unpickler(stream)
			coord = pk.load()
			base = self.ing.base
			stream.close()
			personaggio = giocatore("27382_1174921384",48,32, coord, None)
			Ingame = ingame(personaggio, self.screen, base)
			self.ing = Ingame
		except IOError:
			print ("Nessun salvataggio presente")

	#################################
	# Funzione che gestisce il render
	def render(self,screen,temp,msxy):
		if self.bool_ing == True:
			self.ing.render()
                        return

                screen.blit(self.Sfondo,(0,0))

		#############
		# Render Menu
		if self.istanza == 0:
				if self.Premuto == 0:
					if self.count_init == False:
						screen.blit(self.Inizio_push,(self.x-48,self.y-24-48*2-15))
					else:
						screen.blit(self.Continua_push,(self.x-48,self.y-24-48*2-15))
				else:
					if self.count_init == False:
						screen.blit(self.Inizio,(self.x-48,self.y-24-48*2-15))
					else :
						screen.blit(self.Continua,(self.x-48,self.y-24-48*2-15))
				if self.Premuto == 1:
					screen.blit(self.Carica_push,(self.x-48,self.y-24-48-10))
				else:
					screen.blit(self.Carica,(self.x-48,self.y-24-48-10))
				if self.Premuto == 2:
					screen.blit(self.Salva_push,(self.x-48,self.y-24-5))
				else:
					screen.blit(self.Salva,(self.x-48,self.y-24-5))
				if self.Premuto == 3:
					screen.blit(self.Opzioni_push,(self.x-48,self.y-24+48+5))
				else:
					screen.blit(self.Opzioni,(self.x-48,self.y-24+48+5))
				if self.Premuto == 4:
					screen.blit(self.Esci_push,(self.x-48,self.y-24+48*2+10))
				else :
					screen.blit(self.Esci,(self.x-48,self.y-24+48*2+10))

				if self.anim_push > 0.025 and self.Premuto == 4: # Esci
					exit()
				elif self.anim_push > 0.030 and self.Premuto == 3: # Opzioni
					self.Premuto = -1
					self.anim_push = 0
					self.anim_push_bool = False
					self.istanza = 1
					try:
						stream = open("data/impostazioni.pkl", "r")
						pk = cPickle.Unpickler(stream)
						imp = pk.load()
						self._Schermo_larghezza = imp.larghezza_schermo
						self._Schermo_altezza = imp.altezza_schermo
						self._full = imp.full
						self._bool_buff = imp.bool_buff
						self._bool_hw = imp.bool_hw
						self._bool_opengl = imp.bool_opengl
						stream.close()
					except pygame.error,IOError:
						print ("Impossibile inizializzare Pygame")
						print (pygame.get_error())
						exit()
				elif self.anim_push > 0.030 and self.Premuto == 2: # Salva
					self.Premuto = -1
					self.anim_push = 0
					self.anim_push_bool = False
					self.save_this()
				elif self.anim_push > 0.030 and self.Premuto == 1: # Carica
					self.Premuto = -1
					self.anim_push = 0
					self.anim_push_bool = False
					self.carica_this()
				elif self.anim_push > 0.030 and self.Premuto == 0: # Inizio
					self.Premuto = -1
					self.anim_push = 0
					self.anim_push_bool = False
					self.bool_ing = True
					self.istanza = -1
					self.count_init = True
				else :
					if self.anim_push_bool == True:
						self.anim_push += temp

				if self.anim_punt == False:
					screen.blit(self.Puntatore,msxy)

		#####################
		# Render Menu Opzioni
		if self.istanza == 1:

			if self.Premuto_op == 0:
				screen.blit(self._640_push,(self.x-150,self.y-200))
			else:
				if self._Schermo_larghezza == 640 and self._Schermo_altezza == 480:
					screen.blit(self._640_a,(self.x-150,self.y-200))
				else:
					screen.blit(self._640,(self.x-150,self.y-200))

			if self.Premuto_op == 1:
					screen.blit(self._800_push,(self.x+150-96,self.y-200))
			else:
				if self._Schermo_larghezza == 800 and self._Schermo_altezza == 600:
					screen.blit(self._800_a,(self.x+150-96,self.y-200))
				else:
					screen.blit(self._800,(self.x+150-96,self.y-200))

			if self.Premuto_op == 2:
					screen.blit(self._1024_push,(self.x-150,self.y-150))
			else:
				if self._Schermo_larghezza == 1024 and self._Schermo_altezza == 768:
					screen.blit(self._1024_a,(self.x-150,self.y-150))
				else:
					screen.blit(self._1024,(self.x-150,self.y-150))

			if self.Premuto_op == 3:
					screen.blit(self._1280_push,(self.x+150-96,self.y-150))
			else:
				if self._Schermo_larghezza == 1280 and self._Schermo_altezza == 1024:
					screen.blit(self._1280_a,(self.x+150-96,self.y-150))
				else:
					screen.blit(self._1280,(self.x+150-96,self.y-150))

			if self.Premuto_op == 4:
					screen.blit(self._AccHw_push,(self.x-150,self.y-50))
			else:
				if self._bool_hw == True:
					screen.blit(self._AccHw_a,(self.x-150,self.y-50))
				else:
					screen.blit(self._AccHw,(self.x-150,self.y-50))

			if self.Premuto_op == 5:
					screen.blit(self._OpenGl_push,(self.x+150-96,self.y-50))
			else:
				if self._bool_opengl == True:
					screen.blit(self._OpenGl_a,(self.x+150-96,self.y-50))
				else:
					screen.blit(self._OpenGl,(self.x+150-96,self.y-50))

			if self.Premuto_op == 6:
					screen.blit(self._DoppioBuff_push,(self.x-150,self.y+25))
			else:
				if self._bool_buff == True:
					screen.blit(self._DoppioBuff_a,(self.x-150,self.y+25))
				else:
					screen.blit(self._DoppioBuff,(self.x-150,self.y+25))

			if self.Premuto_op == 7:
					screen.blit(self._Indietro_push,(self.x-150,self.y+125))
			else:
				screen.blit(self._Indietro,(self.x-150,self.y+125))

			if self.Premuto_op == 8:
					screen.blit(self._Applica_push,(self.x+150-96,self.y+125))
			else:
				screen.blit(self._Applica,(self.x+150-96,self.y+125))

			if self.Premuto_op == 9:
				screen.blit(self._FullScreen_push,(self.x+150-96,self.y+25))
			else:
				if self._full == True:
						screen.blit(self._FullScreen_a,(self.x+150-96,self.y+25))
				else:
					screen.blit(self._FullScreen,(self.x+150-96,self.y+25))

			#########################################################
			# Animazioni menu opzioni e cambiamenti impostazioni

			if self.anim_push > 0.030 and self.Premuto_op == 9: # FullScreen
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
					if self._full == False:
						self._full = True
					else:
						self._full = False
			elif self.anim_push > 0.030 and self.Premuto_op == 8: # Applica
					aggiorna_imp( self._Schermo_larghezza, self._Schermo_altezza, self._full, self._bool_buff, self._bool_hw, self._bool_opengl)
					pygame.quit()
					run()
			elif self.anim_push > 0.030 and self.Premuto_op == 7: # Indietro
					self.istanza = 0
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			elif self.anim_push > 0.030 and self.Premuto_op == 6: # DoppioBuffer
					if (self._bool_hw == True or self._bool_opengl == True) and self._bool_buff == False:
						self._bool_buff = True
					else:
						self._bool_buff = False
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			elif self.anim_push > 0.030 and self.Premuto_op == 5: # OpenGl
					if self._bool_opengl == False and self._bool_hw == False:
						self._bool_opengl = True
					else:
						self._bool_opengl = False
						self._bool_buff = False
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			elif self.anim_push > 0.030 and self.Premuto_op == 4: # AccHw
					if self._bool_hw == False and self._bool_opengl == False:
						self._bool_hw = True
					else:
						self._bool_hw = False
						self._bool_buff = False
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			elif self.anim_push > 0.030 and self.Premuto_op == 3: # 1280
					self._Schermo_larghezza = 1280
					self._Schermo_altezza = 1024
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			elif self.anim_push > 0.030 and self.Premuto_op == 2: # 1024
					self._Schermo_larghezza = 1024
					self._Schermo_altezza = 768
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			elif self.anim_push > 0.030 and self.Premuto_op == 1: # 800
					self._Schermo_larghezza = 800
					self._Schermo_altezza = 600
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			elif self.anim_push > 0.030 and self.Premuto_op == 0: #640
					self._Schermo_larghezza = 640
					self._Schermo_altezza = 480
					self.anim_push = 0
					self.anim_push_bool = False
					self.Premuto_op = -1
			else :
				if self.anim_push_bool == True:
					self.anim_push += temp

			if self.anim_punt == False:
					screen.blit(self.Puntatore,msxy)

Analisi:

Il codice ha qualche commento per non perdersi, prendeteli quindi come punti di riferimento. Per prima cosa analizziamo il costruttore; l’idea è quella di far gestire tutto al menù, come abbiamo già accennato in precedenza, per questo il costruttore necessita di quattro parametri:

  1. screen1 : lo schermo dove andremo a renderizzare.
  2. altezza_s  : altezza dello schermo.
  3. larghezza_s : larghezza dello schermo.
  4. ingame : che potremo chiamare istanza di gioco.

Per questo esempio suddividerò l’analisi in righe, per rendere più facile la lettura.

  • dalla 3 alla 9:

Istanza ci serve per tenere conto se siamo nel menù, oppure nel menù opzioni. Memorizziamo poi screen ed ingame. Le variabili anim_push ed anim_push_bool servono per gestire le animazioni dei pulsanti, invece bool_ing e count_init servono per tenere conto se siamo in gioco e se è la prima volta che inziamo a giocare.

  • dalla 11 alla 25

Apriamo il file impostazioni utilizzando cPickle e teniamo conto delle attuali impostazioni per essere modificate memorizzandole in variabili omonime: _Schermo_larghezza, _Schermo_altezza, _full, _bool_buff, _bool_hw e _bool_opengl. Come potete notare gestiamo l’apertuare del file con le eccezioni, per ovviare a problemi di apertura o chiusura file. Infatti aprire un file analizzando poi lo stream può portare a degli errori se per esempio questo non esiste. Il modulo cPickle ci aiuta caricando le impostazioni salvata nell’oggetto imp, che altro non è che la classe impostazioni vista precedentemente. Vedremo in seguito come memorizzare i vari campi delle impostazioni.

Tenete conto che in questo caso, se avevate un file di configurazione scritto da voi, in queste righe dovevate fare il parsing del file per raccogliere le informazioni, mentre qui le carichiamo tutte con le prime 3 istruzioni e poi le associamo ad elementi della classe per tenere conto del loro cambiamento in gioco. Infatti cambiando le impostazioni dal menù opzioni, si dovrà cliccare su applica per rendere effettive le modifiche ed aggiornare il gioco (che nel nostro caso si riavvierà).

Vorrei ricorda inoltre che i file creati da cPickle non sono compatibili se sono stati creati su sistemi operativi differenti, quindi dovete cancellare il file con le attuali impostazioni se volete provare lo stesso esempio su un OS diverso, altrimenti non caricherà nulla e vi rilascerà un errore (gestito opportunatamente nell’eccezione).

  • dalla 28 alla 59

Carichiamo sfondo di gioco e tutti i pulsanti del menù principale (comprese le loro animazioni, ovvero i tasti premuti). Dimezziamo le coordinate dello schermo, perché il nostro menù sarà sempre centrato, così da non portare complicazioni con risoluzioni differenti (ed ecco spiegato perché il gioco si riavvia una volta cambiate le impostazioni). Memorizziamo poi le zone dove dobbiamo gestire le collisioni, per creare le animazioni dei pulsanti e cliccare quindi su di essi. Carichiamo il puntatore del mouse e l’audio del click, prenotando un canale per lui. Teniamo conto del tasto premuto attualmente, in questo caso si parte da -1 perché il primo tasto, “Inizio”, sarà lo 0, mentre “Esci”, che è l’ultimo, sarà il 4.

  • dalla 60 alla 110

Anche per il menù opzioni memorizziamo il tasto attualmente premuto e poi carichiamo tutti i pulsanti con le eventuali animazioni (per esempio il pulsante della risoluzione attuale sarà illuminato di un altro colore). Anche per il menù opzioni creiamo i vari rect per gestire le collisioni (con i click). Infine, creiamo due liste, rect_premere e rect_premere_opzioni dove memorizziamo tutti i rect relativi rispettivamente al menù ed al menù opzioni, perché dovremo controllare ad ogni click, in base all’istanza attuale, quale pulsante stiamo premendo.

  • dalla 114 alla 127

Il metodo action gestisce le eventuali chiamate a seconda del pulsante premuto. Infatti ha bisogno delle coordinate del mouse per controllare su quale tasto si trova sopra il puntatore. A quel punto, restituisce il numero del tasto premuto, cambiando quindi il valore all’interno della classe, che reagirà in base a questo.

  • dalla 131 alla 133

Questo metodo ha solo il compito di passare gli eventi al gioco vero e proprio se non stiamo nel menù. I movimenti sono poi controllati dalla classe giocatore, che è interna ad ingame e per questo possiamo accedere tranquillamente da qui, senza passare l’istanza di gioco perché già memorizzata nella classe menù.

  • dalla 137 alla 143

Questo metodo fa passare al menù quando siamo in gioco e viceversa, semplicemente cambiano le variabili all’interno della classe.

  • dalla 147 alla 156

Il salvataggio durante il gioco, per questo esempio, consiste solo nella memorizzazione delle coordinate attuali del giocatore, così che, caricandole, il giocatore torni nella posizione in cui era stato salvato. Per fare questo apriamo uno stream per il nostro file salvataggio, che sarà sempre sovrascritto e vi memorizziamo le coordinate prendendole dalla classe giocatore stesso (sempre all’interno di ingame). pk.clear_memo() è necessario se si salva più volte con lo stesso file, perché altrimenti il nostro salvataggio sarà aggiornato solo la prima volta, poiché cPickle si ricorderà di aver già scritto quel file e non lo modificherà più se non utilizziamo questo metodo (tecnicamente non servirebbe, perché cPickle non viene mai richiamato nello stesso ciclo di gioco, ma ho ritenuto opportuno fare chiarezza).

Un metodo analogo è presente per il salvataggio delle impostazioni, ma questo lo vedremo più avanti.

  • dalla 160 alla 171

Il caricamento del nostro salvataggio è molto simile al caricamento delle impostazioni, l’unica differenza è che dobbiamo solo caricare il personaggio con le coordinate aggiornate.

  • dalla 175 alla 255

Alla funzione reder passiamo lo schermo (screen), il tempo passato (temp) e le coordinate del mouse (msxy). Per prima cosa controlliamo se siamo in gioco oppure no, per chiamare così il rendering del gioco. Altrimenti renderizziamo lo sfondo e poi passiamo ai pulsanti. Ogni pulsante, in base allo stato in corso (premuto o no) renderizzeranno il tasto caricato in precedenza. Dalla linea 211 si gestisce l’animazione e le eventuali azioni da compiere, come per esempio il cambio di schermata se si clicca su opzioni, con il caricamento delle impostazioni attuali (nell’eventualità che, anche se le opzioni sono state modificate, rimangano le stesse se non si clicca su applica). E’ proprio questa parte che si occupa del tempo di animazione del pulsante e di rimettere “in sesto” le condizioni del menù, che sarà in attesa della pressione di un altro tasto. In poche parole, se il tempo di animazione non è concluso, il tasto rimane pigiato ed incrementiamo il tempo trascorso. Una volta concluso togliamo il tutto ed eseguiamo l’azione, un pò come si faceva per le animazioni delle sprites.

Per ultimo, renderizziamo il puntatore sullo schermo cone le coordinate msxy che avevamo passato prima.

  • dalla 159 alla 410

La gestione del menù opzioni è la stessa, con la differenza che ora i pulsanti devono cambiare la configurazione delle impostazioni (caricata dal file all’interno della classe menù) e solo il tasto applica scrive il nuovo file una volta finita lascelta (riscrivendo la nuova classe impostazione sul file). Per questo carichiamo ogni volta il file impostazioni quando si accede al menù opzioni, perché se non si cambia nulla, le variabili che abbiamo comunque cambiato all’interno della classe non sono la configurazione attuale e non si capirebbe più quali scelte dei settaggi ha fatto il giocatore (infatti se per esempio scegliamo la risoluzione 800×600 questa si colorerà di azzuro, ma se non applichiamo nulla, quando rientriamo in opzioni questa non deve essere azzura, perché magari la risoluzione attuale è un’altra).

Come ho già accennato, il tasto opengl è solo messo per parcondicio, ma sicuramente la sua attivazione porterà a degli errori. Inoltre ho scelto delle risoluzioni standard, una cosa un pò più fine sarebbe riconoscere le varie risoluzioni possibili dello schermo per poi presentarle, ma come potete immaginare, poi ci sarebbe il problema del numero di pulsati da caricare e delle proporzioni degli oggetti sullo schermo. In questo esempio invece non ci sono problemi per come abbiamo impostato la cosa all’inizio, visto che i pulsanti, per esempio, hanno sempre la stessa grandezza e sono sempre visualizzati al centro dello schermo.

Conclusioni

Anche se il codice può sembrare poco chiaro al primo impatto, vedrete che, capito il meccanismo, sarà semplice implementare un vostro menù a seconda delle vostre esigenze. Naturalmente questa è solo una delle possibili soluzioni e con le ultime parti che saranno presentate nel prossimo articolo avrete un esempio concreto dove sperimentare le vostre idee.

Spero che per ora le cose siano abbastanza chiare e che vi torni molto utile questa parte, essenziale per la realizzazione di un videogioco.

Press ESC to close