5: L'OGGETTO Rect

COS'E' UN Rect

Un oggetto che viene spesso usato insieme alla Surface è il Rect (rettangolo). Mentre la Surface è una porzione fisica di schermo sulla quale si può disegnare, il Rect è un oggetto geometrico astratto, caratterizzato da una posizione e dalla lunghezza dei suoi lati. Per creare un Rect basta usare la funzione costruttore Rect(), che ha lo stesso nome dell'oggetto. Ne esistono due versioni:

r = pygame.Rect(10, 10, 50, 80) Prende come parametri 4 numeri interi: le coordinate x, y dell'angolo in alto a sinistra, la larghezza e l'altezza
r = pygame.Rect((10, 10), (50, 80)) Gli stessi parametri, ma sotto forma di due coppie di numeri

Perchè i Rect sono così usati? Se avete provato a svolgere l'Esercizio 4.4 della lezione precedente vi sarete sicuramente annoiati nel fare parecchi calcoli banali con le coordinate (sommare la x del lato sinistro con la larghezza per trovare la x del lato destro ...). Il Rect rende immediatamente disponibili tutti questi calcoli sotto forma di attributi: se r è una variabile di tipo Rect possiamo scrivere:

Attributo Tipo di dato Significato
r.left oppure r.x numero intero la x del lato sinistro
r.right numero intero la x del lato destro
r.top oppure r.y numero intero la y del lato superiore
r.bottom numero intero la y del lato inferiore
r.centerx numero intero la x del centro del rettangolo
r.centery numero intero la y del centro del rettangolo
r.topleft coppia di interi le coordinate dell'angolo in alto a sinistra
r.bottomright coppia di interi le coordinate dell'angolo in basso a destra
r.center coppia di interi le coordinate del centro del rettangolo
r.midtop coppia di interi le coordinate del punto al centro del lato superiore

e ve ne sono altri ancora che potete trovare nella documentazione dell'oggetto Rect: https://www.pygame.org/docs/ref/rect.html (sono elencati in un riquadro giallo dopo l'elenco delle funzioni: dovrebbe essere abbastanza facile capire il loro significato).

Ecco un semplice esempio che potete digitare direttamente su IDLE. Il costruttore crea un rettangolo di 200x100 pixel posizionato in (10, 20):


>>> import pygame
   .   .   .
>>> r = pygame.Rect(10, 20, 200, 100)
>>> r.left
10

>>> r.right      
210

>>> r.topleft    
(10, 20)

>>> r.bottomright
(210, 120)       
ESERCIZIO 5.1: Digitate l'esempio precedente e continuate a chiedere a pygame il valore di qualche altro attributo di r.

Questi attributi possono essere sia letti che scritti: cambiando il valore di uno di essi tutti gli altri si modificheranno di conseguenza. Ad esempio se vogliamo spostare il Rect 10 pixel a destra ci basterà scrivere


r.left += 10
ma potremmo ottenere lo stesso risultato con r.right += 10 o r.centerx += 10, e tutti gli altri attributi saranno aggiornati per rispecchiare la nuova posizione.

Un'altra caratteristica molto utile del Rect è che l'oggetto ha anche dei metodi per verificare facilmente le collisioni (cosa molto utile in un videogioco). Ad esempio, se (x, y) sono le coordinate di un punto e r è un Rect, il metodo r.collidepoint(x, y) restituirà True se il punto è interno al rettangolo, False altrimenti. Se invece r ed s sono due Rect, r.colliderect(s) restituirà True se i due rettangoli si intersecano.

ESERCIZIO 5.2: Possiamo fare un po' di esperimenti in maniera interattiva direttamente da IDLE, senza bisogno di programmare. Seguite queste istruzioni: SOLUZIONI

COME POSIZIONARE UNA Surface SULLO SCHERMO

Una tecnica molto usata in pygame è quella di associare ad una Surface un Rect con le stesse dimensioni e di usare poi il Rect per posizionare la superficie o per verificare se un evento avviene all'interno di essa. Vediamo come usare questa tecnica per posizionare facilmente le Surface (e prossimamente le immagini) sullo schermo.

Ritorniamo al metodo blit(), che, come abbiamo visto qui, serve a disegnare una Surface all'interno di un'altra. Il metodo è piuttosto flessibile: se lo cerchiamo nella documentazione ufficiale trovamo:

blit(source, dest, area=None, special_flags=0)->Rect

Spieghiamo dettagliatamente il significato dei vari parametri:

Parametro Significato
source è la Surface da copiare (ricordo che la Surface destinazione è a sinistra del punto)
dest è il punto della Surface destinazione in cui dobbiamo copiare source. Può essere una dupla (x, y) (le coordinate del punto in cui copieremo l'angolo in alto a sinistra di source) oppure può essere un Rect: in questo caso source sarà copiata a partire dal topleft (l'angolo in alto a sinistra) del Rect
area=None è un parametro opzionale che dovete usare solo se non volete copiare tutta la Surface source. In questo caso potete usare un altro Rect per indicare quale parte della Surface copiare (lo utilizzeremo più avanti)
special_flags=0 anche questo parametro è opzionale e permette di ottenere vari effetti grafici come una semitrasparenza. In questo tutorial non lo useremo.
Rect ora possiamo dare un significato anche al valore restituito: è un oggetto Rect che rappresenta l'area della Surface destinazione effettivamente modificata dalla funzione (in genere non ci sarà bisogno di utilizzare questo valore)

Nella blit() useremo quasi sempre la forma in cui il secondo parametro è un Rect con le stesse dimensioni della Surface da copiare. Per capire le potenzialità di questa tecnica vediamo un esempio: questo programma crea due quadrati e li dispone affiancati sullo schermo.

NUOVO PROGRAMMA: due_quadrati.py

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))

# creiamo la Surface surf1, la coloriamo di rosso e la posizioniamo sullo schermo
surf1 = pygame.Surface((200, 200))
surf1.fill("red")
rect1 = pygame.Rect(50, 50, 200, 200)
screen.blit(surf1, rect1)

# stessa cosa con surf2 (blu)
surf2 = pygame.Surface((100, 100))
surf2.fill("blue")
rect2 = pygame.Rect(0, 0, 100, 100)
rect2.topleft = rect1.topright
screen.blit(surf2, rect2)

# aggiorniamo lo schermo
pygame.display.flip()

# terminiamo correttamente il programma
input("Premi INVIO per terminare il programma")
pygame.quit()

Esaminiamo con attenzione il programma. Nella riga #9, dopo aver creato la Surface surf1 e averla colorata di rosso, creiamo il Rect rect1 con le stesse dimensioni e l'angolo in alto a sinistra in (50, 50); nella #10 usiamo la blit() dando come secondo parametro rect1 (quindi surf1 sarà copiata a partire proprio da (50, 50)). Nelle righe successive facciamo lo stesso con surf2 (blu) e rect2, ma ora osserviamo la riga #16: cambiamo il valore dell'attributo rect2.topleft ponendolo uguale a rect1.topright: questo vuol dire che l'angolo in alto a sinistra del quadrato blu sarà allineato all'angolo in alto a destra di quello rosso: abbiamo affiancato i due quadrati senza dover fare nessun calcolo con le coordinate! Nella riga #17 usiamo di nuovo la blit() con rect2 come secondo parametro.

Quindi possiamo riassumere la nostra tecnica per posizionare facilmente le Surface così:

C'è infine un'ulteriore comodità: anzichè creare i rettangoli con il costruttore (facendo attenzione ad indicare esattamente le stesse dimensioni della Surface, con il rischio di sbagliare), possiamo farli creare direttamente alla Surface con un metodo apposito. Il metodo get_rect() dell'oggetto Surface restituisce un Rect con le stesse dimensioni della Surface posizionato in (0, 0). Questo rende facile e sicuro ottenere il rettangolo corretto.

ESERCIZIO 5.3: Sostituiamo alla riga #9 del programma precedente le due che seguono: nella prima otteniamo automaticamente da surf1 un rettangolo delle giuste dimensioni (ma posizionato in (0, 0)) e nella seconda lo posizioniamo in (50, 50). Sostituite anche la riga #15 allo stesso modo.

.   .   .
rect1 = surf1.get_rect()
rect1.topleft = (50, 50)
.   .   .
ESERCIZIO 5.4: Mantenendo invariata la posizione del quadrato rosso, posizionate quello blu come nelle figure qui sotto; basta cambiare la riga #16 per allineare il secondo quadrato come vogliamo (ripassate tutti gli attributi di un Rect nella tabella all'inizio).
Immagine quadrati
Attenzione! Nel caso di figure sovrapposte diventa importante l'ordine in cui le disegnate. Nel terzo e nel quarto caso, se disegnaste prima il quadrato blu e poi quello rosso, il quadrato blu verrebbe sovrascritto e non si vedrebbe (provate!).
ESERCIZIO 5.5: Notiamo ora il vantaggio di usare la funzione get_rect(): proviamo a modificare le dimensioni delle due Surface (possiamo anche farle diventare dei rettangoli) e notiamo che tutto va a posto automaticamente, senza bisogno di modificare anche i Rect. Proviamo anche a modificare la posizione di rect1 e di nuovo il programma riposiziona tutto automaticamente.
SOLUZIONI

Fine della lezione