5.2.5 Evita i Mattoni
5.2.5.1 Panoramica

In questo progetto, giochiamo a un gioco di evitamento dei mattoni in cui i giocatori usano un gamepad Micro:bit per muovere il loro indicatore LED a sinistra e a destra mentre evitano i mattoni che cadono dall’alto. Ci sono tre stati: a) un’icona dinamica all’avvio, b) azioni di evitamento in tempo reale durante il gioco, e c) un punteggio finale dopo le collisioni.
I giocatori guadagnano 1 punto dopo ogni evitamento (quando il mattone raggiunge il fondo), e il gioco finisce quando si scontrano con un mattone; il punteggio finale viene visualizzato con un effetto di scorrimento.
Il gioco può essere avviato o resettato premendo entrambi i pulsanti A+B. Questo meccanismo di gioco semplice combina la reattività in tempo reale con l’anticipazione strategica.

5.2.5.2 Parti Richieste
|
|
|
|---|---|---|
Scheda micro:bit V2 (auto-fornita) ×1 |
Smart Gamepad micro:bit (assemblato) ×1 |
Batteria AAA (auto-fornita) ×4 |
5.2.5.3 Flusso del Codice

5.2.5.4 Codice di Test
⚠️ Nota che la soglia iniziale 300 nel codice può essere modificata in base alle proprie esigenze. Più alto è il valore, più lentamente cadrà il mattone.
Codice completo:
from microbit import *
import random
import utime
# Variabili di gioco
game_state = 0 # 0: Iniziale, 1: In gioco, 2: Game Over
player_col = 2 # Colonna iniziale del giocatore
brick_x = random.randint(0, 4) # Colonna iniziale del mattone
brick_y = 0 # Riga iniziale del mattone
score = 0
brick_move_speed = 300 # Velocità di caduta del mattone (ms)
last_brick_time = utime.ticks_ms()
flash_count = 0 # Conteggio lampeggi per Game Over
# Flag per il debounce dei pulsanti
a_pressed_flag = False
b_pressed_flag = False
start_button_pressed = False
# Configura i pin dei pulsanti
pin13.set_pull(pin13.PULL_UP) # Pulsante E (destra)
pin15.set_pull(pin15.PULL_UP) # Pulsante C (sinistra)
pin5.set_pull(pin5.PULL_UP) # Pulsante A
pin11.set_pull(pin11.PULL_UP) # Pulsante B
# Funzione per disegnare il gioco sulla matrice LED
def draw_game():
display.clear()
if game_state == 1: # In gioco
display.set_pixel(player_col, 4, 9) # Giocatore in basso
display.set_pixel(brick_x, brick_y, 5) # Mattone
elif game_state == 0: # Iniziale
if utime.ticks_ms() % 1000 < 500: # Lampeggia
display.show(Image.SQUARE)
else:
display.show(Image.SQUARE_SMALL)
elif game_state == 2: # Game Over
if flash_count < 3:
display.scroll(str(score))
utime.sleep(0.5)
display.clear()
utime.sleep(0.5)
else:
display.scroll(str(score))
# Funzione per resettare il gioco
def reset_game():
global game_state, player_col, brick_x, brick_y, score, flash_count
game_state = 1
player_col = 2
brick_x = random.randint(0, 4)
brick_y = 0
score = 0
flash_count = 0
display.clear()
# Funzione per controllare le collisioni
def check_collision():
global game_state
if brick_y == 4 and brick_x == player_col:
game_state = 2 # Game Over
display.clear()
# Loop principale del gioco
while True:
# Gestione dei pulsanti A+B per avviare/resettare il gioco
if not pin5.read_digital() and not pin11.read_digital(): # A e B premuti
if not start_button_pressed:
start_button_pressed = True
utime.sleep_ms(50) # Debounce
if not pin5.read_digital() and not pin11.read_digital(): # Conferma pressione
reset_game()
else:
start_button_pressed = False
if game_state == 1: # In gioco
# Movimento del giocatore (C per sinistra, E per destra)
if not pin15.read_digital(): # Pulsante C (sinistra)
if not a_pressed_flag:
if player_col > 0:
player_col -= 1
a_pressed_flag = True
utime.sleep_ms(50)
else:
a_pressed_flag = False
if not pin13.read_digital(): # Pulsante E (destra)
if not b_pressed_flag:
if player_col < 4:
player_col += 1
b_pressed_flag = True
utime.sleep_ms(50)
else:
b_pressed_flag = False
# Caduta del mattone
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, last_brick_time) > brick_move_speed:
last_brick_time = current_time
brick_y += 1
if brick_y > 4: # Mattone ha raggiunto il fondo
brick_x = random.randint(0, 4)
brick_y = 0
score += 1
check_collision()
draw_game()
utime.sleep_ms(10) # Breve ritardo per il loop

Breve spiegazione:
① Inizializza le variabili correlate, inclusa la colonna iniziale del giocatore, la riga e la velocità del mattone, e imposta la posizione del giocatore sulla colonna iniziale.
Chiama una funzione on_start.
from microbit import *
import random
import utime
# Variabili di gioco
game_state = 0 # 0: Iniziale, 1: In gioco, 2: Game Over
player_col = 2 # Colonna iniziale del giocatore
brick_x = random.randint(0, 4) # Colonna iniziale del mattone
brick_y = 0 # Riga iniziale del mattone
score = 0
brick_move_speed = 300 # Velocità di caduta del mattone (ms)
last_brick_time = utime.ticks_ms()
flash_count = 0 # Conteggio lampeggi per Game Over
# Flag per il debounce dei pulsanti
a_pressed_flag = False
b_pressed_flag = False
start_button_pressed = False
# Configura i pin dei pulsanti
pin13.set_pull(pin13.PULL_UP) # Pulsante E (destra)
pin15.set_pull(pin15.PULL_UP) # Pulsante C (sinistra)
pin5.set_pull(pin5.PULL_UP) # Pulsante A
pin11.set_pull(pin11.PULL_UP) # Pulsante B
# Funzione per disegnare il gioco sulla matrice LED
def draw_game():
display.clear()
if game_state == 1: # In gioco
display.set_pixel(player_col, 4, 9) # Giocatore in basso
display.set_pixel(brick_x, brick_y, 5) # Mattone
elif game_state == 0: # Iniziale
if utime.ticks_ms() % 1000 < 500: # Lampeggia
display.show(Image.SQUARE)
else:
display.show(Image.SQUARE_SMALL)
elif game_state == 2: # Game Over
if flash_count < 3:
display.scroll(str(score))
utime.sleep(0.5)
display.clear()
utime.sleep(0.5)
else:
display.scroll(str(score))
# Funzione per resettare il gioco
def reset_game():
global game_state, player_col, brick_x, brick_y, score, flash_count
game_state = 1
player_col = 2
brick_x = random.randint(0, 4)
brick_y = 0
score = 0
flash_count = 0
display.clear()
# Funzione per controllare le collisioni
def check_collision():
global game_state
if brick_y == 4 and brick_x == player_col:
game_state = 2 # Game Over
display.clear()
② Per quanto riguarda questa funzione, fa apparire il mattone in una colonna casuale da 0 a 4 all’inizio del gioco.
# Loop principale del gioco
while True:
# Gestione dei pulsanti A+B per avviare/resettare il gioco
if not pin5.read_digital() and not pin11.read_digital(): # A e B premuti
if not start_button_pressed:
start_button_pressed = True
utime.sleep_ms(50) # Debounce
if not pin5.read_digital() and not pin11.read_digital(): # Conferma pressione
reset_game()
else:
start_button_pressed = False
③ Determina se “A+B è premuto e il gioco non è iniziato”. Se sì e l’avvio è contrassegnato come stato iniziale, contrassegna prima lo stato di avvio e conferma di nuovo se i pulsanti sono ancora premuti dopo un breve ritardo. Se lo sono, resetta il gioco (chiama la funzione reset_game) e registra il tempo. Altrimenti, annulla il contrassegno di avvio.
if game_state == 0: # Iniziale
if utime.ticks_ms() % 1000 < 500: # Lampeggia
display.show(Image.SQUARE)
else:
display.show(Image.SQUARE_SMALL)
④ La seguente funzione resetta il gioco allo stato iniziale. Imposta lo stato su “gaming” (game_state=1) e posiziona il giocatore nella posizione iniziale. E il mattone apparirà in una colonna casuale (0~4) e nella riga 0, e i punteggi si azzerano. Infine, la pressione di A/B è contrassegnata come non attivata, e la matrice viene quindi cancellata.
# Funzione per resettare il gioco
def reset_game():
global game_state, player_col, brick_x, brick_y, score, flash_count
game_state = 1
player_col = 2
brick_x = random.randint(0, 4)
brick_y = 0
score = 0
flash_count = 0
display.clear()
⑤ Quando lo stato del gioco è 0-stato iniziale (non in gioco dopo l’accensione), l’icona visualizzata lampeggerà.
elif game_state == 0: # Iniziale
if utime.ticks_ms() % 1000 < 500: # Lampeggia
display.show(Image.SQUARE)
else:
display.show(Image.SQUARE_SMALL)
⑥ Quando è 2-game over, il punteggio sarà controllato in base al conteggio dei lampeggi (flash_count). Se il conteggio <3, ripete “mostra punteggio → breve ritardo → cancella display → breve ritardo → conteggio+1”; quando il conteggio raggiunge 3, mostra sempre il punteggio ed estende il ritardo.
elif game_state == 2: # Game Over
if flash_count < 3:
display.scroll(str(score))
utime.sleep(0.5)
display.clear()
utime.sleep(0.5)
else:
display.scroll(str(score))
⑦ Nello stato 1-gaming, quando si preme C senza attivare il contrassegno di pressione e la colonna del giocatore > 0, la colonna del giocatore -1 e contrassegna il pulsante C come attivato (con ritardo per anti-jitter); premere E senza attivare e quando la colonna del giocatore < 4, e la colonna +1 con E attivato (ritardo); se non viene eseguita alcuna azione, il contrassegno di attivazione dei pulsanti corrispondenti viene resettato.
if game_state == 1: # In gioco
# Movimento del giocatore (C per sinistra, E per destra)
if not pin15.read_digital(): # Pulsante C (sinistra)
if not a_pressed_flag:
if player_col > 0:
player_col -= 1
a_pressed_flag = True
utime.sleep_ms(50)
else:
a_pressed_flag = False
if not pin13.read_digital(): # Pulsante E (destra)
if not b_pressed_flag:
if player_col < 4:
player_col += 1
b_pressed_flag = True
utime.sleep_ms(50)
else:
b_pressed_flag = False
⑧ Calcola la differenza tra il tempo corrente e l’ultimo tempo di movimento del mattone. Se questa differenza supera la soglia di velocità del mattone, aggiorna il tempo di movimento del mattone e la riga del mattone +1. Se la riga > 4 (raggiungendo il bordo), resetta il mattone a una colonna casuale con la sua riga=zero, e punteggio+1.
Chiama le funzioni di rilevamento delle collisioni e di rendering della grafica del gioco per far cadere i mattoni, resettare dopo aver raggiunto il bordo, accumulare il punteggio e aggiornare lo stato del gioco in tempo reale.
Invoca le funzioni di rilevamento delle collisioni e di rendering della grafica del gioco per ottenere l’avanzamento automatico dei mattoni, il reset del bordo, l’accumulo del punteggio e gli aggiornamenti dello stato del gioco in tempo reale.
# Caduta del mattone
current_time = utime.ticks_ms()
if utime.ticks_diff(current_time, last_brick_time) > brick_move_speed:
last_brick_time = current_time
brick_y += 1
if brick_y > 4: # Mattone ha raggiunto il fondo
brick_x = random.randint(0, 4)
brick_y = 0
score += 1
check_collision()
⑨ Determina se il gioco è finito: prima controlla se “la colonna del mattone corrisponde a quella del giocatore” e “se la riga del mattone corrisponde a quella del giocatore”. Se entrambe le condizioni sono soddisfatte (cioè, i mattoni si sovrappongono al giocatore), imposta il gioco allo stato 2 (game over), cancella il display e resetta il conteggio dei lampeggi.
“Game over in caso di collisione.”
# Funzione per controllare le collisioni
def check_collision():
global game_state
if brick_y == 4 and brick_x == player_col:
game_state = 2 # Game Over
display.clear()
⑩ Renderizza gli elementi visivi del gioco: prima cancella il display, e poi traccia i punti con una luminosità di 255 (Giocatore) alle posizioni fisse della riga e della colonna corrente del giocatore; se il gioco è iniziato (game_state=1), traccia i punti con una luminosità di 85 (mattone) alla riga e alla colonna del mattone. Così possiamo distinguere i mattoni dal giocatore in base alla loro luminosità.
def draw_game():
display.clear()
if game_state == 1: # In gioco
display.set_pixel(player_col, 4, 9) # Giocatore in basso
display.set_pixel(brick_x, brick_y, 5) # Mattone
elif game_state == 0: # Iniziale
if utime.ticks_ms() % 1000 < 500: # Lampeggia
display.show(Image.SQUARE)
else:
display.show(Image.SQUARE_SMALL)
elif game_state == 2: # Game Over
if flash_count < 3:
display.scroll(str(score))
utime.sleep(0.5)
display.clear()
utime.sleep(0.5)
else:
display.scroll(str(score))
5.2.5.5 Risultato del Test

Dopo aver caricato il codice, inserisci la scheda micro:bit nello slot del gamepad (batterie installate) e sposta l’interruttore su “ON”.
È nello stato iniziale 0 dopo l’accensione e la matrice lampeggia due icone quadrate.
Premi A e B (per almeno 1 secondo) per avviare il gioco (nello stato 1-gaming), e un mattone cadrà in una colonna casuale. Ora puoi muoverti a sinistra/destra premendo C/E. Ogni volta che eviti un mattone, il punteggio +1.
Game over in caso di collisione (2-game over), e il punteggio finale verrà visualizzato sulla matrice. Se vuoi giocare un altro round, premi di nuovo A e B. Spegni per uscire dal gioco (sposta l’interruttore DIP su “OFF”).

Suggerimento: Se non c’è risposta sulla scheda, premi il pulsante di reset sul retro della scheda micro:bit.



