Skillbook Logo
foto profilo

Skill Factory

Lista post > Come progettare e sviluppare giochi per l'educazione e la formazione con Python: lezione 4

Come progettare e sviluppare giochi per l'educazione e la formazione con Python: lezione 4

Mirko Onorato | Skill Factory - 15/07/2024 23:52:03 | in Tutorials

In questa lezione finalmente implementeremo la prima esperienza in HAMMER-XP, l'obiettivo sarà quello di eliminare tutti i numeri pari tra 1 e 100.

L'idea è quella di visualizzare nell'area di gioco una tabella con 100 sprite rettangolari di colore blue che contengono i numeri da 1 a 100. Se Hammer  tocca uno sprite che contiene un numero pari lo score s'incrementa di uno, se per sbaglio si tocca un sprite che contiene un numero dispari allora sarà il wrong a incrementarsi di uno.

Il gioco s'interromperà con il messaggio GAME OVER se il numero di wrong (errori) supererà il numero di score (successi) oppure se il tempo di gioco supererà  i 120 secondi (2 minuti).

Il gioco terminerà con il messaggio GOOL!!!, se verranno eliminati tutti i numeri pari nel tempo utile.

LA CLASSE BLOCCO
La classe seguente servirà per creare gli oggetti sprite che conterranno i numeri da 1 a 100. Una classe è un modello per creare oggetti da utilizzare nei programmi.
Una classe può ereditare il codice di altre classi per specializzarsi a fare qualcosa. La classe Blocco ereditando la classe pygame.sprite.Sprite si specializza nella gestione di sprite perché eredità tutti gli attributi e i metodi che serviranno all'oggetto creato per comportarsi come un sprite. 

# Questa classe permette di creare gli oggetti sprite rettangolari con i numeri
class Blocco(pygame.sprite.Sprite):
    # Costruttore dello sprite
    def __init__(self, color, width, height, text, hit):
        super().__init__()
        self.hit=hit
        self.numero=int(text)
        self.image = pygame.Surface([width, height])
        self.rect = self.image.get_rect()
        self.font= pygame.font.SysFont('arialunicode', 30)
        self.textSurf = self.font.render(text, 1, (255,255,255))
        self.image = pygame.Surface((width, height))
        W = self.textSurf.get_width()
        H = self.textSurf.get_height()
        self.image.fill(color)
        # Centra il testo nel blocco sprite
        self.image.blit(self.textSurf, [width/2 - W/2, height/2 - H/2])

Il costruttore __init__ è il metodo di una classe usato per inizializzare l'oggetto creato, gli argomenti del costruttore:

def __init__(self, color, width, height, text, hit),

sono importanti per capire quali informazioni si dovranno passare alla classe per creare un oggetto di quel tipo. In questo caso per creare uno sprite di tipo Blocco sono richieste le seguenti informazioni:

color=colore dello sprite in formato (red, green, blue)

width=larghezza in pixel dello sprite

height=altezza  in pixel dello sprite

text=testo che corrisponderà al numero visualizzato nello sprite

hit=questo parametro potrà contenere False oppure True (False se il numero assegnato allo sprite sarà dispari, True se il numero assegnato allo sprite sarà pari).

Il comando   self.rect = self.image.get_rect() è importante perché permette di creare l'oggetto serf.rect  con gli attributi x e y, corrispondenti alle coordinate della posizione di uno sprite sullo schermo.

Assegnando un valore a self.rect.x e self.rect.y sarà possibile posizionare qualunque sprite creato all'interno dello schermo.

Vediamo un esempio:

BLUE=(0,0,255) # RED=0 GREEN=0 BLUE=255, impostiamo il colore BLUE con la codifica RGB
primoBloccoSprite=Blocco(BLUE, 35, 35,"1",False) 

il codice Python precedente crea un oggetto sprite di nome primoBloccoSprite di colore blue e di 35X35 pixel. Al centro dello sprite verrà visualizzato il numero 1, quindi il parametro hit dovrà essere impostato a False, perché il numero è dispari.

secondoBloccoSprite=Blocco(BLUE, 35, 35,"2",True) 

il codice Python precedente crea un oggetto sprite di nome secondoBloccoSprite di colore blue e di 35X35 pixel. Al centro dello sprite verrà visualizzato il numero 2, quindi il parametro hit dovrà essere impostato a True, perché il numero è pari.

I comandi seguenti:

primoBloccoSprite.rect.x=100
primoBloccoSprite.rect.y=50
secondoBloccoSprite.rect.x=140
secondoBloccoSprite.rect.y=50

permettono di posizionare i due sprite nell'area di gioco, il primo alla posizione di coordinate (100, 50), il secondo alla posizione di coordinate (140, 50).

COME CREARE E DISPORRE NELL'AREA DI GIOCO GLI SPRITE CON I NUMERI DA 1 A 100
Il codice Python seguente mostra come creare e disporre sull'area di gioco gli sprite con i numeri da 1 a 100:

BLUE=(0, 0, 255) # impostiamo il colore blue
pos_x=55
pos_y=2
for x in range(1,101): # range genera un vettore di numeri interi da 1 a 100, per ogni numero del vettore il for esegue i comandi associati 
      if x%2==0:
            hit=True   # Se il numero è pari imposta hit=True
      else:
            hit=False  # Se il numero è dispari imposta hit=False
      blocco = Blocco(BLUE, 35, 35,str(x),hit) # Crea un blocco di tipo sprite che visualizzera il valore del numero x corrente 
      # Posiziona gli sprite creati nell'area di gioco
      blocco.rect.x=pos_x
      pos_x+=44
      blocco.rect.y=pos_y
      # Aggiunge gli sprite creati ai gruppi di sprite
      block_list.add(blocco)
      sprite_list.add(blocco)
      # Cambia riga dell'area di gioco quando gli sprite hanno occupato tutto lo spazio disponibile sulla riga corrente
      if x==25 or x==50 or x==75:
         pos_x=50
         pos_y+=108

 
COME SPOSTARE HAMMER CON IL MOUSE ED EVITARE CHE IL MARTELLETTO VADA OLTRE I LIMITI DELL'AREA DI GIOCO
Per creare lo sprite hammer abbiamo usato il comando seguente:
 
# oggetto=Classe()
hammer=Hammer() 
 
 
dove Hummer() corrisponde alla classe seguente, vista già nella lezione precedente:
 
class Hammer(pygame.sprite.Sprite):
    def __init__(self):
        super(Hammer, self).__init__()
        # Carica l'immagine di Hammer
        self.image=pygame.image.load('hammer.png')
        # Imposta le dimensioni dell'immagine larghezza, altezza
        self.image = pygame.transform.scale(self.image, (25,50))
        self.images = []
        self.images.append(self.image) # Associamo allo sprite l'immagine di Hammer
        self.index = 0
        self.x=0
        self.y=0
        self.image = self.images[self.index]
        self.rect = self.image.get_rect()
 
A questa classe non dovete passare informazioni quando create l'oggetto hammer, perché il costruttore __init__ non ha argomenti.
 
 
Il codice Python seguente permette di muovere Hammer con il mouse
 
# Muove HAMMER con il mouse
if not gameOver:
    # pos è una tupla che coterrà le coordinate x,y del puntatore del mouse
    pos = pygame.mouse.get_pos()
    # Aggancia Hammer se il puntatore del mouse entra nell'area del martelletto
    if pos[0]>=pos_hammer[0] and pos[0]<=pos_hammer[1] and pos[1]>=pos_hammer[2] and pos[1]<=pos_hammer[3]:
        hammer_agganciato=True
    if hammer_agganciato:
        # I controlli seguenti limitano i movimenti di HAMMER solo nella sezione di gioco
        if pos[0]>5 and pos[0]<1180 and pos[1]>25 and pos[1]<344:
            hammer.rect.x=pos[0]-5  
            hammer.rect.y=pos[1]-25
        else: 
            hammer_agganciato=False
            if pos[1]<=25:     
                hammer.rect.y=0
            elif pos[1]>=344:     
                hammer.rect.y=319
            if pos[0]<=5:
                hammer.rect.x=0 
            elif pos[0]>=1175:
                hammer.rect.x=1175
            # Legge le coordinate della posizione di Hammer nell'area di gioco servono per impostare le azioni e i controlli
            pos_hammer[0]=hammer.rect.x   
            pos_hammer[1]=hammer.rect.x+25
            pos_hammer[2]=hammer.rect.y  
            pos_hammer[3]=hammer.rect.y+50
 
Per iniziare a muovere Hammer bisognerà prima agganciarlo, spostando il puntatore del mouse sul martelletto.
La variabile hammer_agganciato diventerà True se il martelletto verrà agganciato dal puntatore del mouse, altrimenti sarà uguale a False.
 
 
LA FUNZIONE PER VISUALIZZARE LE INFORMAZIONI DEL GIOCO
 
La funzione show_info permette di visualizzare le informazioni del gioco:
 
Gli argomenti che riceve la funzione sono i seguenti:
 
screen=riferimento dello schermo su cui visualizzare le informazioni
informazioni_del_gioco=oggetto che contiene il progressivo dell'esperienza e il livello corrente
wrong_value=errori
score_value=successi
s_tempo=stringa che indica il tempo di gioco trascorso nel formato hh:mm:ss
age_value=età degli studenti a cui è destinata l'esperienza
scheda=vettore che contiene le righe con la descrizione dell'esperienza
 
Il codice seguente  viene utilizzato dalla funzione show_info per visualizzare la descrizione dell'esperienza:
 
# Imposta il tipo di carattere da usare e le dimenzioni
font= pygame.font.SysFont('Verdana', 20)
pos_y=430
# Legge il testo da stampare dal vettore scheda e lo stampa alla posizione x=20 e y=pos_y
for testo in scheda:
      riga = font.render(testo, True, (255,255,255))
      screen.blit(riga, (20, pos_y))
      pos_y+=27
 
Il metodo render dell'oggetto font crea il testo da stampare di colore bianco.
Il metodo blit dell'oggetto screen stampa sullo schermo il testo creato con metodo render.
 
Di seguito il codice completo della funzione show_info:
 
# Visualizza le informazioni del gioco
def show_info(screen, informazioni_del_gioco,wrong_value,score_value,s_tempo,age_value,scheda):
    # Imposta il tipo di carattere da usare e le dimenzioni
    font= pygame.font.SysFont('Verdana', 20)
    pos_y=430
    # Legge il testo da stampare dal vettore scheda e lo stampa alla posizione x=20 e y=pos_y
    for testo in scheda:
        riga = font.render(testo, True, (255,255,255))
        screen.blit(riga, (20, pos_y))
        pos_y+=27
    font= pygame.font.SysFont('arialunicode', 30)
    exp = font.render(str(informazioni_del_gioco.experience_value), True, (255,255,255))
    screen.blit(exp, (820, 430))
    level = font.render(str(informazioni_del_gioco.level_value), True, (255,255,255))
    screen.blit(level, (1100, 430))
    wrong = font.render(str(wrong_value), True, (255,255,255))
    screen.blit(wrong, (820, 510))
    score = font.render(str(score_value), True, (255,255,255))
    screen.blit(score, (1100, 510))
    t = font.render(s_tempo, True, (255,255,255))
    screen.blit(t, (715, 585))
    age = font.render(str(age_value), True, (255,255,255))
    screen.blit(age, (1100, 585))
 
LA FUNZIONE CHE CONVERTE IL TEMPO TRASCORSO IN HH:MM:SS

La funzione struttura_tempo riceve il tempo trascorso in secondi e lo converte in una stringa nel formato hh:mm:ss.

Per calcolare il tempo trascorso basta leggere l'ora di sistema quando parte il gioco e confrontarla ogni volta con l'ora di sistema corrente.

Per fare questa operazioni, quando parte il gioco, possiamo usare il comando:

t1=time.time()

La funzione time() dell'oggetto time, legge l'ora di sistema alla partenza del gioco e la converte in secondi trascorsi dal 01/01/1970; salvando questo valore nella variabile  t1, conosciamo il numero di secondi trascorsi dal 01/01/1970 all'avvio del gioco.

Per conoscere il tempo trascorso durante il gioco, basta rieseguire il comando:

t2=time.time()

In questo caso la variabile t2 conterrà il numero di secondi trascorsi dal 01/01/1970 all'ora corrente; quindi, per ottenere il numero di secondi trascorsi dall'inizio del gioco basterà calcolare la differenza tra t2 e t1
 
t_value=t2-t1
 
La funzione struttura_tempo, riceve come argomento i secondi trascorsi t_value e li converte in ore, minuti e secondi e alla fine li concatena formando la stringa hh:mm:ss.
 
Di seguito il codice completo della funzione struttura_tempo:

def struttura_tempo(t_value):
    ore=math.floor(t_value/3600)
    minuti=t_value-(ore*3600)
    minuti=math.floor(minuti/60)
    secondi=t_value-((ore*3600)+(minuti*60))
    secondiStr="0"+str(secondi)
    minutiStr="0"+str(minuti)
    oreStr="0"+str(ore)
    s_tempo=oreStr[-2:]+":"+minutiStr[-2:]+":"+secondiStr[-2:]
    return s_tempo

LA FUNZIONE CHE CREA IL PRIMO LIVELLO DELLA PRIMA ESPERIENZA DI MATEMATICA

La funzione exp_01_01 viene utilizzata per creare il primo livello della prima esperienza di matematica.

La funzione riceve i seguenti argomenti:

block_list=riferimento del gruppo in cui aggiungere i blocchi dell'esperienza, serve per rilevare le collisioni con Hammer

sprite_list=riferimento del gruppo in cui aggiungere i blocchi dell'esperienza, serve per visualizzare tutti gli sprite creati nell'area di gioco 

informazioni_del_gioco=gli attributi di questo oggetto servono per impostare il progressivo dell'esperienza, il livello, lo score obiettivo, la durata del gioco

La classe con cui viene creato questo oggetto è la seguente:

class Informazioni_del_gioco():
    # Costruttore dello sprite, quando viene eseguito dovete passare il colore dello sprite, la larghezza e l'altezza in pixel
    def __init__(self, experience_value,level_value,score_value_exit,t_value_exit):
        super().__init__()
        self.experience_value=experience_value
        self.level_value=level_value
        self.score_value_exit=score_value_exit
        self.t_value_exit=t_value_exit

Infine, c'è l'argomento:

scheda=vettore che conterrà le stringhe che corrispondono alle righe che descrivono l'esperienza che si sta giocando

Di seguito il codice completo della funzione exp_01_01:

def exp_01_01(block_list,sprite_list,informazioni_del_gioco,scheda):
    BLUE=(0, 0, 255) # impostiamo il colore blue
    pos_x=55
    pos_y=2
    for x in range(1,101): 
        if x%2==0:
            hit=True
        else:
            hit=False
        blocco = Blocco(BLUE, 35, 35,str(x),hit) 
        # Posiziona gli sprite creati nell'area di gioco
        blocco.rect.x=pos_x
        pos_x+=44
        blocco.rect.y=pos_y
        # Aggiunge gli sprite creati ai gruppi di sprite
        block_list.add(blocco)
        sprite_list.add(blocco)
        if x==25 or x==50 or x==75:
           pos_x=50
           pos_y+=108
    informazioni_del_gioco.experience_value=1
    informazioni_del_gioco.level_value=1   
    informazioni_del_gioco.score_value_exit=50    # Il valore di score obiettivo è quello di 50 successi
    informazioni_del_gioco.t_value_exit=120          # L'esperienza dura 2 minuti
    # Aggiungiamo al vettore scheda le stringhe che corrispondono alle righe che descrivono l'esperienza che si sta giocando 
    scheda.append("MATEMATICA I")
    scheda.append("Elimina dall'area di gioco i numeri pari.")
    scheda.append("Il gioco termina se gli errori superano le risposte esatte.")
    scheda.append("L'esperienza avrà una durata di 2 minuti.")
    scheda.append("Aggancia Hammer con il puntatore del mouse e inizia a")
    scheda.append("giocare.")

LA GESTIONE DEI SUCCESSI (SCORE) O DEGLI ERRORI (WRONG) DOPO LE COLLISIONI

Il metodo spritecollide permette di rilevare la collisione tra uno sprite e gli sprite di un gruppo.

Per rilevare le collisioni tra Hammer e i blocchi con i numeri, dovete passare come argomenti alla funzione spritecollide, sia il riferimento dell'oggetto hammer, sia il riferimento del gruppo che contiene i riferimenti dei blocchi con i numeri presenti nell'area di gioco: block_list.

Il terzo argomento della funzione, se impostato a True, indica che l'oggetto che collide con Hammer, dovrà essere eliminato dal gruppo e non dovrà più essere visibile nell'area di gioco.

Gli oggetti che collidono con Hammer vengono aggiunti al gruppo  blocks_hit_list; quindi, con un ciclo for possiamo ottenere il riferimento di tutti gli sprite che hanno avuto una collisione con Hummer. Leggendo il valore dell'attributo hit dei blocchi presenti nel gruppo  blocks_hit_list sarà possibile capire se il blocco era associato a un numero pari (hit=True) o a un numero dispari (hit=false).

In base allo stato dell'attributo hit (True/False) vengono aggiornati i valori dei contatori wrong e score, come mostra il codice Python seguente:

# Aggiunge al gruppo blocks_hit_list i riferimenti dei blocchi che collidono con l'oggetto hummer
blocks_hit_list = pygame.sprite.spritecollide(hammer, block_list, True)
# Legge il riferimento dei blocchi che hanno avuto una collisione con Hammer e verifica se incrementare il contatore score o wrong
for blocco in blocks_hit_list:
    # Genera un suono, come un click, per indicare che c'è stata una collisione

     winsound.Beep(3000,1)
    # Incrementa di 1 score_value se hit è True (numero pari), altrimenti incrementa di 1 wrong_value se hit è False (numero dispari)

    if blocco.hit:
       score_value+=1
    else:
       wrong_value+=1

La funzione Beep della libreria winsound emette un breve suono  di 3000 HZ, serve per indicare che Hammer ha toccato un blocco con un numero.

 

LA FINE DEL GIOCO

Il gioco termina con GAME OVER se il numero di WRONG (Errori) sumera il numero di SCORE (Successi) oppure se il tempo di gioco supera la durata prevista. In questo caso verrà emesso un suono che indica un insuccesso e verrà visualizzata l'etichetta GAME OVER:

I

Il gioco termina con GOAL!!! se viene raggiunto l'obiettivo previsto dall'esperienza che prevede che Hammer elimini dall'area di gioco tutti i blocchi con numeri pari. In questo caso verrà emesso un suono che indica successo e verrà visualizzata l'etichetta GOAL!!!:

Le etichette GAME OVER e GOAL!!! sono due oggetti sprite creati nel modo seguente:

bloccoGameOver=Etichetta((255,0,0),200,40,500,164,'GAME OVER')
bloccoGoal=Etichetta((0,200,86),200,40,500,164,'GOOL!!!')

Entrambi gli oggetti sono dello stesso tipo, perché sono stati creati con la classe Etichette:

class Etichetta(pygame.sprite.Sprite):
    # Quando si crea l'oggeto sprite passare il colore, la larghezza, l'altezza
    # la posizione dello sprite nell'ambiente di gioco
    def __init__(self, color, width, height, x,y,text):
        super().__init__()
        self.image = pygame.Surface([width, height])
        self.rect = self.image.get_rect()
        self.rect.x=x
        self.rect.y=y
        self.font= pygame.font.SysFont('arialunicode', 30)
        self.textSurf = self.font.render(text, 1, (255,255,255))
        self.image = pygame.Surface((width, height))
        W = self.textSurf.get_width()
        H = self.textSurf.get_height()
        self.image.fill(color)
        self.image.blit(self.textSurf, [width/2 - W/2, height/2 - H/2])

Il codice Python seguente mostra come viene gestita la fine del gioco:

# Il gioco termina se gli errori sono maggiori dei successi, se si va oltre il tempo massimo disponibile, se si raggiunge l'obiettivo previsto
if wrong_value>score_value or t_value==informazioni_del_gioco.t_value_exit or score_value==informazioni_del_gioco.score_value_exit:
    # Impostando a True questa variabile vengono inibiti tutti i movimenti di Hummer e la gestione degli eventi, quindi il gioco termina
   gameOver=True
   # Obiettivo raggiunto
   if score_value==informazioni_del_gioco.score_value_exit:
      sprite_list.add(bloccoGoal)
      beep_goal()
   else: # Obiettivo non raggiunto
      beep_game_over()
      sprite_list.add(bloccoGameOver)

Per gestire le tonalità sonore che indicano il successo oppure l'insuccesso sono state create le funzioni:

beep_goal() # Successo

beep_game_over() # Insuccesso

Per creare una tonalità sonora che fa immaginare il successo si è pensato ad una sequenza di suoni con frequenza crescente; al contrario per creare una tonalità sonora che fa immaginare un insuccesso si è pensato ad un sequenza di suoni con frequenza decrescente.
 
Il codice Python sorgente mostra come sono state create le due funzioni:
 
# Genera una tonalità che indica il successo
def beep_goal():
    frequenza=150
    durata=[400,150,200,250,700]
    for x in range(5):
        winsound.Beep(frequenza,durata[x])
        frequenza+=150
    winsound.Beep(frequenza,1000)


# Genera una tonalità che indica un  insuccesso
def beep_game_over():
    frequenza=400
    durata=[600,300,200,300,700]
    for x in range(5):
        winsound.Beep(frequenza,durata[x])  
        frequenza-=30
    winsound.Beep(frequenza,1000)

COME ESEGUIRE IL GIOCO

Per eseguire il gioco HAMMER-XP dovete installare il framework Python sul vostro computer.

Per eseguire il download del setup per installare Python clicate qui oppure collegatevi al link seguente: https://www.python.org/downloads/.
Dopo il download del setup, eseguitelo per installare Python.

Attenzione, durante l'installazione è importante spuntare l'opzione "Add Python 3.X to PATH:



Dopo che avete impostato l'opzione, cliccate sul comando Install Now.

Per verificare se il framework Python è stato installato correttamente, dal prompt dei comandi del sistema operativo, eseguite il comando:

python --version

Se l'installazione è andata a buon fine viene visualizzata la versione di Python, come mostra l'immagine seguente:

Successivamente create una cartella "HAMMER-XP", in questa cartella copiate i file seguenti:

hammer_experience_matematica_001_V01.py

hammer.png

sfondo_hammer_xp.png

Per il download dello zip con i tre file cliccate qui.

Estraete i file nella cartella "HAMMER-XP".

Per avviare il gioco fate doppio click sul file   hammer_experience_matematica_001_V01.py.

Nella prossima lezione aggiungeremo all'esperienza di Matematica I, altri 3 livelli:

1) Elimina 3 e i multipli di 3;
2) Elimina 7 e i divisori di 7;
3) Elimina i numeri primi.


< LEZIONE PRECEDENTE VAI ALLA PRIMA LEZIONE


T U T O R I A L S    S U G G E R I T I


PER IMPARARE A PROGRAMMARE, SEGUI LE PLAYLIST SUL NOSTRO CANALE YOUTUBE "SKILL FACTORY CHANNEL": clicca qui per accedere alle playlist


PAR GOL (Garanzia di Occupabilità dei Lavoratori)

Se sei residente in Campania e cerchi lavoro, sai che puoi partecipare gratuitamente ad un corso di formazione professionale PAR GOL?

I corsi di formazione professionale PAR GOL sono finanziati dalla Regione Campania e ti permettono di acquisire una Qualifica Professionale Europea (EQF) e di partecipare ad un tirocinio formativo aziendale.

 

Invia il tuo CV o una manifestazione d'interesse a: recruiting@skillfactory.it

oppure

chiama ai seguenti numeri di telefono:

Tel.: 081/18181361
Cell.: 327 0870141

oppure

Contattaci attraverso il nostro sito: www.skillfactory.it


Per maggiori informazioni sul progetto PAR GOLclicca qui.

Per maggiori informazioni sulle Qualifiche Professionali Europee (EQF)clicca qui.


Academy delle professioni digitali

Per consultare il catalogo dei corsi online della nostra Academy ...

... collegati al nostro sito: www.skillfactory.it

 

Share Button
TOP