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.

imm_sfondo = "introduzione.jpg"
mouse = "puntatore.png"

import pygame
from pygame.locals import *
from sys import exit

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

screen = pygame.display.set_mode((640,480), DOUBLEBUF | HWSURFACE, 32)
pygame.display.set_caption("Input Vari")

sfondo = pygame.image.load(imm_sfondo).convert()
puntatore = pygame.image.load(mouse).convert_alpha()

speed = 0.5
orologio = pygame.time.Clock()

tasto_start = pygame.Rect((200,100),(200,200))

controller = None

if pygame.joystick.get_count() > 0:
    controller = pygame.joystick.Joystick(0)
    controller.init()

if controller is None:
    print ("Siamo spiacenti, ma hai bisogno di un joystick con almeno 10 tasti!")

x , y = 0,0
move_x, move_y = 0,0

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

while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            exit()

        elif event.type == KEYDOWN:
            tasti_premuti = pygame.key.get_pressed()

            if tasti_premuti[K_ESCAPE]:
                exit()

            if tasti_premuti[K_LALT] and tasti_premuti[K_F4]:
                exit()

            if event.key == K_UP:
                move_y = -1

            if event.key == K_DOWN:
                move_y = 1

            if event.key == K_LEFT:
                move_x = -1

            if event.key == K_RIGHT:
                move_x = 1

            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)

        elif event.type == KEYUP:

            if event.key == K_UP:
                move_y = 0

            if event.key == K_DOWN:
                move_y = 0

            if event.key == K_LEFT:
                move_x = 0

            if event.key == K_RIGHT:
                move_x = 0

        pulsanti_mouse = pygame.mouse.get_pressed()

        if pulsanti_mouse[0]==1:
            coordinate_mouse_attuali = pygame.mouse.get_pos()

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

            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()

        if pulsanti_mouse[2]==1:

            canaleclick = click2.play()

            if imm_sfondo == "giocoavviato.jpg":
                imm_sfondo = "introduzione.jpg"
                sfondo = pygame.image.load(imm_sfondo).convert()

        if controller != None and controller.get_button(7):
            if imm_sfondo == "introduzione.jpg":

                canaleapp = applausi.play()

                imm_sfondo = "giocoavviato.jpg"
                sfondo = pygame.image.load(imm_sfondo).convert()

    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()

    pygame.mouse.set_visible(False)
    coordinate_mouse = pygame.mouse.get_pos()

    tempo_passato = orologio.tick(60)
    tempo_passato_sec = tempo_passato/1000.0

    distanza_spostamento = tempo_passato*speed

    x += move_x*distanza_spostamento
    y += move_y*distanza_spostamento

    tasto_start.move_ip(move_x*distanza_spostamento,move_y*distanza_spostamento)

    screen.fill((0,0,0))
    screen.blit(sfondo,(x,y))
    screen.blit(puntatore,coordinate_mouse)

    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