Introduzione
Per cominciare analizziamo le tre funzioni più semplici che compongono il programma. Vi ricordo solo che agg_rect() è stata creata per sopperire al mancato funzionamento di Rect.move_ip() con i numeri float.
Quando si crea un videogioco, ma non solo, dovete scegliere bene quali sono le routine che possono essere automatizzate e come, creando quindi delle funzioni ad hoc. Molto spesso le soluzioni che si trovano possono essere scritte in diverso modo e anche se il risultato è lo stesso, ne potete guadagnare sotto vari aspetti: come rapidità, facilità di lettura o adattamento a situazioni diverse ecc… Detto questo, passiamo al codice.
Codice
def carica_imm_sprite(nome,h,w,num): immagini = [] if num is None or num == 1: imm1 = pygame.image.load(nome+".png").convert_alpha() imm1_w, imm1_h = imm1.get_size() for y in range(int(imm1_h/h)): for x in range(int(imm1_w/w)): immagini.append(imm1.subsurface((x*w,y*h,w,h))) return immagini else: for x in range(1,num): imm1 = pygame.image.load(nome+str(x)+".png").convert_alpha() immagini.append(imm1) return immagini def agg_rect(sprite): x,y = sprite.coordinate s,t,o,p = sprite.rect sprite.rect.move_ip(int(x)-s,int(y)-t) def tps(orologio, fps): temp = orologio.tick(fps) tps = temp /1000. return tps
Per utilizzare la vostra sprite animata dovete caricare prima di tutto i frame dell’animazione. Come potete notare la funzione carica_imm_sprite() necessita di quattro parametri:
- Il nome del file, senza estensione ‘.png’.
- L’altezza della nostra sprite, non quella del file immagine.
- La larghezza della sprite.
- Numero di immagini che formano la sprite (per numero di immagini si intende quanti file immagini ci sono per questa sprite e naturalmente ogni file immagine si presume sia un frame).
L’obbiettivo è quello di passare una lista di immagini che rappresenti i frame di animazione della nostra sprite, quindi per prima cosa creiamo l’oggetto che ritorneremo al chiamante (piccola nota: in questo caso il passaggio avviene per valore, visto che non si può passare per riferimento in python), come possiamo vedere dalla riga 2.
La condizione successiva serve per individuare un caricamento da più file o da uno soltanto. Se il file è singolo, allora carichiamo l’immagine e la convertiamo senza perdere il canale alpha, la memorizziamo in imm1 e poi salviamo anche le sue grandezze (imm1_w, imm1_h). Con i due for alle righe 7 e 8, dividiamo l’immagine in frame, come se fosse una matrice: quindi il primo frame sarà quello ritagliato dall’angolo in alto a sinistra dell’immagine, mentre l’ultimo quello ritagliato dall’angolo in basso a destra.
Come potete vedere, divindendo la lunghezza dell’immagine per la larghezza del frame, abbiamo il numero di frame su una linea. Quindi per avere le coordinate dell’inizio del frame basta moltiplicare le x (cioè il frame che stiamo memorizzando) per la lunghezza ‘w’. Facciamo la stessa cosa per il numero delle colonne. Con questi dati, ritagliamo dall’immagine originale una sottosuperfice, che creerà l’immagine vera e proria del frame. Memorizziamo il tutto nella lista di immagini creata all’inizio con il seguente comando:
immagini.append(imm1.subsurface((x*w,y*h,w,h)))
Da notare che subsurface ha bisogno di quattro elementi (perché ha bisogno di un oggetto Rect e si può creare anche in questo modo): i primi due indicano le coordinate iniziali dell’immagine da ritagliare (ricordate che si parte dall’angolo in alto a sinistra), mentre le ultime due sono la larghezza e l’altezza del frame da “tagliare”.
Naturalmente se abbiamo dei file separati la cosa è molto più semplice e di questo se ne occupano le righe che vanno dalla 12 alla 16. Si presume che i file abbiamo lo stesso nome e differiscano per un numero alla fine.
La funzione termia ritornando la lista di immagini, che possiamo prontamente riutilizzare nelle nostre sprites e vedremo più avanti come. Vi premetto che più frame caricate, più l’animazione sarà fluida e dettagliata.
Passiamo ora alla funzione agg_rect(), che prende come argomento una sprite. Il compito principale di questa parte di codice è quello di aggiornare le coordinate rect (coordinate necessarie per le collisioni) di una qualsiasi sprite. L’aggiornamento viene effettuato tramite la funzione move_ip(x,y), che fa parte di uno dei metodi di rect.
Quest’ultima funzione prende come argomenti gli spostamenti x e y da effettuare dalla posizione originale, non le nuove coordinate dove ridisegnare rect, il nostro rettangolo delle collisioni. Per questo memorizzo le coordinate attuali della sprite (che sono aggiornate in base alla posizione sullo schermo) e quelle di rect, converto poi i valori in interi (perché non posso lavorare con i float, per il bug citato nell’articolo precendente) e sottraggo le coordinate di rect (che sono temporalmente più vecchie). Così rect riceverà il giusto spostamento per restare incollato alla nostra sprite, in modo abbastanza realistico e non necessitiamo di ridisegnare un nuovo rect ogni volta che spostiamo il nostro personaggio.
Non era strettamente necessario utilizzare una funzione esterna, ma non potendo utilizzare direttamente le coordinate dell’immagine ho preferito crearmene una che può essere utilizzata anche in altre occasioni, visto che necessita solo il passaggio di una sprite che ha memorizzate le sue coordinate e rect.
Per concludere l’ultima funzione gestisce il tempo di gioco, nel senso che restituisce il tempo trascorso in secondi, in base all’orogolio che abbiamo creato per il programma e agli fps da raggiungere (tramite la funzione tick di un oggetto pygame.time.Clock). Come vedete, niente di complicato, ma di fondamentale importanza per sincronizzare spostamenti e animazioni.
Conclusioni
Spero di non aver creato dubbi, ma di aver contribuito, anche se in misera parte, ad uno dei vostri progetti. Infatti l’utilizzo di queste funzioni è di fondamentale importanza e sono le basi per l’utilizzo delle sprite (o almeno, per il caricamento dei loro frame). Successivamente analizzeremo le due classi, per poi passare al corpo del gioco. Alla fine di tutto questo avremo qualcosa di veramente consistente, che ci sonsentirà di sperimentare e affinare le nostre idee e il nostro videogioco.