Arduino-клон Flappy Bird на TFT-сенсорном экране: пошаговый гайд
Сделаем копию популярной игры Flappy Bird на Arduino с TFT-сенсорным экраном. Управление птицей касанием, избегаем движущихся столбов, скорость растёт. Рекорд сохраняется в EEPROM даже без питания. Игра простая, но затягивающая. В предыдущем туториале (Arduino TFT Tutorial) мы изучили работу с TFT-экраном, а здесь разберём код игры. Код разбит на секции с пояснениями. В конце — полный листинг. Используем библиотеки UTFT и URTouch (Henning Karlsen, RinkyDinkElectronics.com) и EEPROM для хранения рекорда. Создаём объекты дисплея и тача, задаём шрифты и битмап птицы. Переменные: координаты столбов (xP, yP), птицы (yB), скорость падения, счёт, рекорд. Функция В Проблема: низкая частота обновления TFT вызывает мерцание при полной перерисовке. Решение: перерисовывать только движущиеся объекты. Функция Птица — битмап, конвертированный из фото через ImageConverter565. Функция Управление: касание экрана задаёт fallRate = -6 (прыжок). Удержание экрана игнорируется (нужно тапнуть). Каждые 5 очков movingRate увеличивается — игра ускоряется. Функция Ссылка на скачивание скетча, изображения птицы и битмап-файла.Обзор

Исходный код
#include <UTFT.h>
#include <URTouch.h>
#include <EEPROM.h>
// Объекты
UTFT myGLCD(SSD1289,38,39,40,41); // Параметры под ваш дисплей
URTouch myTouch(6, 5, 4, 3, 2);
// Шрифты
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
extern uint8_t SevenSegNumFont[];
extern unsigned int bird01[0x41A]; // Битмап птицы
int x, y; // Координаты касания
// Игра
int xP = 319, yP = 100, yB = 50;
int movingRate = 3, fallRateInt = 0; float fallRate = 0;
int score = 0, lastSpeedUpScore = 0, highestScore;
boolean screenPressed = false, gameStarted = false;
void setup() {
myGLCD.InitLCD(); myGLCD.clrScr();
myTouch.InitTouch(); myTouch.setPrecision(PREC_MEDIUM);
highestScore = EEPROM.read(0);
initiateGame();
}initiateGame() рисует стартовый экран: голубой фон, земля, текст «Score», «Highest Score», «TAP TO START», птицу. Ждёт касания экрана. Касание в правом верхнем углу сбрасывает рекорд. Касание в любой другой точке — старт игры.
void initiateGame() {
myGLCD.clrScr();
myGLCD.setColor(114,198,206); myGLCD.fillRect(0,0,319,239); // фон
myGLCD.setColor(221,216,148); myGLCD.fillRect(0,215,319,239); // земля
myGLCD.setColor(47,175,68); myGLCD.fillRect(0,205,319,214); // трава
// Текст
myGLCD.setColor(0,0,0); myGLCD.setBackColor(221,216,148); myGLCD.setFont(BigFont);
myGLCD.print("Score:",5,220);
myGLCD.setFont(SmallFont); myGLCD.print("HowToMechatronics.com",140,220);
myGLCD.setColor(0,0,0); myGLCD.setBackColor(114,198,206);
myGLCD.print("Highest Score: ",5,5); myGLCD.printNumI(highestScore,120,6);
myGLCD.print(">RESET<",255,5); myGLCD.drawLine(0,23,319,23);
myGLCD.print("TAP TO START",CENTER,100);
drawBird(yB);
while(!gameStarted) {
if(myTouch.dataAvailable()) {
myTouch.read(); x=myTouch.getX(); y=myTouch.getY();
if(x>=250 && x<=319 && y>=0 && y<=28) { // RESET
highestScore=0;
myGLCD.setColor(114,198,206); myGLCD.fillRect(120,0,150,22);
myGLCD.setColor(0,0,0); myGLCD.printNumI(highestScore,120,5);
}
if(x>=0 && x<=319 && y>=30 && y<=239) {
gameStarted=true;
myGLCD.setColor(114,198,206); myGLCD.fillRect(0,0,319,32);
}
}
}
myGLCD.setColor(114,198,206); myGLCD.fillRect(85,100,235,116); // стираем TAP TO START
}loop() столбы движутся влево (xP уменьшается на movingRate). Птица падает с ускорением (fallRate растёт). Проверяются столкновения с верхом/низами экрана и столбами. Птица рисуется функцией drawBird(). Когда столб уходит за экран (xP < -51), xP сбрасывается, yP — новая случайная высота, счёт увеличивается.
drawPilars() рисует столбы в новой позиции и закрашивает синим следы старой.void drawPilars(int x, int y) {
if(x>=270) {
myGLCD.setColor(0,200,20); myGLCD.fillRect(318,0,x,y-1);
myGLCD.setColor(0,0,0); myGLCD.drawRect(319,0,x-1,y);
myGLCD.setColor(0,200,20); myGLCD.fillRect(318,y+81,x,203);
myGLCD.setColor(0,0,0); myGLCD.drawRect(319,y+80,x-1,204);
} else if(x<=268) {
myGLCD.setColor(114,198,206); myGLCD.fillRect(x+51,0,x+60,y); // право
myGLCD.setColor(0,200,20); myGLCD.fillRect(x+49,1,x+1,y-1); // столб
myGLCD.setColor(0,0,0); myGLCD.drawRect(x+50,0,x,y); // рамка
myGLCD.setColor(114,198,206); myGLCD.fillRect(x-1,0,x-3,y); // лево
// нижний столб
myGLCD.setColor(114,198,206); myGLCD.fillRect(x+51,y+80,x+60,204);
myGLCD.setColor(0,200,20); myGLCD.fillRect(x+49,y+81,x+1,203);
myGLCD.setColor(0,0,0); myGLCD.drawRect(x+50,y+80,x,204);
myGLCD.setColor(114,198,206); myGLCD.fillRect(x-1,y+80,x-3,204);
}
myGLCD.setColor(0,0,0); myGLCD.setBackColor(221,216,148); myGLCD.setFont(BigFont);
myGLCD.printNumI(score,100,220);
}
drawBird() рисует битмап и закрашивает синим области выше и ниже птицы, стирая её предыдущее положение.void drawBird(int y) {
myGLCD.drawBitmap(50, y, 35, 30, bird01);
myGLCD.setColor(114,198,206);
myGLCD.fillRoundRect(50,y,85,y-6);
myGLCD.fillRoundRect(50,y+30,85,y+36);
}gameOver(): пауза 1 сек, экран «GAME OVER», счёт, обратный отсчёт 3–2–1, запись рекорда в EEPROM, сброс переменных, рестарт через initiateGame().
void gameOver() {
delay(1000);
myGLCD.clrScr(); myGLCD.setColor(255,255,255); myGLCD.setBackColor(0,0,0); myGLCD.setFont(BigFont);
myGLCD.print("GAME OVER",CENTER,40); myGLCD.print("Score:",100,80); myGLCD.printNumI(score,200,80);
myGLCD.print("Restarting...",CENTER,120); myGLCD.setFont(SevenSegNumFont);
myGLCD.printNumI(2,CENTER,150); delay(1000); myGLCD.printNumI(1,CENTER,150); delay(1000);
if(score > highestScore) { highestScore=score; EEPROM.write(0,highestScore); }
xP=319; yB=50; fallRate=0; score=0; lastSpeedUpScore=0; movingRate=3; gameStarted=false;
initiateGame();
}Полный код игры
/* Arduino Game Project
* by Dejan Nedelkovski, www.HowToMechatronics.com
* Uses UTFT & URTouch by Henning Karlsen (www.RinkyDinkElectronics.com)
*/
#include <UTFT.h> #include <URTouch.h> #include <EEPROM.h>
UTFT myGLCD(SSD1289,38,39,40,41); URTouch myTouch(6,5,4,3,2);
extern uint8_t SmallFont[]; extern uint8_t BigFont[]; extern uint8_t SevenSegNumFont[];
extern unsigned int bird01[0x41A];
int x,y; int xP=319,yP=100,yB=50,movingRate=3,fallRateInt=0; float fallRate=0;
int score=0,lastSpeedUpScore=0,highestScore; boolean screenPressed=false,gameStarted=false;
void setup(){ myGLCD.InitLCD(); myGLCD.clrScr(); myTouch.InitTouch(); myTouch.setPrecision(PREC_MEDIUM); highestScore=EEPROM.read(0); initiateGame(); }
void loop(){ xP-=movingRate; drawPilars(xP,yP); yB+=fallRateInt; fallRate+=0.4; fallRateInt=int(fallRate);
if(yB>=180||yB<=0){gameOver();}
if(xP<=85&&xP>=5&&yB<=yP-2){gameOver();}
if(xP<=85&&xP>=5&&yB>=yP+60){gameOver();}
drawBird(yB);
if(xP<=-51){xP=319; yP=rand()%100+20; score++;}
if(myTouch.dataAvailable()&&!screenPressed){fallRate=-6; screenPressed=true;}
else if(!myTouch.dataAvailable()&&screenPressed){screenPressed=false;}
if((score-lastSpeedUpScore)==5){lastSpeedUpScore=score; movingRate++;}
}
// initiateGame, drawPilars, drawBird, gameOver — как выше
Производственный процесс
- Создание потрясающей системы Ambilight для монитора на Arduino
- Создание классической игры Pong на Arduino UNO с OLED-дисплеем: пошаговый урок
- DIY USB-геймпад на Arduino: собственный высокопроизводительный контроллер
- Arduino Breakout на сенсорном TFT 2.4″: классическая аркада своими руками
- Гигантский анимированный LEGO-человечек: игра «Операция» на Arduino
- Детектор аудиочастот на Arduino: измеряем пик громкости с высокочувствительным модулем
- Крестики-нолики на Arduino с матрицей MAX7219 и картонным корпусом
- Сборка надежного таймера обратного отсчета на Arduino с 7-сегментным дисплеем SparkFun
- Pixel Chaser: игра на один тап с NeoPixel Ring и Arduino Nano
- Самодельный RC Porsche на Arduino: Пошаговое руководство