Skip to content

Instantly share code, notes, and snippets.

@luis-c465
Created February 14, 2025 04:15
Show Gist options
  • Save luis-c465/61d3abeed9d1fb80a5d70633b330fe2a to your computer and use it in GitHub Desktop.
Save luis-c465/61d3abeed9d1fb80a5d70633b330fe2a to your computer and use it in GitHub Desktop.
Arduino flappy bird code
#include <SPI.h>
#include <Wire.h>
#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);
#define BUTTON_PIN 2
// Game Variables
int birdX, birdY;
int birdVelocity;
int gravity = 1;
int flapStrength = -4;
int pipeX;
int pipeWidth = 20;
int pipeSpacing = 50;
int score = 0;
bool gameOver = false;
bool gameStarted = false;
// --- Enhanced Randomness Variables ---
int pipeHeightTop;
int pipeHeightBottom;
int minPipeGap = 35; // Slightly reduced min gap for more challenge
int maxPipeGap = 60; // Increased max gap for more variation
int pipeSpeed = 2;
int gameFrameDelay = 1;
int minGapCenter = SCREEN_HEIGHT / 4; // Minimum for pipeGapY (center of gap)
int maxGapCenter = SCREEN_HEIGHT * 3 / 4; // Maximum for pipeGapY (center of gap)
int pipeHeightVariation = 40; // Max variation from center for pipe heights
// ---------------------------------------
void setup() {
Serial.begin(9600);
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
pinMode(BUTTON_PIN, INPUT_PULLUP);
startGame();
drawStartScreen();
}
void loop() {
if (!gameStarted) {
if (digitalRead(BUTTON_PIN) == LOW) {
gameStarted = true;
delay(200);
}
} else if (!gameOver) {
updateGame();
drawGame();
delay(gameFrameDelay); // Use the new gameFrameDelay
} else {
drawGameOverScreen();
gameStarted = false;
if (digitalRead(BUTTON_PIN) == LOW) {
startGame();
gameOver = false;
gameStarted = true;
delay(200);
}
}
}
void startGame() {
birdX = SCREEN_WIDTH / 4;
birdY = SCREEN_HEIGHT / 2;
birdVelocity = 0;
pipeX = SCREEN_WIDTH;
randomizePipes(); // Initialize pipes with random heights and gap
score = 0;
}
void randomizePipes() {
pipeSpeed = 2 + score / 3;
// --- More Independent and Wider Randomization ---
int gapSize = random(minPipeGap, maxPipeGap); // Random gap size
// Randomize the center of the gap more freely within the screen
int pipeGapY = random(minGapCenter, maxGapCenter);
// Calculate top pipe height - allow more variation from the gap center
pipeHeightTop = pipeGapY - gapSize / 2 + random(-pipeHeightVariation/2, pipeHeightVariation/2);
pipeHeightBottom = SCREEN_HEIGHT - (pipeGapY + gapSize / 2) + random(-pipeHeightVariation/2, pipeHeightVariation/2);
// Ensure pipes are within screen bounds and gaps are playable
pipeHeightTop = constrain(pipeHeightTop, 10, SCREEN_HEIGHT - minPipeGap - 10);
pipeHeightBottom = constrain(pipeHeightBottom, 10, SCREEN_HEIGHT - minPipeGap - 10);
// Recalculate pipeGapY based on new heights to ensure consistent gap center (optional but good to have)
pipeGapY = pipeHeightTop + gapSize / 2; // Re-establish the gap center based on top pipe height and gap size
}
void updateGame() {
if (gameOver) {
return;
}
if (digitalRead(BUTTON_PIN) == LOW) {
birdVelocity = flapStrength;
}
birdVelocity += gravity;
birdY += birdVelocity;
// Pipe movement - using increased pipeSpeed
pipeX -= pipeSpeed;
if (pipeX < -pipeWidth) {
pipeX = SCREEN_WIDTH + pipeSpacing;
randomizePipes(); // Randomize pipes when new pipe set enters
score++;
}
if (birdY < 0 || birdY > SCREEN_HEIGHT) {
gameOver = true;
}
if (birdX + 4 > pipeX && birdX - 4 < pipeX + pipeWidth) {
// Collision detection now uses pipeHeightTop and pipeHeightBottom
if (birdY - 4 < pipeHeightTop || birdY + 4 > SCREEN_HEIGHT - pipeHeightBottom) {
gameOver = true;
}
}
}
void drawGame() {
display.clearDisplay();
display.fillCircle(birdX, birdY, 4, WHITE);
// Draw Pipes - Using pipeHeightTop and pipeHeightBottom
display.fillRect(pipeX, 0, pipeWidth, pipeHeightTop, WHITE); // Top pipe - height is now variable
display.fillRect(pipeX, SCREEN_HEIGHT - pipeHeightBottom, pipeWidth, pipeHeightBottom, WHITE); // Bottom pipe - height is now variable
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(10, 10);
display.print("Score: ");
display.print(score);
display.display();
}
void drawStartScreen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(SCREEN_WIDTH / 2 - 40, SCREEN_HEIGHT / 2 - 20);
display.print("Flappy");
display.setCursor(SCREEN_WIDTH / 2 - 40, SCREEN_HEIGHT / 2);
display.print("Bird");
display.setTextSize(1);
display.setCursor(SCREEN_WIDTH / 2 - 50, SCREEN_HEIGHT - 10);
display.print("Press Button");
display.display();
}
void drawGameOverScreen() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(SCREEN_WIDTH / 2 - 50, SCREEN_HEIGHT / 2 - 20);
display.print("Game Over");
display.setTextSize(1);
display.setCursor(SCREEN_WIDTH / 2 - 40, SCREEN_HEIGHT / 2 + 10);
display.print("Score: ");
display.print(score);
display.setCursor(SCREEN_WIDTH / 2 - 60, SCREEN_HEIGHT / 2 + 20);
display.print("Press to Restart");
display.display();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment