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

Introduzione

In questa parte tratteremo gli argomenti fino alla classe impostazioni. Riporterò il codice completo, comprese le funzioni che abbiamo già analizzato nelle puntate precedenti (specificando eventuali modifiche).

Come al solito, non si capirà immediatamente lo scopo del programma, ma seguendolo passo passo, arriverete a comprendere tranquillamente questi concetti. Pensate solamente che queste basilari nozioni possono essere implementate secondo le vostre singole necessità, la base è sempre la stessa: creatività ed ingegno per risolvere un problema (però anche la soluzione non deve essere troppo ostica o difficile per poter essere applicata!!!).

Codice

1#!/usr/bin/python2
2# -*- coding: ascii
3 
4import pygame
5from pygame.locals import *
6from sys import exit
7import cPickle
8 
9def tps(orologio, fps):
10    temp = orologio.tick(fps)
11    tps = temp /1000.
12    return tps
13 
14def carica_imm_sprite(nome,h,w,num):
15    immagini = []
16    if num is None or num == 1:
17        imm1 =  pygame.image.load("data/"+nome+".png").convert_alpha()
18        imm1_w, imm1_h = imm1.get_size()
19 
20        for y in range(int(imm1_h/h)):
21            for x in range(int(imm1_w/w)):
22                immagini.append(imm1.subsurface((x*w,y*h,w,h)))
23 
24        return immagini
25    else:
26        for x in range(1,num):
27            imm1 = pygame.image.load("data/"+nome+str(x)+".png").convert_alpha()
28            immagini.append(imm1)
29        return immagini
30 
31class giocatore(pygame.sprite.Sprite):
32    def __init__(self,nome,altezza,larghezza,xy,num):
33        pygame.sprite.Sprite.__init__(self)
34        self.immagini = carica_imm_sprite(nome,altezza,larghezza,num)
35        self.immagine = self.immagini[0]
36 
37        self.coordinate = (int(xy[0]),int(xy[1]))
38 
39        self.animazione = False
40        self.anim_corrente = 0
41 
42        self.tempo_animazione = 0.0
43 
44        self.passo1 = pygame.mixer.Sound("data/sound/passo1.wav")
45        self.passo2 = pygame.mixer.Sound("data/sound/passo2.wav")
46        self.Passi = pygame.mixer.Channel(1)
47 
48    def anime(self,frame):
49        if self.animazione == False:
50            self.anim_corrente = 0
51            self.immagine = self.immagini[frame]
52            return
53        else :
54            if self.tempo_animazione<0.075:
55                self.tempo_animazione += orologio.get_time()/1000.
56            else:
57                self.immagine = self.immagini[self.anim_corrente+frame]
58                if self.Passi.get_sound() is None or self.Passi.get_sound() != self.passo1:
59                    if self.Passi.get_busy() == False:
60                        self.Passi = self.passo1.play()
61                    else:
62                        self.Passi.queue(self.passo1)
63                else :
64                    if self.Passi.get_busy() == True:
65                        self.Passi.queue(self.passo2)
66                if self.anim_corrente < 3:
67                    self.anim_corrente +=1
68                else:
69                    self.anim_corrente = 0
70                self.tempo_animazione = 0
71 
72    def update(self, direzione,frame):
73 
74        self.animazione = True
75        self.anime(frame)
76        x,y = self.coordinate
77        if direzione == "basso":
78            y += 1*speed*tempo_passato
79            self.coordinate = x,y
80            return
81        if direzione == "alto":
82            y -= 1*speed*tempo_passato
83            self.coordinate = x,y
84            return
85        if direzione == "destra":
86            x += 1*speed*tempo_passato
87            self.coordinate = x,y
88            return
89        if direzione == "sinistra":
90            x -= 1*speed*tempo_passato
91            self.coordinate = x,y
92            return
93 
94        elif direzione == "stop":
95            self.Passi.stop()
96            self.animazione = False
97 
98class ingame():
99    def __init__(self, player1, screen1, base):
100        self.giocatore = player1
101        self.screen = screen1
102        self.base = base
103 
104    def render(self):
105        self.screen.blit(self.base, (0,0))
106        self.screen.blit(self.giocatore.immagine, self.giocatore.coordinate)
107 
108class impostazioni():
109    def __init__(self):
110        self.larghezza_schermo = 640
111        self.altezza_schermo = 480
112        self.full = False
113        self.bool_hw = False
114        self.bool_buff = False
115        self.bool_opengl = False
116        self.depth = 32
117        self.frequenza = 44100
118        self.dimensione = -16
119        self.canali = 2
120        self.buffer = 4096

Analisi

Le funzioni tps e carica_imm_sprite sono rimaste immutate, compreso il loro funzionamento, quindi non spenderò ulteriori parole a riguardo.

La classe giocatore è simile alla classe sprite che abbiamo creato nell’esempio precedente. Unica differenza sostanziale è la mancata gestione di Rect, perché in questo caso vogliamo solo muovere il nostro giocatore sullo schermo e memorizzare la sua posizione con un salvataggio. La gestione dell’animazione è identica, come la sua renderizzazione.

Passiamo invece alla nuova classe ingame:

  • Questa classe ha il compito di mantere e gestire l’istanza di gioco. In questo caso viene inizializzata con il giocatore corrente, lo schermo dove si deve renderizzare e l’immagine dello sfondo.
  • Il render è molto semplice e consiste solo nel fatto di stampare a video l’immagine dello sfondo con il giocatore.

Questa classe è necessaria, in questo esempio, per passare dal menù al gioco e viceversa. Separando le due cose infatti, è più semplice gestire quello che sono le semplici meccaniche di gioco dalla gestione esterna. Nella nostra situazione però, come già accennato, sarà sempre il menù ad avere il “comando” della situazione.

Anche la classe impostazioni fa la sua prima comparsa in questo esempio:

  • Come possiamo vedere, questa classe ha bisogno solo di essere inizializzata con delle impostazioni di base. Per ora non abbiamo impostato nessun metodo che lavori su di essa, perché ho preferito agire direttamente sui suoi componenti per cambiare lo stato delle cose. Essendo gestita in questo modo, non ha bisogno di altro perché ci servirà solo per creare il nostro primo oggetto impostazioni, che sarà salvato con cPickle (vedremo in seguito come), per poi essere modificato dal menù a seconda delle nostre esigenze.

Le componenti di questa classe sono le seguenti:

  • Larghezza ed altezza dello schermo.
  • Quattro variabili booleane per memorizzare lo stato dei flag : full (Fullscreen o no), bool_hw (per l’accelerazione hardware), bool_buff (per l’utilizzo del Double Buffer), bool_opengl (per settare l’utilizzo dell’opengl).
  • Depth indica la profondità dello schermo.
  • Frequenza, dimensione, canali e buffer invece rappresentano le variabili di controllo per inizializzare il mixer. In questo esempio non eseguiremo cambiamenti a queste ultime, ma basterà aggiungere poche righe per adattarlo anche a questo compito, visto che il caricamento delle impostazione è sempre lo stesso e viene eseguito con tutti gli elementi di impostazioni. Dimensione ha un valore negativo per ovviare a problemi di compatibilità con sistemi operativi differenti da windows (infatti quest’ultimo non fa distinzioni e comunque sia, la qualità audio non cambia).

Per ora l’opengl è solo impostabile come flag, ma quasi sicuramente riscontrerete degli errori. Questo perché ancora non abbiamo visto come inizializzare una scena in OpenGL. Più avanti toccheremo anche questo tasto, un pò più pesante da digerire.

Conclusioni

Iniziamo così la nostra analisi riguardante questo nuovo esempio. Per ora c’è poco da dire e da capire, ma con la prossima parte le cose saranno più complicate, visto che parleremo della classe menù. Quello che continuerei a sottolineare è il fatto che non è facile trovare la soluzione alle proprie esigenze, ed è ancora più difficile trovare una soluzione adattabile a molte situazioni.

Essendo proprio quest’ultimo il mio obbiettivo, potete un pò capire il mio disagio nel presentarvi questi esempi, perché potrebbero essere utili solo ai miei scopi, ma inutili e difficilmente comprensibili e/o utilizzabili dalla maggior parte delle persone che leggono.

Per questo cerco sempre di specificare anche le differenti possibilità di utilizzo delle funzioni che presento, così da far capire le varie scelte che si possono presentare durante la stesura del codice o nella creazione del videogioco in se, mettendovi in condizione di gestire la situazione con le vostre forze.

Con questo vi saluto e vi do appuntamento alla prossima parte.

Press ESC to close