Sviluppare un gioco in Python : il suono dei videogiochi

Introduzione


In questo articolo tratteremo il modulo mixer della libreria pygame. Le basi che abbiamo dato per iniziare a guardare con interesse la libreria non ci permettono ancora di raggiungere un livello soddisfacente.

Infatti non basta saper gestire immagini e input per realizzare un piccolo videogioco casalingo, perché per poterlo chiamare davvero “VIDEOGIOCO”, manca una cosa fondamentale: l’audio.

Il codice utilizzato in questo capitolo è lo stesso script utilizzato per l’esempio degli input, con la differenza che c’è una canzone come sottofondo, la quale può essere attivata o disattivata tramite il tasto “M” e si può regolare l’audio con i tasti “+” e “-” del tastierino numerico della vostra tastiera. Inoltre il mouse emetterò un suono diverso quando si clicca con il tasto destro e sinistro ed infine verrà emesso un applauso quando si clicca su start per l’inizio del gioco.

La musica di sottofondo è stata presa dal seguente sito http://www.opsound.org/music/, il suo nome è Relocation. Il sito si occupa di canzoni opensource, ovvero liberamente utilizzabili, così come gli effetti, che sono stati presi da http://www.pacdv.com/sounds/.
Potete scegliere voi stessi quali effetti o canzoni utilizzare, basta che non infrangano il copyright, ecco perché ho scelto canzoni liberamente fruibili, ma se siete musicisti, compositori
o avete una piccola vena artistica, basta un po’ di ingegno e un programma per ritoccare l’audio (come per esempio Audacity).

Il codice
Il codice verrà riportato per intero e poi commentato solo nelle differenze, cioè la parte dell’audio.
Si deve premettere che il mixer esegue file Ogg o Wav (non compresso) e che può essere gestito tramite i canali che la scheda audio mette a disposizione. Si possono controllare
volume e riproduzione e non è vietata la creazione di più canali, se necessario. Il mixer mette a disposizione anche un “canale” per la musica; ovvero potete gestire la musica di sottofondo del vostro gioco senza fare riferimento ad eventi Sound, che richiederebbero un grande spreco di memoria. Con il modulo Music invece, la vostra musica sarà passata in streaming, così da non doverla caricare completamente; in più Music supporta anche i file Mp3.

1imm_sfondo = "introduzione.jpg"
2mouse = "puntatore.png"
3 
4import pygame
5from pygame.locals import *
6from sys import exit
7 
8pygame.mixer.pre_init(44100, 16, 2, 4096)
9pygame.init()
10 
11screen = pygame.display.set_mode((640,480), DOUBLEBUF | HWSURFACE, 32)
12pygame.display.set_caption("Input Vari")
13 
14sfondo = pygame.image.load(imm_sfondo).convert()
15puntatore = pygame.image.load(mouse).convert_alpha()
16 
17speed = 0.5
18orologio = pygame.time.Clock()
19 
20tasto_start = pygame.Rect((200,100),(200,200))
21 
22controller = None
23 
24if pygame.joystick.get_count() > 0:
25    controller = pygame.joystick.Joystick(0)
26    controller.init()
27 
28if controller is None:
29    print ("Siamo spiacenti, ma hai bisogno di un joystick con almeno 10 tasti!")
30 
31x , y = 0,0
32move_x, move_y = 0,0
33 
34applausi = pygame.mixer.Sound("applause-2.wav")
35click = pygame.mixer.Sound("beep-3.wav")
36click2 = pygame.mixer.Sound("beep-6.wav")
37pygame.mixer.music.load("Relocation.mp3")
38 
39playmusica = True
40volume_musica = 1.0
41 
42while True:
43    for event in pygame.event.get():
44        if event.type == QUIT:
45            exit()
46 
47        elif event.type == KEYDOWN:
48            tasti_premuti = pygame.key.get_pressed()
49 
50            if tasti_premuti[K_ESCAPE]:
51                exit()
52 
53            if tasti_premuti[K_LALT] and tasti_premuti[K_F4]:
54                exit()
55 
56            if event.key == K_UP:
57                move_y = -1
58 
59            if event.key == K_DOWN:
60                move_y = 1
61 
62            if event.key == K_LEFT:
63                move_x = -1
64 
65            if event.key == K_RIGHT:
66                move_x = 1
67 
68            if event.key == K_m:
69                if playmusica:
70                    playmusica = False
71                else:
72                    playmusica = True
73 
74            if event.key == K_KP_PLUS:
75                if volume_musica < (1.0) and pygame.mixer.music.get_busy()==True:
76                    volume_musica += 0.1
77                    pygame.mixer.music.set_volume(volume_musica)
78 
79            if event.key == K_KP_MINUS:
80                if volume_musica > (0.0) and pygame.mixer.music.get_busy()==True:
81                    volume_musica -= 0.1
82                    pygame.mixer.music.set_volume(volume_musica)
83 
84        elif event.type == KEYUP:
85 
86            if event.key == K_UP:
87                move_y = 0
88 
89            if event.key == K_DOWN:
90                move_y = 0
91 
92            if event.key == K_LEFT:
93                move_x = 0
94 
95            if event.key == K_RIGHT:
96                move_x = 0
97 
98        pulsanti_mouse = pygame.mouse.get_pressed()
99 
100        if pulsanti_mouse[0]==1:
101            coordinate_mouse_attuali = pygame.mouse.get_pos()
102 
103            canaleclick = click.play()
104            canaleclick.set_volume( 1.0 - (coordinate_mouse_attuali[0]/640.) , coordinate_mouse_attuali[0]/640.)
105 
106            if imm_sfondo == "introduzione.jpg":
107                if tasto_start.collidepoint(coordinate_mouse_attuali):
108                    canaleapp = applausi.play()
109                    imm_sfondo = "giocoavviato.jpg"
110                    sfondo = pygame.image.load(imm_sfondo).convert()
111 
112        if pulsanti_mouse[2]==1:
113 
114            canaleclick = click2.play()
115 
116            if imm_sfondo == "giocoavviato.jpg":
117                imm_sfondo = "introduzione.jpg"
118                sfondo = pygame.image.load(imm_sfondo).convert()
119 
120        if controller != None and controller.get_button(7):
121            if imm_sfondo == "introduzione.jpg":
122 
123                canaleapp = applausi.play()
124 
125                imm_sfondo = "giocoavviato.jpg"
126                sfondo = pygame.image.load(imm_sfondo).convert()
127 
128    if playmusica == True and pygame.mixer.music.get_busy()==False:
129        pygame.mixer.music.play()
130 
131    if pygame.mixer.music.get_busy()==True and playmusica == False:
132        pygame.mixer.music.stop()
133 
134    pygame.mouse.set_visible(False)
135    coordinate_mouse = pygame.mouse.get_pos()
136 
137    tempo_passato = orologio.tick(60)
138    tempo_passato_sec = tempo_passato/1000.0
139 
140    distanza_spostamento = tempo_passato*speed
141 
142    x += move_x*distanza_spostamento
143    y += move_y*distanza_spostamento
144 
145    tasto_start.move_ip(move_x*distanza_spostamento,move_y*distanza_spostamento)
146 
147    screen.fill((0,0,0))
148    screen.blit(sfondo,(x,y))
149    screen.blit(puntatore,coordinate_mouse)
150 
151    pygame.display.flip()

Analisi

pygame.mixer.pre_init(44100, 16, 2, 4096)

Come potete notare, il mixer deve essere configurato prima dell’inizializzazioone.
In questo caso passiamo i parametri per gestire un mixer a 44100 Hz, 16 bit stereo e 4096 kb di buffer.
Fatto questo, una volta inizializzato pygame, non ci resta che memorizzare gli effetti sonori da noi utilizzati.

applausi = pygame.mixer.Sound("applause-2.wav")
click = pygame.mixer.Sound("beep-3.wav")
click2 = pygame.mixer.Sound("beep-6.wav")
pygame.mixer.music.load("Relocation.mp3")
playmusica = True
volume_musica = 1.0

Da notare come i click e gli applausi sono stati caricati come Sound, mentre la musica viene caricata a parte con pygame.mixer.music.load. Questo perché l’utilizzo è molto diverso e lo vediamo subito nella sezione eventi, dove sono state aggiunte le seguenti righe:

if event.key == K_m:
   if playmusica:
     playmusica = False
   else:
     playmusica = True
 
 if event.key == K_KP_PLUS:
   if volume_musica < (1.0) and pygame.mixer.music.get_busy()==True:
     volume_musica += 0.1
     pygame.mixer.music.set_volume(volume_musica)
 
 if event.key == K_KP_MINUS:
   if volume_musica > (0.0) and pygame.mixer.music.get_busy()==True:
     volume_musica -= 0.1
     pygame.mixer.music.set_volume(volume_musica)

In questo modo, controlleremo l’esecuzione della musica tramite il tasto “M”, facendo variare semplicemente playmusica. Invece con il tasto “+” e “-” andiamo a controllare se il volume è già al suo massimo, ovvero 1.0, e se la musica viene eseguita, aumentiamo e diminuiamo il volume. Questo controllo del volume viene effettuato solo sulla musica e non riguarda alcun effetto Sound.

canaleclick = click.play()
 canaleclick.set_volume( 1.0 - (coordinate_mouse_attuali[0]/640.) ,coordinate_mouse_attuali[0]/640.)

Come potete vedere ora, quando si clicca con il tasto sinistro del mouse viene avviato il suono click nel canaleclick. Canaleclick è proprio un Canale (Channel), poiché viene ritornato dalla funzione play() ed ha varie proprietà che possono essere modificate. Tra queste, ho scelto di modificare il volume. In poche parole, a seconda di dove pigiamo sulla schermata del nostro gioco, sentiremo il click sul canale destro o sinistro. Per fare questo, aggiorno tramite le coordinate del mouse il volume del canale dove viene riprodotto click. Vi ricordo che il volume è un valore float che va da 0.0 a 1.0 ed è per questo che divido per 640, visto che è la larghezza massima della nostra finestra. Si può notare subito la differenza con il click del tasto destro, sia nel codice che avviando il programma, poiché nel click destro del mouse c’è solo la seguente riga:

canaleclick = click2.play()

Per quanto riguarda gli applausi, sono solo disponibili quando si preme su start e si passa al gioco avviato:

if imm_sfondo == "introduzione.jpg":
  if tasto_start.collidepoint(coordinate_mouse_attuali):
     canaleapp = applausi.play()
     imm_sfondo = "giocoavviato.jpg"
     sfondo = pygame.image.load(imm_sfondo).convert()

Il metodo play accetta anche più parametri. Quelli che vi possono servire sono i primi due, ovvero loop e maxtime. Se indicate un numero maggiore di zero, il suono verrà eseguito una volta e poi n volte il numero da voi passato. Se invece volete ripetere un suono all’infinito ma lo volete limitare nel tempo, dovete solo fare come segue (il tempo è indicato in millisecondi):

canale = laser.play(-1, 5000)

Naturalmente il suono applausi è stato aggiunto anche nelle righe che riguarda la pressione del tasto start del joystick.
Per concludere, dobbiamo gestire lo scorrere della musica nel loop principale del gioco:

if playmusica == True and pygame.mixer.music.get_busy()==False:
        pygame.mixer.music.play()
if pygame.mixer.music.get_busy()==True and playmusica == False:
        pygame.mixer.music.stop()

Così controlliamo se la musica deve essere eseguita o meno. Il metodo pygame.mixer.music.get_busy() serve per vedere se la musica è già in esecuzione; è solo un controllo in più. In questo caso non servirebbe, ma per ovviare ad errori di inizializzazione, meglio controllare che la musica sia realmente in esecuzione. Potete gestire l’esecuzione della musica anche senza la variabile Booleana playmusica.

Conclusioni
Con questo articolo abbiamo dato la voce al nostro gioco. Nel prossimo cercheremo di mettere un pò d’ordine tra le cose che abbiamo imparato per poi passare ad elementi più complessi.

Press ESC to close