Tag: Pygame

PyGame Sprite 5

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale.

def notrun:

Quando richiamiamo il fine gioco passiamo lo schermo e le coordinate per il testo “Game Over” che apparirà al centro. Quindi non facciamo niente altro che stampare a video fine_gioco (che contiente la stringa “Game Over” con il font da noi scelto, vedremo come più avanti) e aggiornare lo schermo (dalla linea 16 alla 18).

In questo loop controlliamo anche alcuni eventi da tastiera, ovvero Esc e Alt+F4 per uscire, mentre il tasto Invio servirà per ricominciare il gioco. Da notare che per prima cosa si chiam pygame.quit(), perché tutti i moduli devono essere cancellati e poi si richiama run().

def run:

Per prima cosa si inizializza il mixer e la libreria (linea 22 e 23).
Configuriamo il display (linea 25 e 27).
Alla linea 29 e 30 carichiamo il nostro sfondo e lo convertiamo alla risoluzione dello schermo che abbiamo scelto (in questo caso era necessario perché lo sfondo con l’erba è molto più grande dello schermo creato).

Carichiamo la musica alla linea 32.
Dalla linea 34 alla 41 creiamo le sprites delle bombe e le raggruppriamo in un gruppo sprites. Questo ci consentirà di gestirle al meglio tutte insieme.
Creiamo il nostro personaggio alla linea 43.
Alla linea 45 impostiamo la possibilità di ripetizione dei tasti. Di default non è inizializzata con pygame, quindi bisogna impostare il giusto intervallo per la ripetizione di un singolo tasto. Questo è fondamentale se vogliamo che, tenendo premuto per esempio la freccia destra, il nostro gestore degli eventi non la percepisca come un unica pressione, ma continui a “sentire” l’evento come una ripetizione. In questo modo se teniamo premuto il tasto, il nostro personaggio continuerà a camminare in quella direzione, perché non è arrivato un solo evento di spostamento a destra, ma tanti a seconda dell’intervallo di tempo scelto e dal ritardo rispetto alla prima pressione: pygame.key.set_repeat( ritardo, intervallo). Se la funzione è chiamata senza argomenti, disabilita la ripetizione dei tasti.

Di seguito, dalla linea 47 alla 53, dichiariamo delle variabili globali, che possono essere raggiunte da qualsiasi punto del nostro codice (è una pura comodità).

Carichiamo poi i sounds che riguardano il vento e la pioggia e poi riserviamo due canali per essi (dalla linea 55 alla 59).
Settiamo la velocità di gioco, gli fps che vogliamo raggiungere e creiamo l’orologio di gioco (dalla linea 61 alla 63).
Alla linea 65 memorizziamo un font che scegliamo tra quelli di sistema. Per conoscere i fonts disponibili nel proprio sistema basta utilizzare la funzione pygame.font.get_fonts(), che ritorna una lista di stringhe con i nomi dei fonts utilizzabili. Nel nostro caso, creando un font da quelli di sistema, possiamo passare come nome anche una lista di fonts, cioè una stringa dove essi sono separati da una virgola, per dare al programma più scelte nel caso un font non sia reperibile. L’altro parametro passato è la grandezza.
fine_gioco invece è la nostra stringa renderizzata con il font che abbiamo scelto. In questo caso abbiamo scelto come testo “GAME OVER”, abbiamo settato a True l’antialias sui caratteri della stringa e abbiamo scelto il colore verde (0,255,0). C’è la possibilità di decidere anche il colore dello sfondo, ma se non passato, sarà renderizzato utilizzando il canale alpha e quindi lasciando la trasparenza per visualizzare il background.

Dalla linea 68 inizia il loop principale e subito dopo il controllo degli eventi. Oltre ai soliti tasti per l’uscita del programma, controlliamo le frecce direzionali per far muovere il nostro personaggio. Il tasto ‘M’ si oocuperà di avviare o spegnere la musica si sottofondo, mentre il tasto ‘Spazio’ avvierà i rumori di ambiente. Se premiamo il tasto ‘Invio’, vedremo tutte le sprites delle bombe esplodere.

Dalla riga 122 alla 130 renderizziamo il gioco a video. Quindi teniamo conto del tempo passato, aggiorniamo lo schermo ad un colore unitario prima di stampare altre immagini a video, renderizziamo lo sfondo e successivamente il personaggio. Infine aggiorniamo le esplosioni, che non devono essere visualizzate se non accadono certi eventi, per questo passiamo None alla funzione Update insieme al tempo. Per concludere aggiorniamo lo schermo.

Qui e possibile scaricare il codice completo del programma.

PyGame Sprite 4

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale

Classe oggetto_sprite : def _init_

Per creare il nostro personaggio, come per l’altra classe, abbiamo bisogno del nome (nome) del file da caricare, l’altezza (altezza), la larghezza (larghezza), le coordinate (xy), lo schermo dove renderizzare (screen), il numero di immagini “num” (se necessario) e il gruppo (gruppo). Il gruppo rappresenta tutte le altre sprite al di fuori del personaggio. Vengono memorizzate per definire le collisioni con esse e le eventuali azioni da compiere. Vedremo successivamente come. Da notare che l’oggetto rect in questo caso è pari all’immagine caricata ed inoltre memorizzaimo i passi (suoni che successivamente vedremo come utilizzare) del personaggio e creiamo un canale apposito per loro. Infine abbiamo bisogno di una variabile “vita”, che in questo caso rappresenta il personaggio ancora in gioco, perché non ha ancora fatto esplodere nessuna bomba.

Classe oggetto_sprite : def collisione

Con questa funzione verifichiamo se il nostro alter ego è passato sopra una delle bombe. Per farlo controlliamo se l’oggetto rect del nostro personaggio collide con una sprite memorizzata nel gruppo. Non dimentichiamo che questo può accadere solo se è ancora in vita (perché in caso contrario si deve andare al fine gioco). Quindi se la bomba viene calpestata si deve richiamare l’animazione dell’esplosione solo sull’oggetto in questione (linea 27) e , visto che abbiamo settato la vita a false, non ci resta che modificare il valore “fine” della bomba esplosa. In questo modo, conclusa l’esplosione, la bomba richiamerà il fine gioco.

Classe oggetto_sprite : def anime

Se non siamo in tempo di animazione, settiamo la nostra immagine corrente a 0 (cioè la prima) e ritorniamo.
In caso contrario, gestiamo l’animazione in questo modo (a partire dalla linea 35): controlliamo che il tempo di animazione sia quello giusto, altrimenti non possiamo dare inizio all’animazione, ma bisogna aspettare. Da notare che in questo caso non abbiamo preso i “tps” ma utilizziamo un funzione dell’oggetto Clock (get_time()) che ci restituisce il tempo corrente dell’ultimo Clock.tick chiamato. Visto che è in millisecondi dobbiamo dividere per mille. Il procedimento in poche parole è lo stesso, dipende solo da voi come gestire il problema (quindi ho solo elencato due metodi equivaltenti per avere lo stesso risultato ai fini del gioco).
Come avrete sicuramente notato, alla funzione passiamo il parametro frame. Questo perché se l’animazione comincia (linea 38), dobbiamo sapere quale serie di immagini dobbiamo far visualizzare. Basta pensare che abbiamo memorizzato i movimenti all’interno di una matrice e quindi una serie di azioni differisce dall’altra per un parametro (frame). Quindi basta aggiornare l’animazione corrente sommando il frame che dobbiamo visualizzare. La figura sottostante vi chiarirà il tutto.
Quindi l’animazione consiste solo nel fatto di incrementare l’animazione corrente fino ad un massimo di 3 per poi azzerarla. A fine animazione azzeriamo anche il tempo, perché nel prossimo ciclo dobbiamo aspettare altri 0.150 secondi per cambiare frame.
Da notare invece alla riga 40 la gestione dei passi del personaggio. Per prima cosa controlliamo che il rumore dei passi non sia già in esecuzione. Quindi se il file in esecuzione è diverso dal passo1 (inteso come primo passo da far sentire), mettiamo in coda di esecuzione, nel canale apposito, passo1. Facciamo un ulteriore controllo se il canale è impegnato alla riga 41, perché se è la prima volta che il personaggio si muove, vogliamo far sentire il passo1 per primo. Infine mettiamo in coda il passo2. In questo modo avremo un realistico rumore quando il nostro personaggio camminerà (ricordate di tenere bene a mente i tempi animazione con la lunghezza dei suoni che scegliete per non rovinare il realismo della scena).

Classe oggetto_sprite : def update

Passandogli il frame, aggiorniamo la sprite memorizzandolo e chiamando l’animazione del frame corrente. Prendiamo successivamente le coordinate attuali del personaggio, perché dovranno essere incrementate a seconda della direzione che abbiamo passato. Infine controlliamo anche se il nostro personaggio si deve fermare, ternimando quindi l’animazione e il rumore dei passi.

Classe oggetto_sprite : def render

Render si occupa di far visualizzare il nostro alter ego sullo schermo nel seguente ordine: aggiorna le coordinate dell’oggetto rect del personaggio, verifica eventuali collisioni con altri oggetti ed infine renderizza sullo schermo memorizzato (screen) l’immagine attuale a seconda delle coordinate interne alla sprite.

PyGame Sprite 3

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale

Analisi

Come potete vedere alla linea 1, la classe creata estende pygame.sprite.Sprite, così da ereditare alcuni metodi e proprietà: per esempio possiamo utilizzare gruppi di sprite, per controllare meglio la renderizzazione (prossimamente vedremo anche questo) oppure per gestire meglio le collisioni. Dividerò l’analisi per ogni “funzione” che troveremo nella classe, per rendere più semplice la lettura.

Classe oggetto_esplosione : def __init__

Quando inizializziamo un nuovo oggetto abbiamo bisogno del nome, l’altezza e la larghezza della sprite che utilizzeremo, le coordinate della posizione iniziale (xy), l’oggetto screen (che rappresenza lo schermo dove si renderizza il tutto) e il numero di immagini che compongono la sprite (num).
Successivamente possiamo vedere che si richiama anche il metodo per inizializzare la sprite della libreria pygame (linea 3).
Procediamo caricando le immagini per la sprite, utilizzando la funzione vista in precedenza e settiamo l’immagine iniziale, che sarà il primo frame della lista di immagini ritornata.
Memorizziamo poi le coordinate dell’immagine, dividendo la tupla xy ed associamo un oggetto rect alla sprite, per gestire le collisioni. Per l’esempio in questione, ho spostato le cooridnate dell’oggetto rect per farle centrate rispetto l’immagine da me utilizzata, altrimenti la collisione inizierebbe appena si entra nel campo dell’immagine (anche se si utilizzano canali alpha), mentre qui ci interessa che la collisione con la bomba avvenga proprio nelle sue vicinanze. Inoltre in questo caso non necessitavo di spostare le varie bombe piazzate sullo schermo, ma se vi capita di fare un aggiustamento del genere dovete contarlo anche per eventuali spostamenti della sprite, perché l’oggetto rect deve rimanere incollato ad essa come un’ombra, altrimenti si perderà tutto il realismo che si è cercato di creare poco prima.
Non perdiamo traccia dello schermo (screen) e inizializziamo un variabile per l’esplosione (self.esplosione = False).
Memorizziamo poi il suono della nostra esplosione e il canale dove sarà riprodotto. Visto che tutte le esplosioni hanno assegnato lo stesso canale (il numero 4), sappiamo quindi che sarà solo utilizzato per la riproduzione delle bombe e non intaccherà gli altri effetti, come il sottofondo di pioggia e vento che metteremo.
Infine memorizziamo delle variabili che ci servono per le animazioni: maxframe per vedere se siamo arrivati alla fine, frame_corrente, temp_anim per il tempo di animazione e la variabile fine (necessaria per la fine del gioco, vedremo poi come).

Classe oggetto_esplosione : def update

Questa funzione necessita del tempo passato in secondi e di una stringa, che indicherà se l’esplosione deve avvenire o meno.
(Linea 24) Se l’esplosione è avvenuta (oppure non è mai iniziata) e la stringa passata è uguale a “None”, non dobbiamo far esplodere nulla; controlliamo però il volume dell’ambiente, perché in caso di esplosione conclusa, dobbiamo ripristinare i valori iniziali del volume, creando quindi un effetto di vuoto tra il tempo dell’esplosione e la sua conclusione (perchè più avanti abbasseremo il volume dell’ambiente se l’esplosione è avvenuta). Se il volume non è tornato ancora alla normalità si incrementa solamente il tempo di animazione. Il tutto ci serve per regolare la durata dell’effetto e se il volume di ambiente (che va di pari passo con quello del vento, per questo non controlliamo entrambi) è nella norma si esce subito, quindi non avviene nessuna esplosione e non si vedrà nulla (a meno che la stringa non indichi diversamente).
Dalla linea 31 vediamo invece che cosa accade se l’esplosione deve sussistere. Esplosione diventa vera grazie al primo controllo, controlliamo comunque che sia vera, perché significa che stiamo in animazione. Subito dopo, se siamo nel tempo di animazione giusto (per il mio esempio ho scelto che ogni frame deve essere visualizzato ogni 0.025 secondi) e che non siamo arrivati alla fine, mettiamo in pausa l’ambiente e il vento e riproduciamo l’audio dell’esplosione (se il frame corrente è il primo). Aggiorniamo l’immagine corrente e la renderizziamo sullo schermo; prepariamo poi il prossimo frame e rimettiamo il tempo di animazione a zero, perché devono passare altri 0.025 secondi per far renderizzare la prossima immagine.
Facciamo poi un altro controllo alla linea 45 per vedere se siamo giunti a fine animazione ed in questo caso, rimettiamo il frame corrente a zero, l’esplosione a False e il tempo di animazione a zero. Infine se il gioco deve finire, prepariamo le coordinate per renderizzare “Game Over” utilizzando pygame.font. Una volta centrato il testo richiamiamo la funzione notrun, che si occuperà della conclusione del gioco (vedremo il tutto a tempo debito). Ora basti notare che dalle coordinate del centro dello schermo (dividiamo il tutto per due infatti), sottraiamo metà della lunghezza e dell’altezza del testo che vogliamo stampare a video, così che sia perfettamente centrato una volta renderizzato. Se invece il gioco deve proseguire, impostiamo un nuovo volume all’ambiente e al vento (più basso del normale) e togliamo la pausa.
Se l’animazione non è conclusa renderizziamo a video il frame corrente e aggiorniamo il tempo di animazione per poi ritornare.

PyGame Sprite 2

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale

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.

PyGame Sprite 1

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale.

Il videogioco è composto in questo caso da cinque funzioni e due classi che sono riportate di seguito:

La prima funzione serve per caricare i vari frame di una sprite che sono necessari per creare l’animazione. La funzione è stata pensata per caricare le immagini con il “nome” del file passato (sempre in formato .png) e per memorizzari i vari frame, sia che essi siano in più file (quindi va indicato il numero di file che compongono l’animazione passando “num”), sia che il file sia uno solo, ed in quest’ultimo caso si appoggerà alle variabili di larghezza (“w”) e altezza (“h”) opportunamente passate per suddividere l’immagine in frame.

La seconda funzione è stata creata per necessità. Infatti c’è un bug che concerne la funzio Rect.move_ip, che muove un oggetto rect secondo le coordinate passategli. In poche parole, la funzione non supporta dei movimenti con numeri float, quindi ho dovuto riadattare la funzione per far funzionare l’esempio. In certi casi questa può essere una grave limitazione, ma a seconde delle collisioni che gestite (rect, rect circolari, mask …) dovete controllare e testare il funzionamento corretto. Tutto sta per non perdere il realismo del videogioco, quindi tenetelo bene in mente. Per il nostro esempio ho pensato di adattarlo, così da far vedere anche come poteva essere una possibile soluzione al problema.

La terza funzione è la più semplice, ma non la meno importante: infatti tiene il tempo di gioco e serve per sincronizzare movimenti e animazioni.

Poi abbiamo due classi che gestiscono le esplosioni e il giocatore (da notare che estendono la classe Sprite di pygame). Infatti un oggetto Sprite non è niente altro che un’immagine affiancata da un oggetto rect, così da poter gestire le collisioni. Le sprites sono molto utili anche perché possono essere gestite in gruppi, permettendoci quindi di gestire anche, per esempio, profondità, collisioni multiple e renderizzazioni molto più facilmente di pitturare immagini sullo schermo.

Le ultime due funzioni gestiscono il loop di gioco: notrun() per quando il gioco termina, mentre run() per il gioco avviato. La condizione alla fine invece ci permette di utilizzare questo script sia come modulo che come programma stand-alone.

PyGame 3

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale.

Come potete notare, il mixer deve essere configurato prima dell’inizializzazioone.
In questo caso passiamo i parametri per gestire un mixer a 44100 Hz, 16 bit stereo e 4096 kb di buffer.
Fatto questo, una volta inizializzato pygame, non ci resta che memorizzare gli effetti sonori da noi utilizzati.

Da notare come i click e gli applausi sono stati caricati come Sound, mentre la musica viene caricata a parte con pygame.mixer.music.load. Questo perché l’utilizzo è molto diverso e lo vediamo subito nella sezione eventi, dove sono state aggiunte le seguenti righe:

In questo modo, controlleremo l’esecuzione della musica tramite il tasto “M”, facendo variare semplicemente playmusica. Invece con il tasto “+” e “-” andiamo a controllare se il volume è già al suo massimo, ovvero 1.0, e se la musica viene eseguita, aumentiamo e diminuiamo il volume. Questo controllo del volume viene effettuato solo sulla musica e non riguarda alcun effetto Sound.

Come potete vedere ora, quando si clicca con il tasto sinistro del mouse viene avviato il suono click nel canaleclick. Canaleclick è proprio un Canale (Channel), poiché viene ritornato dalla funzione play() ed ha varie proprietà che possono essere modificate. Tra queste, ho scelto di modificare il volume. In poche parole, a seconda di dove pigiamo sulla schermata del nostro gioco, sentiremo il click sul canale destro o sinistro. Per fare questo, aggiorno tramite le coordinate del mouse il volume del canale dove viene riprodotto click. Vi ricordo che il volume è un valore float che va da 0.0 a 1.0 ed è per questo che divido per 640, visto che è la larghezza massima della nostra finestra. Si può notare subito la differenza con il click del tasto destro, sia nel codice che avviando il programma, poiché nel click destro del mouse c’è solo la seguente riga:

Per quanto riguarda gli applausi, sono solo disponibili quando si preme su start e si passa al gioco avviato:

Il metodo play accetta anche più parametri. Quelli che vi possono servire sono i primi due, ovvero loop e maxtime. Se indicate un numero maggiore di zero, il suono verrà eseguito una volta e poi n volte il numero da voi passato. Se invece volete ripetere un suono all’infinito ma lo volete limitare nel tempo, dovete solo fare come segue (il tempo è indicato in millisecondi):

Naturalmente il suono applausi è stato aggiunto anche nelle righe che riguarda la pressione del tasto start del joystick.
Per concludere, dobbiamo gestire lo scorrere della musica nel loop principale del gioco:

Così controlliamo se la musica deve essere eseguita o meno. Il metodo pygame.mixer.music.get_busy() serve per vedere se la musica è già in esecuzione; è solo un controllo in più. In questo caso non servirebbe, ma per ovviare ad errori di inizializzazione, meglio controllare che la musica sia realmente in esecuzione. Potete gestire l’esecuzione della musica anche senza la variabile Booleana playmusica.

PyGame 2

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale.

Analisi:

Rispetto agli altri esempi possiamo notare che abbiamo aggiunto “mouse”, il nostro puntatore. È un’immagine png con sfondo trasparente che potete creare voi stessi (quella utilizzata è 30×30 pixel, ma potete creare un puntatore anche più grande). Aggiungere il nostro puntatore è semplice e lo facciamo proprio con l’ultima riga di questa parte di codice.

Ponete particolare attenzione tra i due differenti caricamenti di “sfondo” e “puntatore”.

Il primo si coverte in una semplice superfice senza canale alpha, il secondo invece forziamo la conversione per non perdere il canale. Se non effettuiamo questa operazione, la trasparenza dello sfondo del mouse viene meno e visivamente avremo un puntatore con sfondo bianco, molto antiestetico, non trovate?

In queste righe impostiamo la velocità di gioco e creiamo l’orologio interno per sincronizzare il tutto. Inoltre creiamo il tasto start. Come potete notare, nell’immagine di sfondo creata per questo esempio, abbiamo un tasto start al centro. Essendo disegnato con tutta l’immagine, per poterlo cliccare con il mouse creiamo un oggetto che abbia la sua superfice, così da poter controllare successivamente se il puntatore vi è sopra o no.

L’oggetto Rect da noi creato non sarà visualizzato a schermo, serve a noi per fare dei calcoli. La prima tupla degli argomenti passati a Rect indica le coordinate del vertice in alto a sinistra del rettangolo che andiamo a creare. La seconda tupla indica invece la larghezza e l’altezza del rettangolo.

Ecco finalmente qualcosa di nuovo. Ora vedremo come aggiungere il nostro joystick. Premetto che il controller da me utilizzato è dell’XBOX 360, ma basta un qualsiasi controller con almeno 10 tasti, poi vedremo il perché.

Inizialmente il nostro controller non è collegato e quindi non attribuiamo niente a lui. Controlliamo successivamente se ci sono joystick con la funzione pygame.joystick.get_count(). Quindi se è maggiore di 0, aggiungiamo il primo joystick che troviamo e lo inizializziamo con controller.init().

Se non vi sono controller, non si aggiungerà nulla e vi sarà notificato che serve un joystick con almeno 10 tasti per utilizzare tutte le funzioni del programma.

Infine creiamo delle variabili per memorizzare gli spostamenti fatti, perché in questo file di esempio muoveremo lo sfondo con la tastiera.

Iniziamo quindi a controllare gli eventi. Potete ricordare che facciamo un controllo se la finestra viene chiusa con event.type == QUIT, terminando quindi il programma. Ora controlleremo anche quando i tasti vengono premuti.

Verifichiamo quindi i tasti premuti aggiungendoli ad una lista tramite la funzione pygame.key.get_pressed(). In seguito verifichiamo se i tasti premuti corrispondono al tasto ESC o alla combinazione di tasti ALT+F4 ed in questi casi terminiamo il programma.

Possiamo notare che si può interrogare direttamente event per avere i tasti premuti e quindi lo facciamo con le frecce direzionali (K_UP, K_DOWN, K_LEFT, K_RIGHT) e teniamo conto delle eventuali variazioni nel movimento, memorizzandole in move_x e move_y.

Se non vogliamo che il nostro sfondo continui a vagare nella direzione da noi premuta, dobbiamo ricordare che quando i tasti direzionali vengono rilasciati si deve cessare di muovere l’oggetto e lo facciamo con queste righe di codice.

Infine controlliamo i tasti del mouse. Anche in questo caso li memorizziamo in una lista. Più precisamente pygame.mouse.get_pressed() ritorna una “lista” di 3 elementi che rappresentano i tre pulsanti del muose con dei valori interi (0 se non sono premuti e 1 se sono premuti). Nel nostro caso controlliamo se vengono premuti il tasto sinistro (pulsanti_mouse[0]) e il tasto destro (pulsanti_mouse[2]) del mouse.

Per il tasto sinistro del mouse memorizziamo, quando viene pigiato, le coordinate del puntatore in quel momento. Di seguito, se siamo nella schermata di introduzione, controlliamo se le coorinate del mouse si trovano all’interno del pulsante da noi creato in precedenza. Se il tutto è vero, cambiamo l’immagine di sfondo con quella del gioco avviato.

Il tasto destro del muose ci serve invece per tornare alla schermata iniziale una volta cambiato lo sfondo e quindi controlliamo solo se siamo in gioco avviato.

L’ultimo controllo che facciamo riguarda il joystick. Come vi avevo detto prima, se il joystick è presente, controllo se viene premuto il tasto numero 8 (quindi il settimo perché la numerazione parte da zero), che sul joystick dell’XBOX 360 corrisponde al tasto “start”. Potete controllare sul vostro pannello di controllo (per gli utenti windows) quale sia l’ordine dei tasti del vostro controller per cambiarlo a piacimento. Comunque, anche in questo caso, controlliamo se siamo ancora alla schermata di avvio per cambiarla in gioco avviato quando pigiamo il tasto “start” del nostro joystick.

Ed ora occupiamoci della parte finale, cioè il loop principale di gioco. Per prima cosa disabilitiamo il puntatore del mouse di sistema, poi memorizziamo le coordinate del mouse, che ci serviranno per stampare a video il nostro puntatore. Quindi memorizziamo il tempo trascorso sincronizzandolo a 60 fps e convertiamo il tutto da millisecondi a secondi.

Calcoliamo dunque la distanza di spostamento in base alla velocità da noi impostata e aggiorniamo le coordinate x e y del nostro sfondo. Visto che abbiamo aggiunto un solo pulsante “immaginario” che si attiva nella sola schermata iniziale, dobbiamo solo aggiornare anche le sue coordinate, altrimenti il pulsante rimarrà sempre al centro dello schermo e non seguirà mai gli spostamenti dello sfondo (visto che il pulsante è tutt’uno con esso) e facciamo il tutto con tasto_start.move_ip(move_x*distanza_spostamento,move_y*distanza_spostamento). Move_ip muove l’oggetto Rect senza creare una nuova copia.

Fatto questo non ci resta che aggiornare lo sfondo dello schermo tramite screen.fill((0,0,0)). Questo comando renderà lo schermo nero, in attesa di essere riscritto. Se non facciamo questa operazione, quando sposteremo il nostro sfondo, rimarranno le tracce dello spostamento, provate a commentare questa riga per capire di cosa sto parlando.

Infine stampiamo a video lo sfondo con le coordinate aggiornate e il puntatore sopra di esso e facciamo aggiornare il buffer video tramite display.flip().

PyGame 1

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale.

L’idea è quella di far scorrere la nostra immagine da sinistra verso destra, quindi l’unica cosa da controllare sono le coordinate delle ascisse e che l’immagine risulti continua nel movimento. Per far questo abbiamo dovuto aggiungere una variabile ‘x’ per tenere conto degli spostamenti e quindi l’abbiamo opportunatamente sostituita alle coordinate da aggiornare (al posto delle ascisse) per renderizzare l’immagine.

Cambiando solo questo però, il risultato sarà che la nostra immagine si sposterà verso destra fino ad uscire dallo schermo. Per renderizzare la parte rientrante da sinistra, utilizziamo sempre screen.blit, ma con le coordinate partendo da – 640 pixel.

Per rendere il tutto consecutivo, abbiamo bisogno che qualcuno riporti le ascisse allo stato iniziale una volta raggiunto il fine spostamento, per rendere ciclica la transizione, in questo modo:

Con questi piccoli accorgimenti abbiamo la nostra immagine in movimento da sinistra verso destra, in un loop infinito, che si muoverà in base alla distanza_spostamento che sommiamo alla coordinata ‘x’.

Se fate girare questo programma senza gli altri accorgimenti che andremo ad introdurre tra poco, potrete facilmente notare che la velocità di esecuzione varia molto tra un pc e l’altro o anche sullo stesso pc in base alla disponibilità delle risorse di sistema.
Per rendere il tutto più omogeneo, abbiamo bisogno di due cose:
speed=250

Una velocità da noi preimpostata (speed=250) e un orologio che tenga conto del tempo passato. Fatto questo basta aggiungere :

Tick è una funzione membro dell’oggetto Clock da noi creato. In questo caso, passandogli l’intero 30 gli diamo la direttiva di aggiornare lo schermo 30 fps. Il valore restituito da quella funzione però è in millisecondi, quindi o si procede con il calcolo in millesimi oppure convertite il tutto in secondi semplicemente dividendo per 1000. A questo punto basta aggiornare la distanza percorsa moltiplicando il tempo passato con la velocità da noi voluta, ovvero 250 pixel per secondo.
In questo modo l’immagine da voi prodotta si muoverà alla stessa velocità su qualsiasi pc, basta che riesca a renderizzare 30 fps.

PyGame 0

Alcuni mesi fa ho trovato una serie di esempi sul pygame. Qui troverete la versione completa del articolo io postero solamente codice e una breve spiegazione tratta sempre dal articolo originale.

Con questa riga di codice memorizziamo il nome della nostra immagine di sfondo, che per essere visualizzata corretamente dovrà essere di 640×480 pixel, vedremo poi perché.

Con questi comandi importiamo le librerie necessarie, ovvero le librerie pygame nella prima riga, le variabili locali nella seconda e l’uscita per il programma dalle librerie di sistema nell’ultima. Fra poco vedremo a cosa servono.

La libreria Pygame è divisa in moduli che gestiscono vari tipi di eventi, per esempio display gestirà lo schermo, mentre mixer servirà per caricare e gestire eventi sonori. Tutti i moduli sono avviabili singolarmente, ma con pygame.init() li avvieremo tutti insieme, così che rimarranno a nostra disposizione quando ne avremo bisogno.

Caricare i moduli singolarmente ne guadagnerà in velocità, perché se per esempio non abbiamo bisogno dell’audio, si caricheranno cose inutilizzate in memoria, rallentando di conseguenza il programma. Per questo codice di esempio non fa nessuna differenza, ma in un progetto più complesso, l’ottimizzazione ha la sua buona parte per la riuscita del videogioco.

Con questa creiamo una finestra 640×480 con doppio buffer (da notare che dobbiamo scrivere solo DOUBLEBUF e non pygame.DOUBLEBUF perché abbiamo importato le variabili locali), accelerazione hardware (HWSURFACE) e con profondità 32 bit.

Qui settiamo il nome della finestra che abbiamo creato.

Ora carichiamo lo sfondo e lo convertiamo in una superficie per essere visualizzato sullo schermo. La conversione è necessaria per essere correttamente disegnato sullo schermo.

Questo è il vero corpo di gioco, per ora. Il primo ciclo equivale in poche parole ad una frame di gioco, mentre il secondo è indispensabile per controllare gli eventi al di fuori del gioco per far interagire l’utente. In questo caso basta controllare solamente se viene premuto il tasto di uscita della finestra (la ‘x’ della cornice).

Grazie a screen.blit, disegniamo il nostro sfondo dalle coordinate (0,0) in poi. Essendo la nostra finestra 640×480 pixel, è per questo motivo che l’immagine da visualizzare deve essere altrettanto grande, altrimenti sarebbe visualizzata solo una porzione. Vi ricordo che le coordinate dello schermo hanno l’origine nell’angolo in alto a sinistra e quindi anche le operazioni che fate sulle immagini tengono conto di questo.

L’ultima riga è un po’ più complessa da spiegare: avendo utilizzato l’accelerazione hardware abbiamo aggiunto un secondo buffer; in poche parole in questo secondo buffer il computer si prepara il prossimo frame da visualizzare. Quindi quando stampiamo a video la nostra immagine non dobbiamo ridisegnare da capo, ma dobbiamo semplicemente scambiare il buffer attuale con quello successivo. In questo modo si evitano discrepanze visive dovute al refresh della memoria video che deve ridisegnare ogni volta la stessa immagine sullo stesso buffer.