#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <TM1637.h>
#define SCREEN_WIDTH 128 // OLED ekran genişliği
#define SCREEN_HEIGHT 64 // OLED ekran yüksekliği
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define BALL_SIZE 3
#define PADDLE_WIDTH 20
#define PADDLE_HEIGHT 3
#define BRICK_WIDTH 16
#define BRICK_HEIGHT 8
#define NUM_BRICK_ROWS 3
#define NUM_BRICKS_PER_ROW 8
int ballX = SCREEN_WIDTH / 2;
int ballY = SCREEN_HEIGHT / 2;
int ballVelocityX = 4;
int ballVelocityY = -4;
int paddleX = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
int paddleY = SCREEN_HEIGHT - 10;
const int buttonUp = 8; // Yukarı butonun pin numarası
const int buttonDown = 7; // Aşağı butonun pin numarası
const int buttonSelect = 6; // Exit butonu pin numarası
const int buttonMode = 2;
const int potPin = A0; // Potansiyometrenin bağlı olduğu analog pin
const int firstLife = 5;
const int secondLife = 4;
const int thirdLife = 3;
const int CLK = 12;
const int DIO = 11;
TM1637 tmd(CLK, DIO);
int menuIndex = 0; // Menü indeksi
bool isGameRunning = false; // Oyunun çalışıp çalışmadığını kontrol eden değişken
bool darkMode = true;
int currentLevel = 1; // Başlangıç levelı
int bricksLeft = NUM_BRICK_ROWS * NUM_BRICKS_PER_ROW;
int maxLevel = 5;
int lives = 3;
int score = 0; // Başlangıç skoru
bool lastButtonState = HIGH; // Butonun önceki durumunu saklamak için değişken
bool lightModeControl = false;
struct ExtraLife {
int x;
int y;
bool isActive;
};
ExtraLife extraLives[5];
int bricks[NUM_BRICK_ROWS][NUM_BRICKS_PER_ROW][2];
void setup() {
pinMode(buttonUp, INPUT_PULLUP);
pinMode(buttonDown, INPUT_PULLUP);
pinMode(buttonSelect, INPUT_PULLUP);
pinMode(buttonMode, INPUT_PULLUP);
pinMode(potPin, INPUT);
pinMode(firstLife, OUTPUT);
pinMode(secondLife, OUTPUT);
pinMode(thirdLife, OUTPUT);
digitalWrite(firstLife, HIGH);
digitalWrite(secondLife, HIGH);
digitalWrite(thirdLife, HIGH);
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Ekranı başlat
display.clearDisplay();
display.display();
tmd.begin();
tmd.setBrightness(0x0f);
for (int row = 0; row < NUM_BRICK_ROWS; row++) {
for (int col = 0; col < NUM_BRICKS_PER_ROW; col++) {
bricks[row][col][0] = col * BRICK_WIDTH; // X konumu
bricks[row][col][1] = row * BRICK_HEIGHT; // Y konumu
}
}
}
void loop() {
if (!isGameRunning) {
handleMenu();
} else {
runGame();
}
bool currentButtonState = digitalRead(buttonMode);
if (currentButtonState == LOW && lastButtonState == HIGH) {
darkMode = !darkMode;
}
lastButtonState = currentButtonState;
}
void handleMenu() {
updateDisplay();
bool upPressed = !digitalRead(buttonUp);
bool downPressed = !digitalRead(buttonDown);
bool selectPressed = !digitalRead(buttonSelect);
static unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 150;
if ((millis() - lastDebounceTime) > debounceDelay) {
if (upPressed) {
menuIndex--;
menuIndex = max(0, menuIndex);
updateDisplay();
lastDebounceTime = millis();
}
if (downPressed) {
menuIndex++;
menuIndex = min(menuIndex, 1);
updateDisplay();
lastDebounceTime = millis();
}
if (selectPressed) {
selectMenuOption();
lastDebounceTime = millis();
}
}
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
if (darkMode) {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_BLACK);
display.setTextColor(SSD1306_WHITE);
} else {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
}
if (menuIndex == 0) {
display.setCursor(50, 20);
display.println(F("> Start"));
} else {
display.setCursor(50, 20);
display.println(F(" Start"));
}
if (menuIndex == 1) {
display.setCursor(50, 40);
display.println(F("> Exit"));
} else {
display.setCursor(50, 40);
display.println(F(" Exit"));
}
display.display();
}
void selectMenuOption() {
if (menuIndex == 0) {
startGame();
} else if (menuIndex == 1) {
exitGame();
}
}
void startGame() {
isGameRunning = true;
lives = 3;
score = 0;
currentLevel = 1;
resetBricks();
resetBall();
paddleX = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
updateLivesDisplay();
tmd.clearScreen();
ballX = SCREEN_WIDTH / 2;
ballY = SCREEN_HEIGHT / 2;
paddleX = (SCREEN_WIDTH - PADDLE_WIDTH) / 2;
}
void runGame() {
if(isGameRunning){
int potValue = analogRead(potPin);
paddleX = map(potValue, 0, 1023, 0, SCREEN_WIDTH - PADDLE_WIDTH);
ballX += ballVelocityX;
ballY += ballVelocityY;
if (ballX <= 0 || ballX >= SCREEN_WIDTH - BALL_SIZE) {
ballVelocityX = -ballVelocityX;
ballX = max(0, min(ballX, SCREEN_WIDTH - BALL_SIZE));
}
if (ballY <= 0) {
ballVelocityY = -ballVelocityY;
ballY = max(ballY, 0);
}
if (ballY >= SCREEN_HEIGHT - BALL_SIZE) {
lives--;
updateLivesDisplay(); // LED'leri güncelle
if (lives <= 0) {
isGameRunning = false;
display.clearDisplay();
display.setTextSize(1);
if (darkMode) {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_BLACK);
display.setTextColor(SSD1306_WHITE);
} else {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
}
display.setCursor((SCREEN_WIDTH - 6 * 10) / 2, SCREEN_HEIGHT / 2 - 10); // Skoru merkezde göster
display.println(F("Skorunuz:"));
display.setCursor((SCREEN_WIDTH - 6 * 10) / 2, SCREEN_HEIGHT / 2);
display.println(score);
display.display();
delay(2000); // Skoru 2 saniye göster
// Ana menüye dön
menuIndex = 0;
display.ssd1306_command(SSD1306_DISPLAYON);
updateDisplay();
tmd.clearScreen();
return;
} else {
delay(3000);
resetBall();
}
}
if (ballY + BALL_SIZE >= paddleY && ballX + BALL_SIZE >= paddleX && ballX <= paddleX + PADDLE_WIDTH) {
int hitPoint = (ballX + BALL_SIZE / 2) - (paddleX + PADDLE_WIDTH / 2);
float impactRatio = hitPoint / (float)(PADDLE_WIDTH / 2);
ballVelocityY = -ballVelocityY;
ballVelocityX = ballVelocityX + (impactRatio * 2);
ballY = paddleY - BALL_SIZE;
}
display.clearDisplay();
// Topu çiz
if (darkMode) {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_BLACK);
display.setTextColor(SSD1306_WHITE);
display.fillCircle(ballX, ballY, BALL_SIZE, SSD1306_WHITE);
display.fillRect(paddleX, paddleY, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_WHITE);
bool brickHit = false;
for (int row = 0; row < NUM_BRICK_ROWS; row++) {
for (int col = 0; col < NUM_BRICKS_PER_ROW; col++) {
if (bricks[row][col][1] != -1) {
int brickX = bricks[row][col][0];
int brickY = bricks[row][col][1];
display.fillRect(brickX, brickY, BRICK_WIDTH - 1, BRICK_HEIGHT - 1, SSD1306_WHITE);
if (ballX + BALL_SIZE > brickX && ballX < brickX + BRICK_WIDTH &&
ballY + BALL_SIZE > brickY && ballY < brickY + BRICK_HEIGHT) {
bool hitCorner = false;
float cornerDistX = min(abs(ballX + BALL_SIZE / 2 - brickX), abs(ballX + BALL_SIZE / 2 - (brickX + BRICK_WIDTH)));
float cornerDistY = min(abs(ballY + BALL_SIZE / 2 - brickY), abs(ballY + BALL_SIZE / 2 - (brickY + BRICK_HEIGHT)));
if (cornerDistX * cornerDistX + cornerDistY * cornerDistY < BALL_SIZE * BALL_SIZE) {
hitCorner = true;
ballVelocityX = -ballVelocityX;
ballVelocityY = -ballVelocityY;
} else if (!hitCorner) {
float prevBallX = ballX - ballVelocityX;
float prevBallY = ballY - ballVelocityY;
bool hitVerticalSide = (prevBallY + BALL_SIZE > brickY) && (prevBallY < brickY + BRICK_HEIGHT);
bool hitHorizontalSide = (prevBallX + BALL_SIZE > brickX) && (prevBallX < brickX + BRICK_WIDTH);
if (hitVerticalSide && !hitHorizontalSide) {
ballVelocityX = -ballVelocityX; // Yatay çarpışma, X yönünü değiştir
} else if (!hitVerticalSide && hitHorizontalSide) {
ballVelocityY = -ballVelocityY; // Dikey çarpışma, Y yönünü değiştir
}
}
bricks[row][col][1] = -1;
brickHit = true;
if (brickHit) {
score++;
tmd.display(score);
bricksLeft--;
if (random(0, 10) == 0) {
for (int i = 0; i < 5; i++) {
if (!extraLives[i].isActive) {
extraLives[i].x = brickX + BRICK_WIDTH / 2;
extraLives[i].y = brickY + BRICK_HEIGHT;
extraLives[i].isActive = true;
break;
}
}
}
if (bricksLeft <= 0) {
nextLevel();
}
}
}
}
}
for (int i = 0; i < 5; i++) {
if (extraLives[i].isActive) {
display.fillCircle(extraLives[i].x, extraLives[i].y, 2, SSD1306_WHITE);
extraLives[i].y += 1;
if (extraLives[i].y >= paddleY && extraLives[i].x >= paddleX && extraLives[i].x <= paddleX + PADDLE_WIDTH) {
lives = min(lives + 1, 3);
extraLives[i].isActive = false;
updateLivesDisplay();
}
if (extraLives[i].y > SCREEN_HEIGHT) {
extraLives[i].isActive = false;
}
}
}
}
display.display();
delay(100);
} else {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.fillCircle(ballX, ballY, BALL_SIZE, SSD1306_BLACK);
display.fillRect(paddleX, paddleY, PADDLE_WIDTH, PADDLE_HEIGHT, SSD1306_BLACK);
bool brickHit = false;
for (int row = 0; row < NUM_BRICK_ROWS; row++) {
for (int col = 0; col < NUM_BRICKS_PER_ROW; col++) {
if (bricks[row][col][1] != -1) {
int brickX = bricks[row][col][0];
int brickY = bricks[row][col][1];
display.fillRect(brickX, brickY, BRICK_WIDTH - 1, BRICK_HEIGHT - 1, SSD1306_BLACK);
if (ballX + BALL_SIZE > brickX && ballX < brickX + BRICK_WIDTH &&
ballY + BALL_SIZE > brickY && ballY < brickY + BRICK_HEIGHT) {
bool hitCorner = false;
float cornerDistX = min(abs(ballX + BALL_SIZE / 2 - brickX), abs(ballX + BALL_SIZE / 2 - (brickX + BRICK_WIDTH)));
float cornerDistY = min(abs(ballY + BALL_SIZE / 2 - brickY), abs(ballY + BALL_SIZE / 2 - (brickY + BRICK_HEIGHT)));
if (cornerDistX * cornerDistX + cornerDistY * cornerDistY < BALL_SIZE * BALL_SIZE) {
hitCorner = true;
ballVelocityX = -ballVelocityX;
ballVelocityY = -ballVelocityY;
} else if (!hitCorner) {
float prevBallX = ballX - ballVelocityX;
float prevBallY = ballY - ballVelocityY;
bool hitVerticalSide = (prevBallY + BALL_SIZE > brickY) && (prevBallY < brickY + BRICK_HEIGHT);
bool hitHorizontalSide = (prevBallX + BALL_SIZE > brickX) && (prevBallX < brickX + BRICK_WIDTH);
if (hitVerticalSide && !hitHorizontalSide) {
ballVelocityX = -ballVelocityX;
} else if (!hitVerticalSide && hitHorizontalSide) {
ballVelocityY = -ballVelocityY;
}
}
bricks[row][col][1] = -1;
brickHit = true;
if (brickHit) {
score++;
tmd.display(score);
bricksLeft--;
if (random(0, 10) == 0) {
for (int i = 0; i < 5; i++) {
if (!extraLives[i].isActive) {
extraLives[i].x = brickX + BRICK_WIDTH / 2;
extraLives[i].y = brickY + BRICK_HEIGHT;
extraLives[i].isActive = true;
break;
}
}
}
if (bricksLeft <= 0) {
nextLevel();
}
}
}
}
}
for (int i = 0; i < 5; i++) {
if (extraLives[i].isActive) {
display.fillCircle(extraLives[i].x, extraLives[i].y, 2, SSD1306_BLACK);
extraLives[i].y += 1;
if (extraLives[i].y >= paddleY && extraLives[i].x >= paddleX && extraLives[i].x <= paddleX + PADDLE_WIDTH) {
lives = min(lives + 1, 3);
extraLives[i].isActive = false;
updateLivesDisplay();
}
if (extraLives[i].y > SCREEN_HEIGHT) {
extraLives[i].isActive = false;
}
}
}
}
display.display();
delay(100);
}
}
}
void nextLevel() {
currentLevel++;
if(currentLevel <= maxLevel){
display.clearDisplay();
display.setTextSize(1);
if (darkMode) {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_BLACK);
display.setTextColor(SSD1306_WHITE);
} else {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
}
display.setCursor((SCREEN_WIDTH - 6 * 10) / 2, SCREEN_HEIGHT / 2);
display.print(F("Level "));
display.print(currentLevel);
display.display();
delay(5000);
resetBricks();
resetBall();
ballVelocityX++;
ballVelocityY--;
}else{
display.clearDisplay();
display.setTextSize(1);
if (darkMode) {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_BLACK);
display.setTextColor(SSD1306_WHITE);
} else {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
}
display.setCursor((SCREEN_WIDTH - 6 * 10) / 2, SCREEN_HEIGHT / 2 - 10);
display.println(F("Oyun bitti!"));
display.println(F("Tebrikler!!"));
display.display();
delay(3000);
isGameRunning = false;
menuIndex = 0;
display.ssd1306_command(SSD1306_DISPLAYON);
updateDisplay();
}
}
void resetBricks() {
for (int row = 0; row < NUM_BRICK_ROWS; row++) {
for (int col = 0; col < NUM_BRICKS_PER_ROW; col++) {
bricks[row][col][1] = -1;
}
}
for (int row = 0; row < NUM_BRICK_ROWS; row++) {
for (int col = 0; col < NUM_BRICKS_PER_ROW; col++) {
if (random(0, 2) == 1) {
bricks[row][col][1] = row * BRICK_HEIGHT; // Y konumunu ayarla (görünür yap)
}
}
}
bricksLeft = 0;
for (int row = 0; row < NUM_BRICK_ROWS; row++) {
for (int col = 0; col < NUM_BRICKS_PER_ROW; col++) {
if (bricks[row][col][1] != -1) {
bricksLeft++;
}
}
}
}
void resetBall() {
ballX = paddleX + PADDLE_WIDTH / 2 - BALL_SIZE / 2;
ballY = paddleY - BALL_SIZE - 1;
ballVelocityY = -abs(ballVelocityY);
if (random(0, 2) == 0) {
ballVelocityX = -abs(ballVelocityX);
} else {
ballVelocityX = abs(ballVelocityX);
}
}
void updateLivesDisplay() {
digitalWrite(firstLife, LOW);
digitalWrite(secondLife, LOW);
digitalWrite(thirdLife, LOW);
if (lives >= 1) digitalWrite(firstLife, HIGH);
if (lives >= 2) digitalWrite(secondLife, HIGH);
if (lives >= 3) digitalWrite(thirdLife, HIGH);
}
void exitGame() {
isGameRunning = false;
display.clearDisplay();
display.setTextSize(1);
if (darkMode) {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_BLACK);
display.setTextColor(SSD1306_WHITE);
} else {
display.fillRect(0, 0, display.width(), display.height(), SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
}
display.setCursor((SCREEN_WIDTH - 6 * 10) / 2, SCREEN_HEIGHT / 2);
display.println(F("Hosca kalin!"));
display.display();
delay(2000);
display.ssd1306_command(SSD1306_DISPLAYOFF);
}