#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);
// Paddle settings
#define PADDLE_WIDTH 20
#define PADDLE_HEIGHT 4
#define PADDLE_Y (SCREEN_HEIGHT - 6)
int paddleX = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
// Ball settings
const int ballSize = 3;
int ballX, ballY;
int ballDX, ballDY;
// Buttons
const int buttonLeftPin = 2;
const int buttonRightPin = 3;
// Game state
bool gameRunning = true;
int score = 0;
int level = 1;
int baseSpeed = 10; // Base delay in ms
int gameSpeed = baseSpeed;
int minSpeed = 2; // Minimum speed (i.e., max ball speed)
void setup() {
pinMode(buttonLeftPin, INPUT_PULLUP);
pinMode(buttonRightPin, INPUT_PULLUP);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
while (true); // OLED not found
}
display.clearDisplay();
display.display();
resetGame();
}
void loop() {
if (gameRunning) {
// Button controls
bool moveLeft = digitalRead(buttonLeftPin) == LOW;
bool moveRight = digitalRead(buttonRightPin) == LOW;
if (moveLeft && paddleX > 0) paddleX -= 2;
if (moveRight && paddleX < SCREEN_WIDTH - PADDLE_WIDTH) paddleX += 2;
// Ball movement
ballX += ballDX;
ballY += ballDY;
// Bounce from walls
if (ballX <= 0 || ballX >= SCREEN_WIDTH - ballSize) ballDX *= -1;
if (ballY <= 0) ballDY *= -1;
// Paddle collision
if (ballY + ballSize >= PADDLE_Y && ballY + ballSize <= PADDLE_Y + PADDLE_HEIGHT) {
if (ballX + ballSize >= paddleX && ballX <= paddleX + PADDLE_WIDTH) {
ballDY *= -1;
ballY = PADDLE_Y - ballSize;
score++;
// 🆕 Update level every 10 points
level = (score / 10) + 1;
// Adjust speed
gameSpeed = baseSpeed - (level - 1);
if (gameSpeed < minSpeed) gameSpeed = minSpeed;
}
}
// Missed paddle
if (ballY > SCREEN_HEIGHT) {
gameRunning = false;
delay(1000); // Show missed ball for a moment
showGameOver();
waitForRestart();
}
// Drawing
display.clearDisplay();
// Paddle
display.fillRect(paddleX, PADDLE_Y, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_WHITE);
// Ball
display.fillRect(ballX, ballY, ballSize, ballSize, SSD1306_WHITE);
// Level and Score
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.print("Lvl: ");
display.print(level);
display.setCursor(64, 0);
display.print("Score: ");
display.print(score);
display.display();
delay(gameSpeed);
}
}
void resetGame() {
paddleX = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
ballX = SCREEN_WIDTH / 2;
ballY = SCREEN_HEIGHT / 2;
ballDX = 1;
ballDY = -1;
score = 0;
level = 1;
gameSpeed = baseSpeed;
gameRunning = true;
display.clearDisplay();
}
void showGameOver() {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
// Line 1: Game Over
display.setTextSize(1);
display.setCursor((SCREEN_WIDTH - 12 * 6) / 2, 5);
display.println("Game Over");
// Line 2: Level and Score
display.setTextSize(1);
String scoreText = "Level: " + String(level) + " Score: " + String(score);
int16_t x1, y1;
uint16_t w, h;
display.getTextBounds(scoreText, 0, 0, &x1, &y1, &w, &h);
display.setCursor((SCREEN_WIDTH - w) / 2, 32);
display.println(scoreText);
// Line 3: Restart prompt
String restartMsg = "Press any button";
display.getTextBounds(restartMsg, 0, 0, &x1, &y1, &w, &h);
display.setCursor((SCREEN_WIDTH - w) / 2, 48);
display.println(restartMsg);
display.display();
}
void waitForRestart() {
while (true) {
if (digitalRead(buttonLeftPin) == LOW || digitalRead(buttonRightPin) == LOW) {
resetGame();
break;
}
delay(100);
}
}