- Home
- iThink
- Programmazione per Ragazzi
- Pygame per principianti: i primi giochi in Python
Pygame per principianti: i primi giochi in Python
Divento Programmatore — Capitolo 7: Pygame e i primi giochi
Finora tutto in console testuale. Ora la parte visiva: Pygame è la libreria standard per giochi 2D in Python. Semplice, potente, usata in produzione.
Cosa imparerai
| Concetto | Descrizione | | ---------- | ------------- | | pygame.init() | Inizializza tutti i sottosistemi | | display.set_mode() | Crea la finestra di gioco | | Event Loop | Gestisce input, chiusura, timer | | draw module | Rettangoli, cerchi, linee, poligoni | | key.get_pressed() | Input tastiera stato continuo | | mouse | Posizione, click, stati pulsanti | | Clock.tick(FPS) | Limita frame rate, delta time |
1. Installazione Pygame
# Terminale (Thonny: Tools → Manage packages → pygame)
pip install pygame
Verifica:
import pygame
print(pygame.version.ver) # es. 2.5.2
2. Struttura base: il Game Loop
Ogni gioco ha questo scheletro:
import pygame
import sys
# 1. INIZIALIZZAZIONE
pygame.init()
# Costanti schermo
LARGHEZZA, ALTEZZA = 800, 600
FPS = 60
TITOLO = "Il Mio Primo Gioco"
# 2. CREA FINESTRA
schermo = pygame.display.set_mode((LARGHEZZA, ALTEZZA))
pygame.display.set_caption(TITOLO)
# 3. CLOCK per controllo FPS
clock = pygame.time.Clock()
# 4. VARIABILI GIOCO (stato)
running = True
player_pos = [LARGHEZZA // 2, ALTEZZA // 2]
player_vel = 5
# 5. GAME LOOP PRINCIPALE
while running:
# --- A. GESTIONE EVENTI ---
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
# --- B. AGGIORNAMENTO STATO (LOGICA) ---
tasti = pygame.key.get_pressed()
if tasti[pygame.K_LEFT] or tasti[pygame.K_a]:
player_pos[0] -= player_vel
if tasti[pygame.K_RIGHT] or tasti[pygame.K_d]:
player_pos[0] += player_vel
if tasti[pygame.K_UP] or tasti[pygame.K_w]:
player_pos[1] -= player_vel
if tasti[pygame.K_DOWN] or tasti[pygame.K_s]:
player_pos[1] += player_vel
# Limiti schermo
player_pos[0] = max(0, min(LARGHEZZA, player_pos[0]))
player_pos[1] = max(0, min(ALTEZZA, player_pos[1]))
# --- C. DISEGNO (RENDER) ---
schermo.fill((30, 30, 40)) # Sfondo scuro (R, G, B)
# Giocatore: quadrato blu
pygame.draw.rect(schermo, (50, 150, 255),
(*player_pos, 40, 40)) # x, y, w, h
# Aggiorna display
pygame.display.flip() # o pygame.display.update()
# --- D. CONTROLLO FPS ---
clock.tick(FPS)
# 6. PULIZIA
pygame.quit()
sys.exit()
3. Analisi componenti chiave
pygame.init()
Inizializza: display, audio, joystick, font, timer. Obbligatorio prima di tutto.
pygame.display.set_mode((w, h))
Crea Surface principale (la finestra). Restituisce oggetto Surface su cui disegnare.
| Flag opzionale | Uso | | ---------------- | ----- | | pygame.FULLSCREEN | Schermo intero | | pygame.RESIZABLE | Finestra ridimensionabile | | pygame.NOFRAME | Senza bordi | | pygame.DOUBLEBUF | Double buffering (default) | | pygame.OPENGL | Per rendering 3D/OpenGL |
Event Loop: pygame.event.get()
Restituisce lista eventi dall'ultimo frame. Tipi principali:
| Evento | Attributi utili | Quando scatta | | -------- | ----------------- | --------------- | | QUIT | - | Clicca X chiudi finestra | | KEYDOWN | key, mod, unicode | Tasto premuto | | KEYUP | key, mod | Tasto rilasciato | | MOUSEMOTION | pos, rel, buttons | Mouse mosso | | MOUSEBUTTONDOWN | pos, button | Click premuto | | MOUSEBUTTONUP | pos, button | Click rilasciato | | MOUSEWHEEL | x, y, flipped | Rotella | | VIDEORESIZE | size, w, h | Finestra ridimensionata |
Tasti comuni: K_ESCAPE, K_SPACE, K_RETURN, K_LEFT/RIGHT/UP/DOWN, K_a...K_z, K_0...K_9, K_F1...K_F12, K_LSHIFT, K_RCTRL, ecc.
pygame.key.get_pressed() vs KEYDOWN
# get_pressed(): STATO ATTUALE (continuo, per movimento)
tasti = pygame.key.get_pressed()
if tasti[pygame.K_LEFT]:
x -= 5 # Si muove finché tieni premuto
# KEYDOWN: EVENTO DISCRETO (singolo, per azioni)
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
spara() # Una volta per premuta
Mouse
pos = pygame.mouse.get_pos() # (x, y) posizione
pressed = pygame.mouse.get_pressed() # (sinistro, centrale, destro) bool
rel = pygame.mouse.get_rel() # Movimento relativo dall'ultimo frame
# Nascondi cursore / cattura mouse
pygame.mouse.set_visible(False)
pygame.event.set_grab(True) # Mouse bloccato in finestra
Disegno: pygame.draw
Tutte le funzioni prendono: surface, color, geometria, width=0 (0 = riempito).
# Colori: tuple (R, G, B) 0-255
ROSSO = (255, 0, 0)
VERDE = (0, 255, 0)
BLU = (0, 0, 255)
BIANCO = (255, 255, 255)
NERO = (0, 0, 0)
GIALLO = (255, 255, 0)
TRASPARENTE = (0, 0, 0, 0) # Con SRCALPHA
# Rettangolo
pygame.draw.rect(surf, colore, (x, y, w, h), width=0, border_radius=0)
# Cerchio
pygame.draw.circle(surf, colore, (cx, cy), raggio, width=0)
# Ellisse (in rettangolo)
pygame.draw.ellipse(surf, colore, (x, y, w, h), width=0)
# Linea
pygame.draw.line(surf, colore, (x1, y1), (x2, y2), width=1)
# Linee multiple (polilinea)
pygame.draw.lines(surf, colore, closed, punti_list, width=1)
# Poligono (riempito)
pygame.draw.polygon(surf, colore, [(x1,y1), (x2,y2), ...], width=0)
# Arco (parte di ellisse)
pygame.draw.arc(surf, colore, (x, y, w, h), ang_inizio, ang_fine, width=1)
# Anti-aliased (più liscio, solo width=1)
pygame.draw.aaline(surf, colore, (x1,y1), (x2,y2))
pygame.draw.aalines(surf, colore, closed, punti, width=1)
Clock e Delta Time
clock = pygame.time.Clock()
# Nel loop:
dt = clock.tick(60) / 1000.0 # Delta time in SECONDI (float)
# Movimento frame-rate independent
player_pos[0] += velocita * dt # pixel al secondo, non al frame
Perché dt?: Se il gioco gira a 30 FPS vs 60 FPS, senza dt il movimento è dimezzato. Con dt è identico.
4. Template completo riutilizzabile
# template_gioco.py
"""Template base per giochi Pygame."""
import pygame
import sys
from typing import Tuple
class Gioco:
def __init__(self, larghezza: int = 800, altezza: int = 600,
titolo: str = "Gioco", fps: int = 60):
pygame.init()
self.larghezza = larghezza
self.altezza = altezza
self.fps = fps
self.schermo = pygame.display.set_mode((larghezza, altezza))
pygame.display.set_caption(titolo)
self.clock = pygame.time.Clock()
self.running = True
self.dt = 0.0
# Stato gioco (override in sottoclasse)
self.inizializza()
def inizializza(self):
"""Override: setup variabili, carica risorse."""
pass
def gestisci_eventi(self):
"""Override: processa input."""
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
self.running = False
def aggiorna(self, dt: float):
"""Override: logica gioco (fisica, IA, ecc.)."""
pass
def disegna(self, schermo: pygame.Surface):
"""Override: rendering."""
schermo.fill((30, 30, 40))
def run(self):
"""Game loop principale."""
while self.running:
self.dt = self.clock.tick(self.fps) / 1000.0
self.gestisci_eventi()
self.aggiorna(self.dt)
self.disegna(self.schermo)
pygame.display.flip()
pygame.quit()
sys.exit()
# Esempio uso
if __name__ == "__main__":
class MioGioco(Gioco):
def inizializza(self):
self.player = pygame.Rect(380, 280, 40, 40)
self.vel = 300 # pixel/secondo
def aggiorna(self, dt):
tasti = pygame.key.get_pressed()
if tasti[pygame.K_LEFT] or tasti[pygame.K_a]:
self.player.x -= self.vel * dt
if tasti[pygame.K_RIGHT] or tasti[pygame.K_d]:
self.player.x += self.vel * dt
if tasti[pygame.K_UP] or tasti[pygame.K_w]:
self.player.y -= self.vel * dt
if tasti[pygame.K_DOWN] or tasti[pygame.K_s]:
self.player.y += self.vel * dt
# Clamp a schermo
self.player.clamp_ip(self.schermo.get_rect())
def disegna(self, schermo):
schermo.fill((20, 20, 30))
pygame.draw.rect(schermo, (60, 180, 255), self.player, border_radius=8)
# HUD debug
font = pygame.font.Font(None, 24)
fps_text = font.render(f"FPS: {self.clock.get_fps():.0f}", True, (200, 200, 200))
schermo.blit(fps_text, (10, 10))
gioco = MioGioco()
gioco.run()
5. Testo e Font
# Font di sistema (None = default)
font = pygame.font.Font(None, 32) # Dimensione 32
font = pygame.font.SysFont("Arial", 24) # Nome font specifico
# Render testo → Surface
testo_surf = font.render("Ciao Pygame!", True, (255, 255, 255))
# antialias colore
# Posiziona e disegna
rect = testo_surf.get_rect(center=(400, 300))
schermo.blit(testo_surf, rect)
6. Immagini e Sprite (anticipazione Cap. 8)
# Carica immagine (PNG con trasparenza consigliato)
img = pygame.image.load("player.png").convert_alpha()
# Scala
img = pygame.transform.scale(img, (64, 64))
# Ruota
img = pygame.transform.rotate(img, 45)
# Ribalta
img = pygame.transform.flip(img, True, False)
# Disegna (blit)
schermo.blit(img, (x, y)) # Angolo sup-sx
# Centrato
rect = img.get_rect(center=(x, y))
schermo.blit(img, rect)
7. Suoni (anticipazione)
# Inizializza mixer (già in pygame.init())
suono = pygame.mixer.Sound("jump.wav")
suono.play()
# Musica di sottofondo (streaming, una alla volta)
pygame.mixer.music.load("theme.ogg")
pygame.mixer.music.play(-1) # -1 = loop infinito
pygame.mixer.music.set_volume(0.5)
8. Script completo: "Evita i quadrati rossi"
# evita_quadrati.py
"""Mini-gioco: muovi quadrato blu, evita rossi che cadono."""
import pygame
import random
import sys
LAR, ALT = 800, 600
FPS = 60
class Giocatore:
def __init__(self):
self.rect = pygame.Rect(LAR//2 - 20, ALT - 60, 40, 40)
self.vel = 400
self.colore = (50, 150, 255)
def aggiorna(self, dt, tasti):
if tasti[pygame.K_LEFT] or tasti[pygame.K_a]:
self.rect.x -= self.vel * dt
if tasti[pygame.K_RIGHT] or tasti[pygame.K_d]:
self.rect.x += self.vel * dt
self.rect.clamp_ip(pygame.Rect(0, 0, LAR, ALT))
def disegna(self, surf):
pygame.draw.rect(surf, self.colore, self.rect, border_radius=8)
class Nemico:
def __init__(self):
self.rect = pygame.Rect(random.randint(0, LAR-30), -30, 30, 30)
self.vel = random.uniform(150, 300)
self.colore = (255, 60, 60)
def aggiorna(self, dt):
self.rect.y += self.vel * dt
def disegna(self, surf):
pygame.draw.rect(surf, self.colore, self.rect)
def fuori_schermo(self):
return self.rect.top > ALT
def main():
pygame.init()
schermo = pygame.display.set_mode((LAR, ALT))
pygame.display.set_caption("Evita i Quadrati Rossi!")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 36)
giocatore = Giocatore()
nemici = []
timer_spawn = 0
punteggio = 0
running = True
game_over = False
while running:
dt = clock.tick(FPS) / 1000.0
# Eventi
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
if game_over and event.key == pygame.K_SPACE:
# Riavvia
return main()
if not game_over:
tasti = pygame.key.get_pressed()
giocatore.aggiorna(dt, tasti)
# Spawn nemici
timer_spawn += dt
if timer_spawn > 0.5:
nemici.append(Nemico())
timer_spawn = 0
# Aggiorna nemici
for nem in nemici[:]:
nem.aggiorna(dt)
if nem.fuori_schermo():
nemici.remove(nem)
punteggio += 10
elif giocatore.rect.colliderect(nem.rect):
game_over = True
# Disegno
schermo.fill((25, 25, 35))
giocatore.disegna(schermo)
for nem in nemici:
nem.disegna(schermo)
# HUD
score_surf = font.render(f"Punteggio: {punteggio}", True, (220, 220, 220))
schermo.blit(score_surf, (15, 15))
if game_over:
over_surf = font.render("GAME OVER - SPAZIO per rigiocare", True, (255, 100, 100))
rect = over_surf.get_rect(center=(LAR//2, ALT//2))
schermo.blit(over_surf, rect)
pygame.display.flip()
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
9. Tabella riassuntiva: cheat sheet Pygame base
| Operazione | Codice | | ------------ | -------- | | Inizializza | pygame.init() | | Finestra | pygame.display.set_mode((w,h)) | | Titolo | pygame.display.set_caption("Titolo") | | Colore sfondo | schermo.fill((R,G,B)) | | Event loop | for e in pygame.event.get(): | | Esci su X | if e.type == pygame.QUIT: | | Tasto premuto (evento) | e.type == KEYDOWN and e.key == K_SPACE | | Tasto tenuto (stato) | pygame.key.get_pressed()[K_LEFT] | | Mouse pos | pygame.mouse.get_pos() | | Mouse click | pygame.mouse.get_pressed()[0] | | Rettangolo | pygame.draw.rect(surf, col, (x,y,w,h)) | | Cerchio | pygame.draw.circle(surf, col, (cx,cy), r) | | Linea | pygame.draw.line(surf, col, (x1,y1), (x2,y2), w) | | Testo | font.render("txt", True, col) → blit | | Limita FPS | clock.tick(60) | | Delta time (s) | clock.tick(60)/1000.0 | | Aggiorna schermo | pygame.display.flip() | | Esci pulito | pygame.quit(); sys.exit() |
10. Esercizi pratici
- Pong: Due paddle (W/S e ↑/↓), palla che rimbalza, punteggio
- Disegna liberamente: Click sinistro = disegna cerchio, destro = cancella, rotella = cambia colore
- Schiva pioggia: Gocce cadono random, giocatore si muove mouse, conta quante eviti in 30s
- Template esteso: Aggiungi a
Giococlassi:pause(),menu_principale(),schermata_game_over() - Input manager: Crea classe
Inputche astrae tastiera+mouse+gamepad con azioni nominate ("muovi_sx","salta")
11. Checklist fine capitolo
- [ ] Installa
pygamee verifica versione - [ ] Crea finestra con
set_mode(), imposta titolo - [ ] Scrivi game loop: eventi → aggiorna → disegna → flip → tick
- [ ] Usi
pygame.event.get()per QUIT, KEYDOWN, MOUSE - [ ] Distingui
key.get_pressed()(movimento) vsKEYDOWN(azioni) - [ ] Disegni:
rect,circle,line,polygoncon colori RGB - [ ] Usi
Clock.tick(FPS)edtper movimento frame-independent - [ ] Carichi font e renderizzi testo/HUD
Prossimo capitolo
Capitolo 8: Progetto finale — Crea il tuo gioco completo — Da zero a gioco giocabile: pianifica (Game Design Document), classi base (Player, Enemy), game loop, collision detection, score, game over, packaging (.exe, .app).
Serie "Divento Programmatore: Python e i Giochi" (Cervello Elettronico Vol.2). Codice completo, tabelle, esercizi. Target 10-14 anni + guide adulti.
Per approfondire
- Disegnare con Python: arte con il modulo turtle
- Snake in Python con Pygame, passo per passo
- Moduli e librerie standard di Python
- Corsi e percorsi di programmazione