#include #include #include // Ustawienia wyświetlacza #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define SCREEN_ADDRESS 0x3C Adafruit_SH1106G display = Adafruit_SH1106G(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Piny przycisków #define PIN_UP 7 #define PIN_RIGHT 8 #define PIN_DOWN 9 #define PIN_LEFT 10 #define PIN_BTN1 5 #define PIN_BTN2 4 // Parametry gry #define PADDLE_WIDTH 24 #define PADDLE_HEIGHT 3 #define BALL_SIZE 3 #define BRICK_ROWS 4 #define BRICK_COLS 8 #define BRICK_WIDTH 14 #define BRICK_HEIGHT 5 #define BRICK_SPACING 2 #define STATUS_BAR_HEIGHT 10 // Zmienne gry float ballX, ballY; float ballDX, ballDY; int paddleX; int score = 0; bool bricks[BRICK_ROWS][BRICK_COLS]; int gameState = 0; int difficulty = 0; // 0 - Łatwy, 1 - Średni, 2 - Trudny float currentSpeed = 1.2; // Zmienne czasu i animacji int gameSeconds = 0; unsigned long lastMillis = 0; unsigned long blinkTimer = 0; bool blinkState = true; void setup() { Serial.begin(9600); randomSeed(analogRead(A0)); pinMode(PIN_UP, INPUT_PULLUP); pinMode(PIN_RIGHT, INPUT_PULLUP); pinMode(PIN_DOWN, INPUT_PULLUP); pinMode(PIN_LEFT, INPUT_PULLUP); pinMode(PIN_BTN1, INPUT_PULLUP); pinMode(PIN_BTN2, INPUT_PULLUP); if(!display.begin(SCREEN_ADDRESS, true)) { for(;;); } display.setRotation(2); display.clearDisplay(); showStartScreen(blinkState); } void loop() { if (gameState == 0) { if (millis() - blinkTimer > 400) { blinkTimer = millis(); blinkState = !blinkState; showStartScreen(blinkState); } if (digitalRead(PIN_BTN1) == LOW || digitalRead(PIN_BTN2) == LOW) { gameState = 5; showDifficultyScreen(); delay(300); } } else if (gameState == 5) { bool changed = false; if (digitalRead(PIN_LEFT) == LOW && difficulty > 0) { difficulty--; changed = true; } if (digitalRead(PIN_RIGHT) == LOW && difficulty < 2) { difficulty++; changed = true; } if (changed) { showDifficultyScreen(); delay(200); } if (digitalRead(PIN_BTN1) == LOW) { if (difficulty == 0) currentSpeed = 1.2; else if (difficulty == 1) currentSpeed = 2.2; else currentSpeed = 3.2; resetGame(); gameState = 1; delay(300); } } else if (gameState == 1) { if (digitalRead(PIN_BTN1) == LOW) { gameState = 4; showPauseScreen(); delay(300); } else if (digitalRead(PIN_BTN2) == LOW) { gameState = 0; blinkState = true; showStartScreen(blinkState); delay(300); } else { if (millis() - lastMillis >= 1000) { gameSeconds++; lastMillis = millis(); } updateGame(); if (gameState == 1) { drawGame(); } delay(15); } } else if (gameState == 2 || gameState == 3) { if (millis() - blinkTimer > 400) { blinkTimer = millis(); blinkState = !blinkState; if (gameState == 2) showEndScreen("KONIEC GRY", false, blinkState); else showEndScreen("WYGRANA!", true, blinkState); } if (digitalRead(PIN_BTN1) == LOW) { resetGame(); gameState = 1; delay(300); } else if (digitalRead(PIN_BTN2) == LOW) { gameState = 0; blinkState = true; showStartScreen(blinkState); delay(300); } } else if (gameState == 4) { if (digitalRead(PIN_BTN1) == LOW) { gameState = 1; lastMillis = millis(); delay(300); } else if (digitalRead(PIN_BTN2) == LOW) { gameState = 0; blinkState = true; showStartScreen(blinkState); delay(300); } } } void resetGame() { score = 0; gameSeconds = 0; lastMillis = millis(); paddleX = (SCREEN_WIDTH - PADDLE_WIDTH) / 2; ballX = random(SCREEN_WIDTH / 2 - 20, SCREEN_WIDTH / 2 + 20); ballY = SCREEN_HEIGHT - 18; if (random(0, 2) == 0) ballDX = currentSpeed; else ballDX = -currentSpeed; ballDY = -currentSpeed; for (int r = 0; r < BRICK_ROWS; r++) { for (int c = 0; c < BRICK_COLS; c++) { bricks[r][c] = true; } } } void updateGame() { if (digitalRead(PIN_LEFT) == LOW) paddleX -= 3; if (digitalRead(PIN_RIGHT) == LOW) paddleX += 3; if (paddleX < 0) paddleX = 0; if (paddleX > SCREEN_WIDTH - PADDLE_WIDTH) paddleX = SCREEN_WIDTH - PADDLE_WIDTH; ballX += ballDX; ballY += ballDY; // -- POPRAWKA: Wypychanie ze ścian bocznych i anty-pion -- if (ballX <= 0) { ballX = 0; // Wypchnięcie if (ballDX < 0) ballDX = -ballDX; // Odbicie w prawo if (ballDX < 0.3) ballDX = 0.3; // Uniemożliwienie idealnego pionu } else if (ballX >= SCREEN_WIDTH - BALL_SIZE) { ballX = SCREEN_WIDTH - BALL_SIZE; // Wypchnięcie if (ballDX > 0) ballDX = -ballDX; // Odbicie w lewo if (ballDX > -0.3) ballDX = -0.3; // Uniemożliwienie idealnego pionu } // -- POPRAWKA: Wypychanie z sufitu -- if (ballY <= STATUS_BAR_HEIGHT) { ballY = STATUS_BAR_HEIGHT; if (ballDY < 0) ballDY = -ballDY; // Wymuszenie lotu w dół } if (ballY >= SCREEN_HEIGHT) { gameState = 2; blinkState = true; blinkTimer = millis(); showEndScreen("KONIEC GRY", false, blinkState); return; } // Kolizja z paletką if (ballY + BALL_SIZE >= SCREEN_HEIGHT - PADDLE_HEIGHT - 2 && ballX + BALL_SIZE >= paddleX && ballX <= paddleX + PADDLE_WIDTH) { ballY = SCREEN_HEIGHT - PADDLE_HEIGHT - BALL_SIZE - 2; float hitPoint = (ballX - paddleX) / PADDLE_WIDTH; ballDX = (hitPoint - 0.5) * (currentSpeed * 2.0); // -- POPRAWKA: Jeśli piłka uderzy idealnie w środek paletki, lekko ją zaginamy -- if (ballDX >= 0 && ballDX < 0.3) ballDX = 0.3; if (ballDX < 0 && ballDX > -0.3) ballDX = -0.3; ballDY = -currentSpeed; } bool winCheck = true; for (int r = 0; r < BRICK_ROWS; r++) { for (int c = 0; c < BRICK_COLS; c++) { if (bricks[r][c]) { winCheck = false; int brickX = c * (BRICK_WIDTH + BRICK_SPACING) + 2; int brickY = r * (BRICK_HEIGHT + BRICK_SPACING) + STATUS_BAR_HEIGHT + 2; if (ballX + BALL_SIZE >= brickX && ballX <= brickX + BRICK_WIDTH && ballY + BALL_SIZE >= brickY && ballY <= brickY + BRICK_HEIGHT) { bricks[r][c] = false; score += 10; ballDY = -ballDY; goto endCollision; } } } } endCollision: if (winCheck) { gameState = 3; blinkState = true; blinkTimer = millis(); showEndScreen("WYGRANA!", true, blinkState); } } void drawGame() { display.clearDisplay(); display.drawLine(0, STATUS_BAR_HEIGHT, SCREEN_WIDTH, STATUS_BAR_HEIGHT, SH110X_WHITE); display.setTextSize(1); display.setCursor(2, 1); display.print(F("Pkt:")); display.print(score); display.setCursor(85, 1); int m = gameSeconds / 60; int s = gameSeconds % 60; if (m < 10) display.print("0"); display.print(m); display.print(":"); if (s < 10) display.print("0"); display.print(s); display.fillRect(paddleX, SCREEN_HEIGHT - PADDLE_HEIGHT - 2, PADDLE_WIDTH, PADDLE_HEIGHT, SH110X_WHITE); display.fillRect((int)ballX, (int)ballY, BALL_SIZE, BALL_SIZE, SH110X_WHITE); for (int r = 0; r < BRICK_ROWS; r++) { for (int c = 0; c < BRICK_COLS; c++) { if (bricks[r][c]) { int brickX = c * (BRICK_WIDTH + BRICK_SPACING) + 2; int brickY = r * (BRICK_HEIGHT + BRICK_SPACING) + STATUS_BAR_HEIGHT + 2; display.fillRect(brickX, brickY, BRICK_WIDTH, BRICK_HEIGHT, SH110X_WHITE); } } } display.display(); } void showStartScreen(bool glowText) { display.clearDisplay(); display.setTextSize(2); int x = 16; int y = 10; if (glowText) { display.setTextColor(SH110X_WHITE); display.setCursor(x - 1, y); display.print(F("BREAKOUT")); display.setCursor(x + 1, y); display.print(F("BREAKOUT")); display.setCursor(x, y - 1); display.print(F("BREAKOUT")); display.setCursor(x, y + 1); display.print(F("BREAKOUT")); display.setTextColor(SH110X_BLACK); display.setCursor(x, y); display.print(F("BREAKOUT")); } else { display.setTextColor(SH110X_WHITE); display.setCursor(x, y); display.print(F("BREAKOUT")); } display.setTextColor(SH110X_WHITE); display.setTextSize(1); display.setCursor(22, 38); display.println(F("ARDUINO EDITION")); display.setCursor(15, 52); display.println(F("Wcisnij 5 aby grac")); display.display(); } void showDifficultyScreen() { display.clearDisplay(); display.setTextColor(SH110X_WHITE); display.setTextSize(1); display.setCursor(20, 10); display.println(F("Wybierz poziom:")); // LATWY if (difficulty == 0) { display.fillRect(2, 30, 38, 15, SH110X_WHITE); display.setTextColor(SH110X_BLACK); } else { display.drawRect(2, 30, 38, 15, SH110X_WHITE); display.setTextColor(SH110X_WHITE); } display.setCursor(7, 34); display.print(F("LATWY")); // SREDNI if (difficulty == 1) { display.fillRect(42, 30, 42, 15, SH110X_WHITE); display.setTextColor(SH110X_BLACK); } else { display.drawRect(42, 30, 42, 15, SH110X_WHITE); display.setTextColor(SH110X_WHITE); } display.setCursor(46, 34); display.print(F("SREDNI")); // TRUDNY if (difficulty == 2) { display.fillRect(86, 30, 40, 15, SH110X_WHITE); display.setTextColor(SH110X_BLACK); } else { display.drawRect(86, 30, 40, 15, SH110X_WHITE); display.setTextColor(SH110X_WHITE); } display.setCursor(89, 34); display.print(F("TRUDNY")); display.setTextColor(SH110X_WHITE); display.setCursor(15, 55); display.print(F("<- L/P -> 5: Start")); display.display(); } void showEndScreen(const char* title, bool isWin, bool glowText) { display.clearDisplay(); display.setTextSize(2); int x = isWin ? 16 : 4; int y = 10; if (glowText) { display.setTextColor(SH110X_WHITE); display.setCursor(x - 1, y); display.print(title); display.setCursor(x + 1, y); display.print(title); display.setCursor(x, y - 1); display.print(title); display.setCursor(x, y + 1); display.print(title); display.setTextColor(SH110X_BLACK); display.setCursor(x, y); display.print(title); } else { display.setTextColor(SH110X_WHITE); display.setCursor(x, y); display.print(title); } display.setTextColor(SH110X_WHITE); display.setTextSize(1); display.setCursor(10, 38); display.print(F("PKT: ")); display.print(score); display.print(F(" CZAS: ")); display.print(gameSeconds); display.print(F("s")); display.setCursor(10, 55); display.println(F("5: Ponow | 4: Menu")); display.display(); } void showPauseScreen() { display.fillRect(34, 20, 60, 24, SH110X_BLACK); display.drawRect(34, 20, 60, 24, SH110X_WHITE); display.setTextColor(SH110X_WHITE); display.setTextSize(1); display.setCursor(48, 28); display.println(F("PAUZA")); display.display(); }