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
![](https://www.skillbook.it//Content/Users/gino.visciano18/BlogImage_20240622_94ba2b57-3339-44c2-816f-1c7287fe52a0_.png)
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()
if not gameOver:
# pos è una tupla che coterrà le coordinate x,y del puntatore del mouse
pos = pygame.mouse.get_pos()
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
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
![](https://www.skillbook.it//Content/Users/gino.visciano18/BlogImage_20240717_6fa25f11-32d4-4673-a4cc-02a3dd374ced_.png)
![](https://www.skillbook.it//Content/Users/gino.visciano18/BlogImage_20240717_55241f64-9e82-4415-9ab0-905ef405a429_.png)
pos_y=430
for testo in scheda:
riga = font.render(testo, True, (255,255,255))
screen.blit(riga, (20, pos_y))
pos_y+=27
def show_info(screen, informazioni_del_gioco,wrong_value,score_value,s_tempo,age_value,scheda):
font= pygame.font.SysFont('Verdana', 20)
pos_y=430
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 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.
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:
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
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
- Competenze per programmare
- Impariamo Python giocando al "Solitario del ferroviere"
- Impariamo a programmare con JavaScript
- Laboratori di Logica di programmazione in C
- Introduzione alla Logica degli oggetti
- Ricominciamo ... dal Linguaggio SQL
- APP Mania
- Come sviluppare un Sito con Wordpress
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 GOL, clicca 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