1. Home
  2. iThink
  3. Programmazione per Ragazzi
  4. Pygame per principianti: i primi giochi in Python
Programmazione per Ragazzi Divento Programmatore · Capitolo 7

Pygame per principianti: i primi giochi in Python

Codice Python sullo schermo di un computer
Foto: JohnsonMartin · Pixabay · Licenza dei contenuti Pixabay

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

  1. Pong: Due paddle (W/S e ↑/↓), palla che rimbalza, punteggio
  2. Disegna liberamente: Click sinistro = disegna cerchio, destro = cancella, rotella = cambia colore
  3. Schiva pioggia: Gocce cadono random, giocatore si muove mouse, conta quante eviti in 30s
  4. Template esteso: Aggiungi a Gioco classi: pause(), menu_principale(), schermata_game_over()
  5. Input manager: Crea classe Input che astrae tastiera+mouse+gamepad con azioni nominate ("muovi_sx", "salta")

11. Checklist fine capitolo

  • [ ] Installa pygame e 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) vs KEYDOWN (azioni)
  • [ ] Disegni: rect, circle, line, polygon con colori RGB
  • [ ] Usi Clock.tick(FPS) e dt per 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).

👉 Leggi il Capitolo 8 →


Serie "Divento Programmatore: Python e i Giochi" (Cervello Elettronico Vol.2). Codice completo, tabelle, esercizi. Target 10-14 anni + guide adulti.

Per approfondire

Fonti e riferimenti

Condividi LinkedIn Facebook X WhatsApp
Continua a leggere

Tutti gli articoli del blog

Strumenti gratuiti