Sviluppare un gioco in Python: gestire mouse, tastera e joystick

Pubblichiamo un pezzo di Mirco Tracolli

Il codice che stiamo per esaminare in questo articolo è molto complesso, semplicemente perché ci sono molte cose da assimilare; però vi sono le basi per capire il funzionamento delle varie periferiche: mouse, tastiera e joystick. Una volta completato questo tutorial, sarete in grado già di creare una piccola avventura grafica, con qualche piccolo accorgimento. Ulteriori ritocchi saranno apportati in seguito, come l’aggiunta del sonoro, fondamentale per ogni gioco; più avanti utilizzeremo le sprites e impareremo a controllare le collisioni e ad affinare alcuni movimenti. Non escluderei anche un approfondimento sul 3D, visto che può essere molo intrigante. Cominciamo quindi a vedere il codice, verrà successivamente commentato pezzo per pezzo:


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

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

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

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

    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()
           if imm_sfondo == "introduzione.jpg":
             if tasto_start.collidepoint(coordinate_mouse_attuali):
               imm_sfondo = "giocoavviato.jpg"
               sfondo = pygame.image.load(imm_sfondo).convert()

       if pulsanti_mouse[2]==1:
         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":
         imm_sfondo = "giocoavviato.jpg"
         sfondo = pygame.image.load(imm_sfondo).convert()

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

Vorrei premettere che è assolutamente inutile farsi spaventare da questo codice e mi scuso per il disordine. Questo vuol dire che quando organizzerete il vostro gioco dovrete includere queste feature nel vostro Motore di Gioco, cioè quella parte del programma che si occupa di meccanizzare il tutto. In questa sede analizzeremo il codice contenuto tutto in un unico file per rendere meglio l’idea e per capire meglio il funzionamento di questi componenti, ma il tutto può essere organizzato in maniera ottimale per gestire un progetto e un videogioco di più grande portata.

Detto questo, cominciamo ad esaminare le prime righe di codice:


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

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

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

Rispetto agli altri esempi possiamo notare che abbiamo aggiunto “mouse”, il nostro puntatore. È un’immagine png con sfondo trasparente che potete creare voi stessi (quella utilizzata è 30×30 pixel, ma potete creare un puntatore anche più grande). Aggiungere il nostro puntatore è semplice e lo facciamo proprio con l’ultima riga di questa parte di codice.

Ponete particolare attenzione tra i due differenti caricamenti di “sfondo” e “puntatore”.

Il primo si coverte in una semplice superfice senza canale alpha, il secondo invece forziamo la conversione per non perdere il canale. Se non effettuiamo questa operazione, la trasparenza dello sfondo del mouse viene meno e visivamente avremo un puntatore con sfondo bianco, molto antiestetico, non trovate?


speed = 0.5

orologio = pygame.time.Clock()

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

In queste righe impostiamo la velocità di gioco e creiamo l’orologio interno per sincronizzare il tutto. Inoltre creiamo il tasto start. Come potete notare, nell’immagine di sfondo creata per questo esempio, abbiamo un tasto start al centro. Essendo disegnato con tutta l’immagine, per poterlo cliccare con il mouse creiamo un oggetto che abbia la sua superfice, così da poter controllare successivamente se il puntatore vi è sopra o no.

L’oggetto Rect da noi creato non sarà visualizzato a schermo, serve a noi per fare dei calcoli. La prima tupla degli argomenti passati a Rect indica le coordinate del vertice in alto a sinistra del rettangolo che andiamo a creare. La seconda tupla indica invece la larghezza e l’altezza del rettangolo.


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

Ecco finalmente qualcosa di nuovo. Ora vedremo come aggiungere il nostro joystick. Premetto che il controller da me utilizzato è dell’XBOX 360, ma basta un qualsiasi controller con almeno 10 tasti, poi vedremo il perché.

Inizialmente il nostro controller non è collegato e quindi non attribuiamo niente a lui. Controlliamo successivamente se ci sono joystick con la funzione pygame.joystick.get_count(). Quindi se è maggiore di 0, aggiungiamo il primo joystick che troviamo e lo inizializziamo con controller.init().

Se non vi sono controller, non si aggiungerà nulla e vi sarà notificato che serve un joystick con almeno 10 tasti per utilizzare tutte le funzioni del programma.

Infine creiamo delle variabili per memorizzare gli spostamenti fatti, perché in questo file di esempio muoveremo lo sfondo con la tastiera.


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

Iniziamo quindi a controllare gli eventi. Potete ricordare che facciamo un controllo se la finestra viene chiusa con event.type == QUIT, terminando quindi il programma. Ora controlleremo anche quando i tasti vengono premuti.

Verifichiamo quindi i tasti premuti aggiungendoli ad una lista tramite la funzione pygame.key.get_pressed(). In seguito verifichiamo se i tasti premuti corrispondono al tasto ESC o alla combinazione di tasti ALT+F4 ed in questi casi terminiamo il programma.

Possiamo notare che si può interrogare direttamente event per avere i tasti premuti e quindi lo facciamo con le frecce direzionali (K_UP, K_DOWN, K_LEFT, K_RIGHT) e teniamo conto delle eventuali variazioni nel movimento, memorizzandole in move_x e move_y.


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

Se non vogliamo che il nostro sfondo continui a vagare nella direzione da noi premuta, dobbiamo ricordare che quando i tasti direzionali vengono rilasciati si deve cessare di muovere l’oggetto e lo facciamo con queste righe di codice.


if pulsanti_mouse[0]==1:
 coordinate_mouse_attuali = pygame.mouse.get_pos()
 if imm_sfondo == "introduzione.jpg":
  if tasto_start.collidepoint(coordinate_mouse_attuali):
   imm_sfondo = "giocoavviato.jpg"
   sfondo = pygame.image.load(imm_sfondo).convert()

if pulsanti_mouse[2]==1:
 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":
  imm_sfondo = "giocoavviato.jpg"
  sfondo = pygame.image.load(imm_sfondo).convert()

Infine controlliamo i tasti del mouse. Anche in questo caso li memorizziamo in una lista. Più precisamente pygame.mouse.get_pressed() ritorna una “lista” di 3 elementi che rappresentano i tre pulsanti del muose con dei valori interi (0 se non sono premuti e 1 se sono premuti). Nel nostro caso controlliamo se vengono premuti il tasto sinistro (pulsanti_mouse[0]) e il tasto destro (pulsanti_mouse[2]) del mouse.

Per il tasto sinistro del mouse memorizziamo, quando viene pigiato, le coordinate del puntatore in quel momento. Di seguito, se siamo nella schermata di introduzione, controlliamo se le coorinate del mouse si trovano all’interno del pulsante da noi creato in precedenza. Se il tutto è vero, cambiamo l’immagine di sfondo con quella del gioco avviato.

Il tasto destro del muose ci serve invece per tornare alla schermata iniziale una volta cambiato lo sfondo e quindi controlliamo solo se siamo in gioco avviato.

L’ultimo controllo che facciamo riguarda il joystick. Come vi avevo detto prima, se il joystick è presente, controllo se viene premuto il tasto numero 8 (quindi il settimo perché la numerazione parte da zero), che sul joystick dell’XBOX 360 corrisponde al tasto “start”. Potete controllare sul vostro pannello di controllo (per gli utenti windows) quale sia l’ordine dei tasti del vostro controller per cambiarlo a piacimento. Comunque, anche in questo caso, controlliamo se siamo ancora alla schermata di avvio per cambiarla in gioco avviato quando pigiamo il tasto “start” del nostro joystick.


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

Ed ora occupiamoci della parte finale, cioè il loop principale di gioco. Per prima cosa disabilitiamo il puntatore del mouse di sistema, poi memorizziamo le coordinate del mouse, che ci serviranno per stampare a video il nostro puntatore. Quindi memorizziamo il tempo trascorso sincronizzandolo a 60 fps e convertiamo il tutto da millisecondi a secondi.

Calcoliamo dunque la distanza di spostamento in base alla velocità da noi impostata e aggiorniamo le coordinate x e y del nostro sfondo. Visto che abbiamo aggiunto un solo pulsante “immaginario” che si attiva nella sola schermata iniziale, dobbiamo solo aggiornare anche le sue coordinate, altrimenti il pulsante rimarrà sempre al centro dello schermo e non seguirà mai gli spostamenti dello sfondo (visto che il pulsante è tutt’uno con esso) e facciamo il tutto con tasto_start.move_ip(move_x*distanza_spostamento,move_y*distanza_spostamento). Move_ip muove l’oggetto Rect senza creare una nuova copia.

Fatto questo non ci resta che aggiornare lo sfondo dello schermo tramite screen.fill((0,0,0)). Questo comando renderà lo schermo nero, in attesa di essere riscritto. Se non facciamo questa operazione, quando sposteremo il nostro sfondo, rimarranno le tracce dello spostamento, provate a commentare questa riga per capire di cosa sto parlando.

Infine stampiamo a video lo sfondo con le coordinate aggiornate e il puntatore sopra di esso e facciamo aggiornare il buffer video tramite display.flip().

Conclusioni

Con questo articolo abbiamo imparato a gestire gli input di sistema. Prossimamente vedremo come gestire meglio i movimenti, per dare una base del motore di gioco che utilizzi anche un’intelligenza artificiale poco complessa. Poi non rimane che dare vita alle cose facendo sentire musica ed effetti. Comunque per ora potete già cimentarvi nella creazione di una piccola storia punta e clicca, ma fate bene attenzione alla gestione dei pulsanti!

Press ESC to close