- Home
- iThink
- Programmazione per Ragazzi
- Snake in Python con Pygame, passo per passo
Snake in Python con Pygame, passo per passo
Divento Programmatore — Capitolo 7: Snake — Il Classico dei Classici
Obiettivi di Apprendimento
Al termine di questo capitolo avrai costruito un gioco completo di Snake in tempo reale e imparerai a:
- Installare librerie Python esterne usando lo strumento
pip - Comprendere la struttura del Game Loop (il motore continuo di ogni videogioco)
- Gestire la frequenza di aggiornamento dello schermo usando l'orologio interno (
clock.tick) - Catturare gli eventi di input della tastiera per cambiare direzione in tempo reale
- Creare un sistema di collisioni discrete tra lo Snake, il cibo, i bordi e il proprio corpo
Termini Fondamentali
| Termine | Simbolo | Significato | | --------- | --------- | ------------- | | pygame | 🎮 | Nota libreria Python di terze parti specificamente progettata per facilitare la creazione di giochi multimediali bidimensionali | | Game Loop | 🔄 | Ciclo infinito fondamentale che gestisce la logica di gioco, l'input del giocatore e il ridisegno grafico a ogni fotogramma | | pip | 📦 | Gestore di pacchetti standard di Python usato per scaricare e installare librerie esterne non presenti nell'installazione base | | FPS (Frames Per Second) | ⏱️ | Unità di misura della fluidità di un gioco, indicante quante volte al secondo il codice del gioco viene eseguito e visualizzato | | pygame.Rect | 📐 | Oggetto geometrico fornito da Pygame per rappresentare aree rettangolari e gestire facilmente posizioni, dimensioni e collisioni |
La Storia: La consacrazione sui telefoni tascabili
Il concetto originale del gioco di Snake risale al 1976 con il gioco arcade intitolato Blockade, in cui due giocatori si sfidavano a guidare una traccia continua cercando di far schiantare l'avversario contro la scia dell'altro (un concetto che ispirò anche i famosi cicli di luce del film Tron). Negli anni successivi, il gioco venne convertito per i primi personal computer e per le console domestiche sotto svariati nomi.
La vera consacrazione planetaria del gioco avvenne però nel 1997. Un ingegnere finlandese di nome Taneli Armanto ricevette l'incarico di programmare un giochino per il nuovo modello di telefono cellulare dell'azienda per cui lavorava: il Nokia 6110. L'apparecchio aveva uno schermo monocromatico verde piccolissimo, una memoria limitata a pochi kilobyte e una tastiera numerica rigida.
Armanto creò una variante minimalista di Snake che divenne un successo colossale senza precedenti. Milioni di persone in tutto il mondo trascorsero ore a premere i tasti 2 (su), 8 (giù), 4 (sinistra) e 6 (destra) per far crescere il proprio serpentino pixelato senza toccare i muri del piccolo display. Oggi, quel gioco è considerato una pietra miliare della storia dei videogiochi mobili, e tu stai per scriverne il motore software da zero!
Concept 1: Il motore segreto di ogni videogioco (Il Game Loop)
A differenza dei programmi scritti finora, che attendevano pazientemente che l'utente inserisse un testo con input() prima di procedere, i videogiochi sono programmi dinamici e vivi: le cose si muovono sullo schermo anche se il giocatore non tocca la tastiera!
Per ottenere questo effetto, ogni videogioco utilizza un ciclo infinito chiamato Game Loop. In ogni singolo fotogramma (frame), il computer esegue velocemente queste tre azioni:
┌───────────────────────┐
│ 1. LEGGI INPUT │ (Tastiera, mouse, gamepad)
└───────────┬───────────┘
▼
┌───────────────────────┐
│ 2. AGGIORNA STATO │ (Muovi personaggi, calcola vite)
└───────────┬───────────┘
▼
┌───────────────────────┐
│ 3. DISEGNA SCHERMO │ (Svuota schermo, disegna pixel)
└───────────┬───────────┘
▼
(Ripete da capo 60 volte al secondo)
Se il loop gira troppo lentamente, il gioco risulterà scattoso; se gira troppo velocemente, il gioco diventerà ingiocabile perché il serpente sfreccerà via a velocità folle. Per questo motivo, usiamo un orologio interno (pygame.time.Clock()) per limitare il ciclo a un numero di fotogrammi controllato, ad esempio 10 o 15 FPS.
Concept 2: La griglia di coordinate discreta
Nello spazio grafico tradizionale di Python, lo schermo è composto da singoli pixel (ad esempio 600x400). Tuttavia, se muovessimo lo Snake di un singolo pixel alla volta a ogni fotogramma, il movimento risulterebbe disallineato e le curve del corpo sarebbero difficili da calcolare.
Per questo motivo, dividiamo lo schermo in una griglia discreta di blocchi (es. 20x20 pixel ciascuno).
- La coordinata
(0, 0)è il primo blocco in alto a sinistra. - La coordinata
(20, 0)è il secondo blocco alla sua destra. - La coordinata
(0, 20)è il blocco sotto il primo.
Lo Snake e il cibo si muoveranno esclusivamente a scatti di 20 pixel alla volta. In questo modo, l'intero corpo dello Snake rimarrà sempre perfettamente allineato e le collisioni avverranno solo quando la testa si troverà esattamente sulla stessa identica coordinata (x, y) del cibo o di un ostacolo!
Installazione di pygame e passaggio a VS Code
Per creare giochi grafici interattivi con Pygame, Thonny potrebbe avere difficoltà a gestire la finestra grafica in tempo reale. Da questo capitolo in poi, ti consigliamo vivamente di passare all'uso di VS Code, come spiegato in dettaglio nell'Appendice A.
Installazione pygame:
pip install pygame
Costruiamo il Gioco: Versione Base
Ecco la versione base in cui un quadrato verde si sposta sullo schermo e può essere guidato con le frecce direzionali:
import pygame
import sys
# Inizializziamo Pygame
pygame.init()
# Setup dello schermo
schermo = pygame.display.set_mode((600, 400))
pygame.display.set_caption("Snake Base")
# Posizione iniziale dello Snake
x, y = 300, 200
velocita_x, velocita_y = 20, 0
orologio = pygame.time.Clock()
while True:
# 1. Rileviamo l'input (Eventi)
for evento in pygame.event.get():
if evento.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif evento.type == pygame.KEYDOWN:
if evento.key == pygame.K_UP:
velocita_x, velocita_y = 0, -20
elif evento.key == pygame.K_DOWN:
velocita_x, velocita_y = 0, 20
elif evento.key == pygame.K_LEFT:
velocita_x, velocita_y = -20, 0
elif evento.key == pygame.K_RIGHT:
velocita_x, velocita_y = 20, 0
# 2. Aggiorniamo lo stato
x += velocita_x
y += velocita_y
# 3. Disegniamo lo schermo
schermo.fill((0, 0, 0)) # Pulisce lo schermo colorandolo di nero
# Disegna lo Snake (quadrato verde)
pygame.draw.rect(schermo, (0, 255, 0), pygame.Rect(x, y, 20, 20))
pygame.display.flip() # Aggiorna la visualizzazione
orologio.tick(10) # Limita il gioco a 10 fotogrammi al secondo
Sfide: Fai salire di livello il tuo Snake
Sfida 1 — La coda dello Snake
Nella versione base lo Snake è composto da un unico quadrato. Trasforma lo Snake in una lista di coordinate [(x, y), (x-20, y), ...] e fai in modo che la coda si allunghi a ogni cibo mangiato.
Sfida 2 — Velocità progressiva
Rendi il gioco più stimolante incrementando il valore di FPS passato a orologio.tick() man mano che il punteggio del giocatore aumenta. Più punti fai, più lo Snake si muove velocemente!
Sfida 3 — Effetti Sonori Retro
Utilizza il modulo pygame.mixer per emettere suoni sintetizzati o caricare file audio corti a ogni cibo consumato e in caso di scontro finale.
Sfida 4 — Gli Ostacoli Fissi
Crea una lista di coordinate fisse che rappresentano ostacoli e muri sul campo di gioco. Se la testa dello Snake entra in collisione con uno di essi, il gioco termina immediatamente!
Il Codice Completo con Tutte le Sfide
Salva questo file all'interno di VS Code come snake_completo.py ed eseguilo dal terminale per sfidare i tuoi amici!
import pygame
import sys
import random
# Dimensioni dello schermo
LARGHEZZA = 600
ALTEZZA = 400
DIM_BLOCCO = 20
# Colori (RGB)
NERO = (0, 0, 0)
VERDE = (0, 255, 0)
ROSSO = (255, 0, 0)
GRIGIO = (128, 128, 128)
def genera_posizione_cibo(ostacoli, snake):
"""Genera una coordinata allineata alla griglia non occupata da snake o ostacoli."""
while True:
x = random.randint(0, (LARGHEZZA - DIM_BLOCCO) // DIM_BLOCCO) * DIM_BLOCCO
y = random.randint(0, (ALTEZZA - DIM_BLOCCO) // DIM_BLOCCO) * DIM_BLOCCO
pos = (x, y)
if pos not in snake and pos not in ostacoli:
return pos
def main():
# Inizializzazione di pygame
pygame.init()
# Finestra di gioco
schermo = pygame.display.set_mode((LARGHEZZA, ALTEZZA))
pygame.display.set_caption("🐍 Retro Snake Pro 🎮")
# Orologio per controllare il framerate
orologio = pygame.time.Clock()
# Stato iniziale dello Snake (lista di tuple coordinate)
snake = [(300, 200), (280, 200), (260, 200)]
direzione = pygame.K_RIGHT
# Ostacoli fissi (Sfida 4)
ostacoli = [(100, 100), (100, 120), (100, 140), (480, 260), (480, 280), (480, 300)]
# Cibo iniziale
cibo = genera_posizione_cibo(ostacoli, snake)
punteggio = 0
fps_base = 8
# Game Loop principale
while True:
# 1. Input del giocatore
for evento in pygame.event.get():
if evento.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif evento.type == pygame.KEYDOWN:
# Evitiamo di poter invertire la marcia direttamente su se stessi
if evento.key == pygame.K_UP and direzione != pygame.K_DOWN:
direzione = pygame.K_UP
elif evento.key == pygame.K_DOWN and direzione != pygame.K_UP:
direzione = pygame.K_DOWN
elif evento.key == pygame.K_LEFT and direzione != pygame.K_RIGHT:
direzione = pygame.K_LEFT
elif evento.key == pygame.K_RIGHT and direzione != pygame.K_LEFT:
direzione = pygame.K_RIGHT
# 2. Aggiornamento dello Stato
testa_x, testa_y = snake[0]
if direzione == pygame.K_UP:
nuova_testa = (testa_x, testa_y - DIM_BLOCCO)
elif direzione == pygame.K_DOWN:
nuova_testa = (testa_x, testa_y + DIM_BLOCCO)
elif direzione == pygame.K_LEFT:
nuova_testa = (testa_x - DIM_BLOCCO, testa_y)
elif direzione == pygame.K_RIGHT:
nuova_testa = (testa_x + DIM_BLOCCO, testa_y)
# Inseriamo la nuova testa dello Snake
snake.insert(0, nuova_testa)
# Rilevamento collisione con il cibo
if nuova_testa == cibo:
punteggio += 1
# Sfida 2: aumenta la velocità man mano che si mangia cibo
cibo = genera_posizione_cibo(ostacoli, snake)
else:
# Se non mangia, rimuoviamo l'ultimo pezzo della coda per avanzare
snake.pop()
# Controlliamo la collisione della testa con i bordi dello schermo,
# con la propria coda o con gli ostacoli
head = snake[0]
if (head[0] < 0 or head[0] >= LARGHEZZA or
head[1] < 0 or head[1] >= ALTEZZA or
head in snake[1:] or
head in ostacoli):
print(f"💀 GAME OVER! Punteggio finale: {punteggio}")
break
# 3. Disegno degli elementi grafici (Rendering)
schermo.fill(NERO)
# Disegniamo gli ostacoli
for ostacolo in ostacoli:
pygame.draw.rect(schermo, GRIGIO, pygame.Rect(ostacolo[0], ostacolo[1], DIM_BLOCCO, DIM_BLOCCO))
# Disegniamo lo Snake
for pezzo in snake:
pygame.draw.rect(schermo, VERDE, pygame.Rect(pezzo[0], pezzo[1], DIM_BLOCCO, DIM_BLOCCO))
# Disegniamo il cibo
pygame.draw.rect(schermo, ROSSO, pygame.Rect(cibo[0], cibo[1], DIM_BLOCCO, DIM_BLOCCO))
# Mostriamo il punteggio in tempo reale
pygame.display.set_caption(f"🐍 Retro Snake Pro | Punteggio: {punteggio} | Velocità: {fps_base + punteggio}")
# Aggiorna lo schermo
pygame.display.flip()
# Controllo della velocità (Sfida 2)
orologio.tick(fps_base + punteggio)
pygame.quit()
if __name__ == "__main__":
main()
Analisi del Codice Completo
Ecco come funziona la parte matematica e strutturale del codice:
1. La gestione del movimento della Coda
Ad ogni ciclo, calcoliamo dove dovrebbe trovarsi la nuova testa dello Snake e la inseriamo all'inizio della lista con snake.insert(0, nuova_testa).
- Se lo Snake ha mangiato il cibo, non rimuoviamo l'ultimo elemento. Lo Snake risulterà più lungo di un blocco!
- Se lo Snake non ha mangiato niente, eliminiamo l'ultimo pezzo della coda con
snake.pop(). Questo dà l'illusione ottica che il corpo si sia spostato in avanti.
2. Prevenire il suicidio immediato (Inversione di marcia)
Nello Snake originale, se ti sposti a destra non puoi premere sinistra per tornare indietro all'istante, altrimenti ti scontreresti con il tuo stesso collo. Nel codice questo controllo viene blindato all'interno della gestione input (es: if evento.key == pygame.K_UP and direzione != pygame.K_DOWN).
3. Il sistema di collisioni discrete
Verifichiamo se le coordinate (x, y) della testa coincidono con elementi sensibili. L'espressione head in snake[1:] controlla in una sola riga se la coordinata della testa appartiene al resto della lista del corpo dello Snake, gestendo l'auto-collisione.
Curiosità: Easter egg Nokia
Lo sapevi che la prima versione di Snake per Nokia conteneva un easter egg crittografico? Alcuni programmatori scrissero cheat code segreti che permettevano di rallentare il tempo o rendere lo snake immune alle pareti inserendo codici numerici durante l'avvio del telefono!
Provaci tu! — I bordi avvolgenti
Prova a modificare la logica di collisione con i bordi dello schermo. Fai in modo che, invece di morire, lo Snake che esce a destra ricompaia immediatamente sul bordo sinistro, e se esce in alto ricompaia in basso! (Suggerimento: usa l'operatore modulo % o dei semplici if prima di aggiornare le coordinate).
Il Piccolo Glossario
| Termine | Definizione | | --------- | ------------- | | pip (Package Installer for Python) | Strumento a riga di comando per scaricare e installare pacchetti e moduli aggiuntivi da internet. | | Event / Evento | Un'azione registrata dal sistema operativo, come la pressione di un tasto sulla tastiera o un clic del mouse. | | Framerate / FPS | Velocità con cui lo schermo viene aggiornato, espressa in fotogrammi al secondo. | | Game loop | Il ciclo di controllo principale del videogioco che elabora ciclicamente input, logica e grafica. | | pygame.display.flip() | Comando che aggiorna l'intero display del gioco per mostrare sullo schermo quanto disegnato in memoria. |
Prossimo capitolo
Capitolo 8: Pong: Due giocatori, un solo vincitore
OOP introduzione, classi Pallina e Racchetta, self, metodi update/draw, collisione fisica rimbalzo, punteggio, IA avversario base. Codice completo con 4 sfide: racchette in classe, velocità pallina, punteggio grafico, avversario AI.
Questo post è un estratto dal libro "Divento Programmatore: Python e i Giochi" (Volume 2 della collana "Cervello Elettronico") di Rocco Balzamà. Target: 10-14 anni · ~180 pagine · Thonny + VS Code.
Per approfondire
- Pygame per principianti: i primi giochi in Python
- Progetto finale in Python: dall'idea al programma
- Disegnare con Python: arte con il modulo turtle
- Corsi e percorsi di programmazione