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:

1class main_menu():
2    def __init__(self, screen1, altezza_s, larghezza_s, ingame): #menu_opzioni, caricamento_gioco, salvataggio_gioco,inizio_gioco,screen)
3        self.istanza = 0
4        self.screen = screen1
5        self.anim_push = 0
6        self.anim_push_bool = False
7        self.ing = ingame
8        self.bool_ing = False
9        self.count_init = False
10 
11        try:
12            stream = open("data/impostazioni.pkl", "r")
13            pk = cPickle.Unpickler(stream)
14            imp = pk.load()
15            self._Schermo_larghezza = imp.larghezza_schermo
16            self._Schermo_altezza = imp.altezza_schermo
17            self._full = imp.full
18            self._bool_buff = imp.bool_buff
19            self._bool_hw = imp.bool_hw
20            self._bool_opengl = imp.bool_opengl
21            stream.close()
22 
23        except IOError:
24            print ("Impossibile inizializzare Pygame")
25            exit()
26 
27        ######
28        # Menu
29        self.Sfondo = pygame.image.load("data/Sfondo.jpg").convert()
30        self.Sfondo = pygame.transform.scale(self.Sfondo, (self._Schermo_larghezza, self._Schermo_altezza))
31 
32        self.Continua = pygame.image.load("data/pulsanti/Continua.png").convert_alpha()
33        self.Inizio = pygame.image.load("data/pulsanti/Inizio.png").convert_alpha()
34        self.Carica = pygame.image.load("data/pulsanti/Carica.png").convert_alpha()
35        self.Salva = pygame.image.load("data/pulsanti/Salva.png").convert_alpha()
36        self.Opzioni = pygame.image.load("data/pulsanti/Opzioni.png").convert_alpha()
37        self.Esci = pygame.image.load("data/pulsanti/Esci.png").convert_alpha()
38        self.Continua_push = pygame.image.load("data/pulsanti/Continua_push.png").convert_alpha()
39        self.Inizio_push = pygame.image.load("data/pulsanti/Inizio_push.png").convert_alpha()
40        self.Carica_push = pygame.image.load("data/pulsanti/Carica_push.png").convert_alpha()
41        self.Salva_push = pygame.image.load("data/pulsanti/Salva_push.png").convert_alpha()
42        self.Opzioni_push = pygame.image.load("data/pulsanti/Opzioni_push.png").convert_alpha()
43        self.Esci_push = pygame.image.load("data/pulsanti/Esci_push.png").convert_alpha()
44 
45        self.x = larghezza_s/2
46        self.y = altezza_s/2
47 
48        self.Inizio_rect = pygame.Rect((self.x-48,self.y-24-48*2), (96,48))
49        self.Carica_rect = pygame.Rect((self.x-48,self.y-24-48), (96,48))
50        self.Salva_rect = pygame.Rect((self.x-48,self.y-24), (96,48))
51        self.Opzioni_rect = pygame.Rect((self.x-48,self.y-24+48), (96,48))
52        self.Esci_rect = pygame.Rect((self.x-48,self.y-24+48*2), (96,48))
53 
54        self.Puntatore = pygame.image.load("data/puntatore.png").convert_alpha()
55        self.anim_punt = False
56        self.audio_click = pygame.mixer.Sound("data/sound/beep-3.wav")
57        self.canale_mouse = pygame.mixer.Channel(1)
58 
59        self.Premuto = -1
60        self.Premuto_op = -1
61 
62        ##############
63        # Menu Opzioni
64        self._640 = pygame.image.load("data/pulsanti/640.png").convert_alpha()
65        self._800 = pygame.image.load("data/pulsanti/800.png").convert_alpha()
66        self._1024 = pygame.image.load("data/pulsanti/1024.png").convert_alpha()
67        self._1280 = pygame.image.load("data/pulsanti/1280.png").convert_alpha()
68        self._AccHw = pygame.image.load("data/pulsanti/AccHw.png").convert_alpha()
69        self._OpenGl = pygame.image.load("data/pulsanti/Opengl.png").convert_alpha()
70        self._DoppioBuff = pygame.image.load("data/pulsanti/DoppioBuff.png").convert_alpha()
71        self._Indietro = pygame.image.load("data/pulsanti/Indietro.png").convert_alpha()
72        self._Applica = pygame.image.load("data/pulsanti/Applica.png").convert_alpha()
73        self._FullScreen = pygame.image.load("data/pulsanti/FullScreen.png").convert_alpha()
74        self._640_push = pygame.image.load("data/pulsanti/640_push.png").convert_alpha()
75        self._800_push = pygame.image.load("data/pulsanti/800_push.png").convert_alpha()
76        self._1024_push = pygame.image.load("data/pulsanti/1024_push.png").convert_alpha()
77        self._1280_push = pygame.image.load("data/pulsanti/1280_push.png").convert_alpha()
78        self._AccHw_push = pygame.image.load("data/pulsanti/AccHw_push.png").convert_alpha()
79        self._OpenGl_push = pygame.image.load("data/pulsanti/Opengl_push.png").convert_alpha()
80        self._DoppioBuff_push = pygame.image.load("data/pulsanti/DoppioBuff_push.png").convert_alpha()
81        self._Indietro_push = pygame.image.load("data/pulsanti/Indietro_push.png").convert_alpha()
82        self._Applica_push = pygame.image.load("data/pulsanti/Applica_push.png").convert_alpha()
83        self._FullScreen_push = pygame.image.load("data/pulsanti/FullScreen_push.png").convert_alpha()
84        self._640_a = pygame.image.load("data/pulsanti/640_attivo.png").convert_alpha()
85        self._800_a = pygame.image.load("data/pulsanti/800_attivo.png").convert_alpha()
86        self._1024_a = pygame.image.load("data/pulsanti/1024_attivo.png").convert_alpha()
87        self._1280_a = pygame.image.load("data/pulsanti/1280_attivo.png").convert_alpha()
88        self._AccHw_a = pygame.image.load("data/pulsanti/AccHw_attiva.png").convert_alpha()
89        self._OpenGl_a = pygame.image.load("data/pulsanti/Opengl_attiva.png").convert_alpha()
90        self._DoppioBuff_a = pygame.image.load("data/pulsanti/DoppioBuff_attivo.png").convert_alpha()
91        self._FullScreen_a = pygame.image.load("data/pulsanti/FullScreen_a.png").convert_alpha()
92 
93        self._640_rect = pygame.Rect((self.x-150,self.y-200), (96,48))
94        self._800_rect = pygame.Rect((self.x+150-96,self.y-200), (96,48))
95        self._1024_rect = pygame.Rect((self.x-150,self.y-150), (96,48))
96        self._1280_rect = pygame.Rect((self.x+150-96,self.y-150), (96,48))
97        self._AccHw_rect = pygame.Rect((self.x-150,self.y-50), (96,48))
98        self._OpenGl_rect = pygame.Rect((self.x+150-96,self.y-50), (96,48))
99        self._DoppioBuff_rect = pygame.Rect((self.x-150,self.y+25), (96,48))
100        self._Indietro_rect = pygame.Rect((self.x-150,self.y+125), (96,48))
101        self._Applica_rect = pygame.Rect((self.x+150-96,self.y+125), (96,48))
102        self._FullScreen_rect = pygame.Rect((self.x+150-96,self.y+25), (96,48))
103 
104        self.rect_premere = [self.Inizio_rect, self.Carica_rect,self.Salva_rect, self.Opzioni_rect,self.Esci_rect]
105        self.rect_premere_opzioni = [self._640_rect,self._800_rect,
106self._1024_rect,self._1280_rect,
107self._AccHw_rect,self._OpenGl_rect,
108self._DoppioBuff_rect,self._Indietro_rect,
109self._Applica_rect,self._FullScreen_rect]
110 
111    ##########################################
112    # Funzione che gestisce le azioni nel menu
113    def action(self,coordinate_mouse):
114        if self.bool_ing == False:
115            if self.canale_mouse.get_busy() != True:
116                self.canale_mouse = self.audio_click.play()
117            if self.istanza == 0:
118                for x in range(0,len(self.rect_premere)):
119                    if self.rect_premere[x].collidepoint(coordinate_mouse):
120                        self.Premuto = x
121                        self.anim_push_bool = True
122            elif self.istanza == 1:
123                for x in range(0,len(self.rect_premere_opzioni)):
124                    if self.rect_premere_opzioni[x].collidepoint(coordinate_mouse):
125                        self.Premuto_op = x
126                        self.anim_push_bool = True
127 
128    ############################################
129    # Funzione per muovere il personaggio ingame
130    def action_p (self, stringa, num):
131        if self.bool_ing == True:
132            self.ing.giocatore.update(stringa,num)
133 
134    #########################################
135    # Funzione per chiamare il menu da ingame
136    def action_esc(self):
137        if self.bool_ing == True:
138            self.bool_ing = False
139            self.istanza = 0
140        else:
141            self.bool_ing = True
142            self.istanza = -1
143 
144    ####################################
145    # Funzione per il salvataggio ingame
146    def save_this(self):
147        try:
148            stream = open("data/save.pkl", "w")
149            pk = cPickle.Pickler(stream)
150            pk.dump(self.ing.giocatore.coordinate)
151            stream.close()
152            pk.clear_memo()
153        except IOError:
154            print ("Impossibile salvare il gioco")
155            exit()
156 
157    ####################################
158    # Funzione per il caricamento ingame
159    def carica_this(self):
160        try:
161            stream = open("data/save.pkl", "r")
162            pk = cPickle.Unpickler(stream)
163            coord = pk.load()
164            base = self.ing.base
165            stream.close()
166            personaggio = giocatore("27382_1174921384",48,32, coord, None)
167            Ingame = ingame(personaggio, self.screen, base)
168            self.ing = Ingame
169        except IOError:
170            print ("Nessun salvataggio presente")
171 
172    #################################
173    # Funzione che gestisce il render
174    def render(self,screen,temp,msxy):
175        if self.bool_ing == True:
176            self.ing.render()
177                        return
178 
179                screen.blit(self.Sfondo,(0,0))
180 
181        #############
182        # Render Menu
183        if self.istanza == 0:
184                if self.Premuto == 0:
185                    if self.count_init == False:
186                        screen.blit(self.Inizio_push,(self.x-48,self.y-24-48*2-15))
187                    else:
188                        screen.blit(self.Continua_push,(self.x-48,self.y-24-48*2-15))
189                else:
190                    if self.count_init == False:
191                        screen.blit(self.Inizio,(self.x-48,self.y-24-48*2-15))
192                    else :
193                        screen.blit(self.Continua,(self.x-48,self.y-24-48*2-15))
194                if self.Premuto == 1:
195                    screen.blit(self.Carica_push,(self.x-48,self.y-24-48-10))
196                else:
197                    screen.blit(self.Carica,(self.x-48,self.y-24-48-10))
198                if self.Premuto == 2:
199                    screen.blit(self.Salva_push,(self.x-48,self.y-24-5))
200                else:
201                    screen.blit(self.Salva,(self.x-48,self.y-24-5))
202                if self.Premuto == 3:
203                    screen.blit(self.Opzioni_push,(self.x-48,self.y-24+48+5))
204                else:
205                    screen.blit(self.Opzioni,(self.x-48,self.y-24+48+5))
206                if self.Premuto == 4:
207                    screen.blit(self.Esci_push,(self.x-48,self.y-24+48*2+10))
208                else :
209                    screen.blit(self.Esci,(self.x-48,self.y-24+48*2+10))
210 
211                if self.anim_push > 0.025 and self.Premuto == 4: # Esci
212                    exit()
213                elif self.anim_push > 0.030 and self.Premuto == 3: # Opzioni
214                    self.Premuto = -1
215                    self.anim_push = 0
216                    self.anim_push_bool = False
217                    self.istanza = 1
218                    try:
219                        stream = open("data/impostazioni.pkl", "r")
220                        pk = cPickle.Unpickler(stream)
221                        imp = pk.load()
222                        self._Schermo_larghezza = imp.larghezza_schermo
223                        self._Schermo_altezza = imp.altezza_schermo
224                        self._full = imp.full
225                        self._bool_buff = imp.bool_buff
226                        self._bool_hw = imp.bool_hw
227                        self._bool_opengl = imp.bool_opengl
228                        stream.close()
229                    except pygame.error,IOError:
230                        print ("Impossibile inizializzare Pygame")
231                        print (pygame.get_error())
232                        exit()
233                elif self.anim_push > 0.030 and self.Premuto == 2: # Salva
234                    self.Premuto = -1
235                    self.anim_push = 0
236                    self.anim_push_bool = False
237                    self.save_this()
238                elif self.anim_push > 0.030 and self.Premuto == 1: # Carica
239                    self.Premuto = -1
240                    self.anim_push = 0
241                    self.anim_push_bool = False
242                    self.carica_this()
243                elif self.anim_push > 0.030 and self.Premuto == 0: # Inizio
244                    self.Premuto = -1
245                    self.anim_push = 0
246                    self.anim_push_bool = False
247                    self.bool_ing = True
248                    self.istanza = -1
249                    self.count_init = True
250                else :
251                    if self.anim_push_bool == True:
252                        self.anim_push += temp
253 
254                if self.anim_punt == False:
255                    screen.blit(self.Puntatore,msxy)
256 
257        #####################
258        # Render Menu Opzioni
259        if self.istanza == 1:
260 
261            if self.Premuto_op == 0:
262                screen.blit(self._640_push,(self.x-150,self.y-200))
263            else:
264                if self._Schermo_larghezza == 640 and self._Schermo_altezza == 480:
265                    screen.blit(self._640_a,(self.x-150,self.y-200))
266                else:
267                    screen.blit(self._640,(self.x-150,self.y-200))
268 
269            if self.Premuto_op == 1:
270                    screen.blit(self._800_push,(self.x+150-96,self.y-200))
271            else:
272                if self._Schermo_larghezza == 800 and self._Schermo_altezza == 600:
273                    screen.blit(self._800_a,(self.x+150-96,self.y-200))
274                else:
275                    screen.blit(self._800,(self.x+150-96,self.y-200))
276 
277            if self.Premuto_op == 2:
278                    screen.blit(self._1024_push,(self.x-150,self.y-150))
279            else:
280                if self._Schermo_larghezza == 1024 and self._Schermo_altezza == 768:
281                    screen.blit(self._1024_a,(self.x-150,self.y-150))
282                else:
283                    screen.blit(self._1024,(self.x-150,self.y-150))
284 
285            if self.Premuto_op == 3:
286                    screen.blit(self._1280_push,(self.x+150-96,self.y-150))
287            else:
288                if self._Schermo_larghezza == 1280 and self._Schermo_altezza == 1024:
289                    screen.blit(self._1280_a,(self.x+150-96,self.y-150))
290                else:
291                    screen.blit(self._1280,(self.x+150-96,self.y-150))
292 
293            if self.Premuto_op == 4:
294                    screen.blit(self._AccHw_push,(self.x-150,self.y-50))
295            else:
296                if self._bool_hw == True:
297                    screen.blit(self._AccHw_a,(self.x-150,self.y-50))
298                else:
299                    screen.blit(self._AccHw,(self.x-150,self.y-50))
300 
301            if self.Premuto_op == 5:
302                    screen.blit(self._OpenGl_push,(self.x+150-96,self.y-50))
303            else:
304                if self._bool_opengl == True:
305                    screen.blit(self._OpenGl_a,(self.x+150-96,self.y-50))
306                else:
307                    screen.blit(self._OpenGl,(self.x+150-96,self.y-50))
308 
309            if self.Premuto_op == 6:
310                    screen.blit(self._DoppioBuff_push,(self.x-150,self.y+25))
311            else:
312                if self._bool_buff == True:
313                    screen.blit(self._DoppioBuff_a,(self.x-150,self.y+25))
314                else:
315                    screen.blit(self._DoppioBuff,(self.x-150,self.y+25))
316 
317            if self.Premuto_op == 7:
318                    screen.blit(self._Indietro_push,(self.x-150,self.y+125))
319            else:
320                screen.blit(self._Indietro,(self.x-150,self.y+125))
321 
322            if self.Premuto_op == 8:
323                    screen.blit(self._Applica_push,(self.x+150-96,self.y+125))
324            else:
325                screen.blit(self._Applica,(self.x+150-96,self.y+125))
326 
327            if self.Premuto_op == 9:
328                screen.blit(self._FullScreen_push,(self.x+150-96,self.y+25))
329            else:
330                if self._full == True:
331                        screen.blit(self._FullScreen_a,(self.x+150-96,self.y+25))
332                else:
333                    screen.blit(self._FullScreen,(self.x+150-96,self.y+25))
334 
335            #########################################################
336            # Animazioni menu opzioni e cambiamenti impostazioni
337 
338            if self.anim_push > 0.030 and self.Premuto_op == 9: # FullScreen
339                    self.anim_push = 0
340                    self.anim_push_bool = False
341                    self.Premuto_op = -1
342                    if self._full == False:
343                        self._full = True
344                    else:
345                        self._full = False
346            elif self.anim_push > 0.030 and self.Premuto_op == 8: # Applica
347                    aggiorna_imp( self._Schermo_larghezza, self._Schermo_altezza, self._full, self._bool_buff, self._bool_hw, self._bool_opengl)
348                    pygame.quit()
349                    run()
350            elif self.anim_push > 0.030 and self.Premuto_op == 7: # Indietro
351                    self.istanza = 0
352                    self.anim_push = 0
353                    self.anim_push_bool = False
354                    self.Premuto_op = -1
355            elif self.anim_push > 0.030 and self.Premuto_op == 6: # DoppioBuffer
356                    if (self._bool_hw == True or self._bool_opengl == True) and self._bool_buff == False:
357                        self._bool_buff = True
358                    else:
359                        self._bool_buff = False
360                    self.anim_push = 0
361                    self.anim_push_bool = False
362                    self.Premuto_op = -1
363            elif self.anim_push > 0.030 and self.Premuto_op == 5: # OpenGl
364                    if self._bool_opengl == False and self._bool_hw == False:
365                        self._bool_opengl = True
366                    else:
367                        self._bool_opengl = False
368                        self._bool_buff = False
369                    self.anim_push = 0
370                    self.anim_push_bool = False
371                    self.Premuto_op = -1
372            elif self.anim_push > 0.030 and self.Premuto_op == 4: # AccHw
373                    if self._bool_hw == False and self._bool_opengl == False:
374                        self._bool_hw = True
375                    else:
376                        self._bool_hw = False
377                        self._bool_buff = False
378                    self.anim_push = 0
379                    self.anim_push_bool = False
380                    self.Premuto_op = -1
381            elif self.anim_push > 0.030 and self.Premuto_op == 3: # 1280
382                    self._Schermo_larghezza = 1280
383                    self._Schermo_altezza = 1024
384                    self.anim_push = 0
385                    self.anim_push_bool = False
386                    self.Premuto_op = -1
387            elif self.anim_push > 0.030 and self.Premuto_op == 2: # 1024
388                    self._Schermo_larghezza = 1024
389                    self._Schermo_altezza = 768
390                    self.anim_push = 0
391                    self.anim_push_bool = False
392                    self.Premuto_op = -1
393            elif self.anim_push > 0.030 and self.Premuto_op == 1: # 800
394                    self._Schermo_larghezza = 800
395                    self._Schermo_altezza = 600
396                    self.anim_push = 0
397                    self.anim_push_bool = False
398                    self.Premuto_op = -1
399            elif self.anim_push > 0.030 and self.Premuto_op == 0: #640
400                    self._Schermo_larghezza = 640
401                    self._Schermo_altezza = 480
402                    self.anim_push = 0
403                    self.anim_push_bool = False
404                    self.Premuto_op = -1
405            else :
406                if self.anim_push_bool == True:
407                    self.anim_push += temp
408 
409            if self.anim_punt == False:
410                    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