
Projekt przedstawia klasyczną grę Flappy Bird, zaadaptowaną na mikrokontroler Arduino z wyświetlaczem OLED 128x64 (SSD1306). Gracz steruje ptakiem, który musi unikać przeszkód w postaci rur – rozgrywka polega na utrzymaniu się w powietrzu, klikając przycisk, który powoduje skok ptaka.
1. Płytka Arduino UNO lub kompatybilna
2. Shield SIC Game Console
Komputer PC z Arduino IDE
Projekt przedstawia klasyczną grę Flappy Bird, zaadaptowaną na mikrokontroler Arduino z wyświetlaczem OLED 128x64 (SSD1306). Gracz steruje ptakiem, który musi unikać przeszkód w postaci rur – rozgrywka polega na utrzymaniu się w powietrzu, klikając przycisk, który powoduje skok ptaka.
Głównymi założeniami gry są:
- opadanie postaci pod wpływem grawitacji
- zakończenie gry gdy postać dotknie przeszkody lub spadnie poza ekran
- po zakończeniu gry wyświetlany jest wynik oraz rekord
- po uzyskaniu 5pkt zmienia się grafika gry




#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Pins
const int buttonPin = 9; //wybrort przyciskou do skoku
const int gravity = 1; //wsp grawiatcji
const int flapStrength = -5; //zmniejszac wrtosc z np. -4 na -5 powodujemyu wiekszy skok postaci
// wyglad postaci - bitmapa
const unsigned char PROGMEM flappyBirdSprite[] = {
0b00001110,
0b00001010,
0b00001111,
0b01111110,
0b01111100,
0b01111100,
0b00010000,
0b00010000
};
const unsigned char PROGMEM flappyBirdSpriteNew[] = {
0b00011100,
0b00111110,
0b00101010,
0b00111110,
0b00100010,
0b00111110,
0b00111110,
0b00101010
};
// Bird
int birdY;
int birdVelocity = 0;
const int birdX = 20;
// Pipes
int pipeX;
int pipeGap = 25;
int pipeTopHeight;
bool passedPipe = false;
// Timing
unsigned long lastUpdate = 0;
int gameSpeed = 30;
// Game state
bool isGameOver = false;
bool isIntro = true;
bool gameOverFalling = false;
int score = 0;
int highScore = 0;
bool useNewBird = false;
bool useNewPipes = false;
// Debounce
unsigned long lastButtonPress = 0;
const int debounceDelay = 150;
bool isButtonPressed() {
if (digitalRead(buttonPin) == LOW) {
if (millis() - lastButtonPress > debounceDelay) {
lastButtonPress = millis();
return true;
}
}
return false;
}
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.setRotation(2);
display.setTextSize(1);
display.setTextColor(WHITE);
birdY = SCREEN_HEIGHT / 2;
showIntroScreen();
}
void resetGame() {
if (score > highScore) highScore = score;
birdY = SCREEN_HEIGHT / 2;
birdVelocity = 0;
pipeX = SCREEN_WIDTH;
pipeTopHeight = random(10, 40);
isGameOver = false;
gameOverFalling = false;
score = 0;
passedPipe = false;
useNewBird = false;
useNewPipes = false;
}
void showIntroScreen() {
display.clearDisplay();
display.setCursor(25, 10);
display.setTextSize(2);
display.println("Flappy");
display.setCursor(35, 28);
display.println("Bird");
display.setTextSize(1);
display.setCursor(10, 55);
display.println("Press button to start");
//display.drawBitmap(birdX, birdY, flappyBirdSprite, 8, 8, WHITE); // rysowanie bitmapy powodowalo zacinanie sie gry
display.display();
}
//animacja po przegranej tzn postac spada swobodnie do opuszczenia ekranu
void animateGameOverFall() {
while (birdY < SCREEN_HEIGHT - 😎 {
birdVelocity += gravity;
birdY += birdVelocity;
display.clearDisplay();
display.drawBitmap(birdX, birdY, useNewBird ? flappyBirdSpriteNew : flappyBirdSprite, 8, 8, WHITE);
display.display();
delay(30);
}
gameOverFalling = false;
}
//ekran koncowy wyswietlajacy wynik i rekord gry
void showGameOverScreen() {
display.clearDisplay();
display.setCursor(25, 15);
display.println("Game Over");
display.setCursor(10, 35);
display.print("Final: ");
display.print(score);
display.print(" Best: ");
display.println(highScore);
display.setCursor(15, 50);
display.println("Press to restart");
display.display();
}
void loop() {
if (isIntro) {
showIntroScreen();
if (isButtonPressed()) {
isIntro = false;
resetGame();
}
delay(10);
return;
}
if (isGameOver) {
if (gameOverFalling) {
animateGameOverFall();
} else {
showGameOverScreen();
if (isButtonPressed()) {
isIntro = true;
}
}
return;
}
if (millis() - lastUpdate > gameSpeed) {
lastUpdate = millis();
if (isButtonPressed()) {
birdVelocity = flapStrength;
}
birdVelocity += gravity;
birdY += birdVelocity;
if (birdY < 0) birdY = 0;
pipeX -= 2;
if (!passedPipe && pipeX + 10 < birdX) {
score++;
passedPipe = true;
if (score >= 5) {
useNewBird = true;
useNewPipes = true;
}
}
if (pipeX < -10) {
pipeX = SCREEN_WIDTH;
pipeTopHeight = random(10, 40);
passedPipe = false;
}
if (birdY > SCREEN_HEIGHT ||
(birdX + 8 > pipeX && birdX < pipeX + (useNewPipes ? 20 : 10) &&
(birdY < pipeTopHeight || birdY > pipeTopHeight + pipeGap))) {
isGameOver = true;
gameOverFalling = true;
birdVelocity = 0;
return;
}
// RENDER
display.clearDisplay();
display.drawBitmap(birdX, birdY, useNewBird ? flappyBirdSpriteNew : flappyBirdSprite, 8, 8, WHITE);
if (useNewPipes) {
display.fillRect(pipeX, 0, 20, pipeTopHeight, WHITE);
display.fillRect(pipeX, pipeTopHeight + pipeGap, 20, SCREEN_HEIGHT - (pipeTopHeight + pipeGap), WHITE);
} else {
display.fillRect(pipeX, 0, 10, pipeTopHeight, WHITE);
display.fillRect(pipeX, pipeTopHeight + pipeGap, 10, SCREEN_HEIGHT - (pipeTopHeight + pipeGap), WHITE);
}
display.setCursor(0, 0);
display.print("Score: ");
display.print(score);
display.setCursor(80, 0);
display.print("Best: ");
display.print(highScore);
display.display();
}
}