CODE
TFT VCC → 3.3V
TFT GND → GND
TFT SCK → GPIO 18
TFT SDA → GPIO 23
TFT RES → GPIO 4
TFT DC → GPIO 2
TFT CS → GPIO 5
Button:
One side → GPIO 15
Other → GND
Buzzer
One side GPIO 19
Other GND
CODE
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>
// TFT Pins
#define TFT_CS 5
#define TFT_RST 4
#define TFT_DC 2
// Controls
#define BTN 15
#define BUZZER 19
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
// Colors
#define SKY tft.color565(80,180,255)
#define GROUND tft.color565(200,150,50)
// Bird sprite
#define BIRD_W 16
#define BIRD_H 8
const uint16_t birdBitmap[BIRD_W * BIRD_H] = {
0,0,0xFFE0,0xFFE0,0xFFE0,0,0,0,0,0,0,0,0,0,0,0,
0,0xFFE0,0xFFFF,0xFFE0,0xFFFF,0xFFE0,0,0,0,0,0,0,0,0,0,0,
0xFFE0,0xFFFF,0xFFFF,0xFFE0,0xFFFF,0xFFFF,0xFFE0,0,0,0,0,0,0,0,0,0,
0xFFE0,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFE0,0,0,0,0,0,0,0,0,0,
0xFFE0,0xFFFF,0,0xFFFF,0,0xFFFF,0xFFE0,0,0,0,0,0,0,0,0,0,
0,0xFFE0,0xFFFF,0xFFFF,0xFFFF,0xFFE0,0,0,0,0,0,0,0,0,0,0,
0,0,0xFFE0,0xFFFF,0xFFE0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0xFFE0,0,0,0,0,0,0,0,0,0,0,0,0
};
// Game variables
float birdY = 60;
float velocity = 0;
int pipeX = 160;
int gapY = 50;
int score = 0;
bool gameOver = false;
bool gameStarted = false;
unsigned long lastFrame = 0;
void setup() {
pinMode(BTN, INPUT_PULLUP);
pinMode(BUZZER, OUTPUT);
tft.initR(INITR_BLACKTAB);
tft.fillScreen(ST77XX_BLACK);
showStartScreen();
}
void loop() {
// Start screen
if (!gameStarted) {
if (digitalRead(BTN) == LOW) {
gameStarted = true;
resetGame();
delay(300);
}
return;
}
// Game over screen
if (gameOver) {
showGameOver();
if (digitalRead(BTN) == LOW) {
gameStarted = false;
delay(300);
showStartScreen();
}
return;
}
// Frame control (smooth)
if (millis() - lastFrame < 30) return;
lastFrame = millis();
updateGame();
drawGame();
}
// 🎮 Game Logic
void updateGame() {
// Jump
if (digitalRead(BTN) == LOW) {
velocity = -2;
tone(BUZZER, 1200, 50);
}
// Gravity
velocity += 0.20;
if (velocity > 4) velocity = 4;
birdY += velocity;
// Ground collision
if (birdY > 112) {
birdY = 112;
gameOver = true;
tone(BUZZER, 200, 300);
}
// Pipe movement
pipeX -= 2;
if (pipeX < -20) {
pipeX = 160;
gapY = random(20, 80);
score++;
}
// Collision
if (pipeX < 35 && pipeX > 5) {
if (birdY < gapY || birdY > gapY + 50) {
gameOver = true;
tone(BUZZER, 200, 300);
}
}
}
// 🎨 Draw
void drawGame() {
// Sky
tft.fillRect(0, 0, 160, 128, SKY);
// Ground
tft.fillRect(0, 120, 160, 8, GROUND);
// Bird
tft.drawRGBBitmap(20, (int)birdY, birdBitmap, BIRD_W, BIRD_H);
// Pipes
tft.fillRect(pipeX, 0, 12, gapY, ST77XX_GREEN);
tft.fillRect(pipeX, gapY + 50, 12, 120, ST77XX_GREEN);
// Pipe heads
tft.fillRect(pipeX - 2, gapY - 5, 16, 5, ST77XX_GREEN);
tft.fillRect(pipeX - 2, gapY + 50, 16, 5, ST77XX_GREEN);
// Score
tft.setCursor(5, 5);
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(1);
tft.print("Score:");
tft.print(score);
}
// 🔁 Reset
void resetGame() {
birdY = 60;
velocity = 0;
pipeX = 160;
score = 0;
gameOver = false;
}
// ▶ Start Screen
void showStartScreen() {
tft.fillScreen(ST77XX_BLACK);
tft.setCursor(20, 50);
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(2);
tft.print("FLAPPY");
tft.setCursor(10, 80);
tft.setTextSize(1);
tft.print("Press Button");
}
// 💀 Game Over
void showGameOver() {
tft.fillScreen(ST77XX_BLACK);
tft.setCursor(20, 40);
tft.setTextColor(ST77XX_RED);
tft.setTextSize(2);
tft.print("GAME OVER");
tft.setCursor(20, 80);
tft.setTextSize(1);
tft.setTextColor(ST77XX_WHITE);
tft.print("Press Button");
}
