In questo tutorial vedremo in che modo creare un gioco interattivo con Arduino.
Flappy Bird è un videogioco per telefono cellulare sviluppato dal programmatore vietnamita Dong Nguyen e distribuito su App Store e Google Play dalla dotGEARS Studios a partire da maggio 2013, per esser poi ritirato dagli store il 10 febbraio 2014.
Basta con le chiacchiere, arriviamo subito al sodo.
Le specifiche tecniche di questo display sono le seguenti:
- risoluzione 128×160 a 65 mila colori
- dimensioni: 28x35mm
- driver: st7735
- 5v o 3.3v
- Opzioni SD Card reader
- Interfaccia SPI
- 1,8″
I materiali da utilizzare per il collegamento del display TFT 1,8″ alla board Arduino sono:
- Arduino Uno
- Display TFT 1,8″
- Cavo USB
- 1x breadboard
- 1x bottone
- resistenza da 10K Ohm
COLLEGAMENTO
Ci sono tantissimi display TFT da 1,8″ nel mercato, quindi il collegamento potrebbe cambiare da display a display. I display possono avere la posizioni di alcuni pin differente ma comunque sono presenti le “stesse sigle” che permettono quindi di effettuare correttamente il collegamento prendendo anche come riferimento altri display con la posizione dei pin diversa. Rispetto a questo display ce ne potrebbero essere altri con pin BLK (blacklight), che in questo caso non è presente, ma niente di preoccuparsi, lo puoi collegare al pin 12.
Se è la prima volta che utilizzi il display, prima di scrivere il codice ti consiglio vivamente di leggere questo articolo introddutivo sul collegamento e configurazione del display ST7735 TFT 1,8″
Come collegare display TFT 1,8″ ST7735 ad Arduino
Ecco una schema che può tornare utile:
Ecco il diagramma di collegamento:
CODICE
Puoi trovare la fonte del progetto e il CODICE su github: github.com/mrt-prodz/ATmega328-Flappy-Bird-Clone
Approccio al codice
Il processo di creazione del gioco si è svolto in più fasi:
- provare il pulsante e interagire con lo schermo
- fare in modo che il pulsante una volta premuto sposti l’uccellino (il nostro giocatore)
- testare la collisione dell’uccellino con il suolo
- visualizzare il tubo, che si muoverà sullo schermo e si troverà il modo più veloce per disegnarlo
- provare la collisione dell’uccellino con il tubo
- impostare un sistema di punteggio
- aggiungere velocità e gravità all’uccellino
- provare a usare uno sprite per il nostro uccellino invece di una forma
- provare a migliore anche il tubo
- provare a creare un ambiente (limitato)
Il ciclo di gioco è suddiviso in tre parti:
- aggiornamento del gioco (posizione dell’uccellino e degli oggetti, controllo dei pulsanti, ecc.)
- disegno (sfondo, tubo, giocatore, punteggio)
- controllo la collisione (se l’uccellino ha superato con successo un tubo si aumenta il punteggio, se ha colpito un tubo o il terreno la partita termina)
Il codice utilizza due librerie:
- Libreria Adafruit GFX: github.com/adafruit/Adafruit-GFX-Library
- Libreria Adafruit ST7735: github.com/adafruit/Adafruit-ST7735-Library
Il programma ha bisogno di entrambe le librerie per interagire con lo schermo.
Ci sono un paio di funzioni per disegnare linee, forme e pixel sullo schermo:
- fillScreen(uint16_t color)
- drawPixel(int16_t x, int16_t y, uint16_t color)
- drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
- drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
- fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
TEST E PROVE
Se si utilizza la funzione fillScreen l’esecuzione del codice è estremamente lenta, e non va bene per un gioco. Riempiremmo la memoria.
Una probabile soluzione potrebbe consistere nel “brush method”. Dopo ogni frame io non cancello lo schermo intero ma “cancello” solo una parte dei pixel (quelli della parte sinistra in particolar modo)
Vediamo con una immagine una illustrazione di questo metodo:
A cosa dobbiamo porre attenzione? Dobbiamo utilizzare il colore dello sfondo nel momento in cui l’uccellino e i tubi si muovono.
Per l’uccellino salviamo la posizione y ad ogni interazione utilizzando una variabile old_y. Prima di disegnare la nuova posizione “cancelliamo” il vecchio uccellino utilizzando il colore dello sfondo nella posizione old_y. Utilizziamo lo stesso metodo per il tubo e per tutto ciò che si muove.
Risparmiamo in questo modo molta memoria in quanto limitiamo l’intervento delle funzioni che disegnano lo sfondo solo nelle posizioni in cui sappiamo che in quel punto vi è un movimento o qualcosa da “cancellare”.
I tubi non sono “rettangoli”, ma costituiti da singoli linee di larghezza 1 e altezza h. Nel momento in cui viene cancellata la parte sx e si forma la parte dx i tubi saranno formati da tante linee di larghezza 1 e altezza h.
Per verificare se ci sono collisioni si controlla se il riquadro di delimitazione dell’uccellino è all’interno del riquadro di delimitazione del tubo.
1 2 3 4 5 6 7 8 9 10 11 |
// checking for bird collision with pipe if (bird.x+BIRDW >= pipe.x-BIRDW2 && bird.x <= pipe.x+PIPEW-BIRDW) { // bird entered a pipe, check for collision if (bird.y < pipe.gap_y || bird.y+BIRDH > pipe.gap_y+GAPHEIGHT) break; else passed_pipe = true; } // if bird has passed the pipe increase score else if (bird.x > pipe.x+PIPEW-BIRDW && passed_pipe) { passed_pipe = false; // ... } |
Per un movimento migliore dell’uccellino si incrementa in modo costa la velocità lungo l’asse y e viene calcolata moltiplicato il valore della forza di gravità con l’intervallo di tempo deltaT.
L’intervallo di tempo deltaT viene calcolato sottraendo l’ora corrente con l’ora della precedente iterazione
1 2 3 4 5 |
bird.vel_y += GRAVITY * delta; bird.y += bird.vel_y; old_time = current_time; current_time = millis(); delta = (current_time-old_time)/1000; |
Ottimizzazione del codice
Per ottenere migliori performance al posto della funzione digitalRead(pin) è stata utilizzata la manipolazione diretta delle porte. Per maggiori informazioni leggi:
https://www.arduino.cc/en/Reference/PortManipulation
Scrivere efficiente codice ottimizzato per Arduino
Nel momento in cui un determinata variabile non cambia valore viene memorizzata questo valore in un’altra variabile temporanea al di fuori del ciclo. In questo modo evitiamo il calcolo per ogni interazione. Ovviamente questa cosa vale per variabili con valore costante.
Invece di utilizzare il metodo drawPixel sono stare create funzioni (setAddrWindow e pushColor) che permettono di cancellare e disegnare lo spirte dell’uccellino.
Il pavimento non è ridisegnato a ogni iterazione, in quanto in questa area non si muove nulla.
Conclusione
Voglio ringraziare AZ-Delivery per avermi dato la possibilità di avere il display.
Preferireste aspettare 8 settimane per l’autoimportazione dalla Cina, incerti se le merci arriveranno mai, le terre alla dogana o semplicemente saranno rispedite indietro? O acquistare in Germania ad un prezzo totalmente gonfiato da un rivenditore specializzato? Preferisci acquistare il prodotto a buon mercato in Cina senza essere sicuro di quale versione del prodotto riceverai, o ordinarlo dal rivenditore tedesco incl. istruzioni e scheda dati?
Nel seguente link è presente il sito ufficiale AZdelivery
Iscriviti ai nostri gruppi Telegram
Link utili