Components and supplies
1
Arduino Nano
1
Grove - Buzzer - Piezo
1
8*8 led matrix
2
Push Button
Tools and machines
1
Soldering kit
Apps and platforms
1
Arduino IDE
Project description
Code
code
cpp
...
1/*Arduino BREAKOUT Game on 8x8 Matrix WS2812b 2by mircemk, June 2025 3*/ 4 5#include <FastLED.h> 6 7#define LED_PIN 6 8#define NUM_LEDS 64 9#define MATRIX_WIDTH 8 10#define MATRIX_HEIGHT 8 11#define LEFT_BUTTON_PIN 9 12#define RIGHT_BUTTON_PIN 10 13#define BUZZER_PIN 2 14 15// Game constants 16#define MIN_X_SPEED 0.2 17#define MAX_X_SPEED 0.35 18#define MIN_Y_SPEED 0.25 19#define MAX_Y_SPEED 0.4 20#define SPEED_DECAY 0.98 21 22// Sound settings 23#define TONE_HIT 1200 24#define TONE_PADDLE 800 25#define TONE_WALL 1000 26#define TONE_GAMEOVER 400 27#define TONE_DURATION 15 28#define TONE_COOLDOWN 50 29 30CRGB leds[NUM_LEDS]; 31 32// Game elements 33int paddlePos = 3; 34int paddleWidth = 3; 35float ballX = 4; 36float ballY = 6; 37float ballSpeedX = 0.2; 38float ballSpeedY = -0.25; 39bool bricks[3][8]; 40CRGB brickColors[3][8]; 41int score = 0; 42int level = 1; 43bool gameOver = false; 44bool levelCompleted = false; 45bool newGame = false; 46bool displayScoreDone = false; 47 48// Timing control 49unsigned long lastFrameTime = 0; 50unsigned long lastSoundTime = 0; 51const unsigned long FRAME_TIME = 50; 52 53const byte SMILEY[8] = { 54 B00111100, 55 B01000010, 56 B10100101, 57 B10000001, 58 B10100101, 59 B10011001, 60 B01000010, 61 B00111100 62}; 63 64// Font definition for letters and numbers (5x7 font) 65const byte font[37][5] = { 66 {0x7C, 0x22, 0x22, 0x22, 0x7C}, // A 67 {0x7E, 0x4A, 0x4A, 0x4A, 0x34}, // B 68 {0x3C, 0x42, 0x42, 0x42, 0x24}, // C 69 {0x7E, 0x42, 0x42, 0x42, 0x3C}, // D 70 {0x7E, 0x4A, 0x4A, 0x4A, 0x42}, // E 71 {0x7E, 0x0A, 0x0A, 0x0A, 0x02}, // F 72 {0x3C, 0x42, 0x52, 0x52, 0x34}, // G 73 {0x7E, 0x08, 0x08, 0x08, 0x7E}, // H 74 {0x00, 0x42, 0x7E, 0x42, 0x00}, // I 75 {0x20, 0x40, 0x42, 0x3E, 0x02}, // J 76 {0x7E, 0x08, 0x14, 0x22, 0x42}, // K 77 {0x7E, 0x40, 0x40, 0x40, 0x40}, // L 78 {0x7E, 0x04, 0x18, 0x04, 0x7E}, // M 79 {0x7E, 0x04, 0x08, 0x10, 0x7E}, // N 80 {0x3C, 0x42, 0x42, 0x42, 0x3C}, // O 81 {0x7E, 0x12, 0x12, 0x12, 0x0C}, // P 82 {0x3C, 0x42, 0x52, 0x22, 0x5C}, // Q 83 {0x7E, 0x12, 0x32, 0x52, 0x0C}, // R 84 {0x24, 0x4A, 0x4A, 0x4A, 0x30}, // S 85 {0x02, 0x02, 0x7E, 0x02, 0x02}, // T 86 {0x3E, 0x40, 0x40, 0x40, 0x3E}, // U 87 {0x1E, 0x20, 0x40, 0x20, 0x1E}, // V 88 {0x3E, 0x40, 0x30, 0x40, 0x3E}, // W 89 {0x66, 0x18, 0x18, 0x18, 0x66}, // X 90 {0x06, 0x08, 0x70, 0x08, 0x06}, // Y 91 {0x62, 0x52, 0x4A, 0x46, 0x42}, // Z 92 {0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0 93 {0x00, 0x42, 0x7F, 0x40, 0x00}, // 1 94 {0x72, 0x49, 0x49, 0x49, 0x46}, // 2 95 {0x21, 0x41, 0x45, 0x4B, 0x31}, // 3 96 {0x18, 0x14, 0x12, 0x7F, 0x10}, // 4 97 {0x27, 0x45, 0x45, 0x45, 0x39}, // 5 98 {0x3E, 0x49, 0x49, 0x49, 0x32}, // 6 99 {0x01, 0x71, 0x09, 0x05, 0x03}, // 7 100 {0x36, 0x49, 0x49, 0x49, 0x36}, // 8 101 {0x26, 0x49, 0x49, 0x49, 0x3E}, // 9 102 {0x00, 0x00, 0x00, 0x00, 0x00} // Space 103}; 104 105// Function to get the index of a character in the font array 106int getCharIndex(char c) { 107 if (c >= 'A' && c <= 'Z') { 108 return c - 'A'; 109 } else if (c >= '0' && c <= '9') { 110 return c - '0' + 26; 111 } else { 112 return 36; // Space 113 } 114} 115 116// Function to scroll text on the LED matrix 117void scrollText(const char* text, int delayTime) { 118 int length = strlen(text); 119 for (int offset = 0; offset < length * 6 + MATRIX_WIDTH; offset++) { 120 fill_solid(leds, NUM_LEDS, CRGB::Black); 121 for (int i = 0; i < length; i++) { 122 int charIndex = getCharIndex(text[i]); 123 for (int col = 0; col < 5; col++) { 124 if (i * 6 + col - offset >= 0 && i * 6 + col - offset < MATRIX_WIDTH) { 125 byte column = font[charIndex][col]; 126 for (int row = 0; row < 7; row++) { 127 if (column & (1 << row)) { 128 leds[getPixelIndex(i * 6 + col - offset, row)] = CRGB::White; 129 } 130 } 131 } 132 } 133 } 134 FastLED.show(); 135 delay(delayTime); 136 } 137} 138 139void setup() { 140 FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); 141 FastLED.setBrightness(50); 142 143 pinMode(LEFT_BUTTON_PIN, INPUT_PULLUP); 144 pinMode(RIGHT_BUTTON_PIN, INPUT_PULLUP); 145 pinMode(BUZZER_PIN, OUTPUT); 146 delay (1000); 147 scrollText(" MINI BREAKOUT", 80); 148 initializeGame(); 149} 150 151void playSound(int frequency, int duration) { 152 noTone(BUZZER_PIN); // Ensure the buzzer is off before playing a new sound 153 tone(BUZZER_PIN, frequency, duration); 154 delay(duration); 155 noTone(BUZZER_PIN); // Turn off the buzzer after the duration 156} 157 158void initializeGame() { 159 FastLED.setBrightness(50); // Ensure consistent brightness 160 for (int row = 0; row < 3; row++) { 161 for (int col = 0; col < 8; col++) { 162 bricks[row][col] = true; 163 brickColors[row][col] = CHSV(random(0, 255), 255, 255); // Random color 164 } 165 } 166 167 paddlePos = 3; 168 ballX = 4.0; 169 ballY = 6.0; 170 ballSpeedX = 0.2; 171 ballSpeedY = -0.25; 172 score = 0; 173 level = 1; 174 paddleWidth = 3; 175 gameOver = false; 176 levelCompleted = false; 177 newGame = false; 178 displayScoreDone = false; 179 lastFrameTime = millis(); 180 lastSoundTime = 0; 181} 182 183void initializeLevel() { 184 for (int row = 0; row < 3; row++) { 185 for (int col = 0; col < 8; col++) { 186 bricks[row][col] = true; 187 brickColors[row][col] = CHSV(random(0, 255), 255, 255); // Random color 188 } 189 } 190 191 paddlePos = 3; 192 ballX = 4.0; 193 ballY = 6.0; 194 ballSpeedX = 0.2; 195 ballSpeedY = -0.25; 196 levelCompleted = false; 197 lastFrameTime = millis(); 198} 199 200void loop() { 201 unsigned long currentTime = millis(); 202 203 if (!gameOver && !newGame) { 204 if (currentTime - lastFrameTime >= FRAME_TIME) { 205 handleInput(); 206 updateGame(); 207 lastFrameTime = currentTime; 208 } 209 drawGame(); 210 FastLED.show(); 211 } else if (gameOver && !displayScoreDone) { 212 char scoreText[20]; 213 sprintf(scoreText, "SCORE: %d", score); 214 scrollText(scoreText, 100); 215 displayScoreDone = true; 216 displaySmiley(); 217 } else if (gameOver && displayScoreDone) { 218 if (digitalRead(LEFT_BUTTON_PIN) == LOW || digitalRead(RIGHT_BUTTON_PIN) == LOW) { 219 delay(300); 220 initializeGame(); 221 newGame = false; // Reset newGame for the next game cycle 222 displayScoreDone = false; 223 } 224 } 225} 226 227void handleInput() { 228 if (digitalRead(LEFT_BUTTON_PIN) == LOW && paddlePos > 0) { 229 paddlePos--; 230 } 231 if (digitalRead(RIGHT_BUTTON_PIN) == LOW && paddlePos < MATRIX_WIDTH - paddleWidth) { 232 paddlePos++; 233 } 234} 235 236void updateGame() { 237 ballX += ballSpeedX; 238 ballY += ballSpeedY; 239 240 // Brick collisions 241 bool allBricksDestroyed = true; 242 for (int row = 0; row < 3; row++) { 243 for (int col = 0; col < 8; col++) { 244 if (bricks[row][col]) { 245 allBricksDestroyed = false; 246 if (ballY >= row - 0.1 && ballY < row + 1.1 && 247 ballX >= col - 0.1 && ballX < col + 1.1) { 248 bricks[row][col] = false; 249 250 float dx = ballX - (col + 0.5); 251 float dy = ballY - (row + 0.5); 252 253 if (abs(dx) > abs(dy)) { 254 ballSpeedX = -ballSpeedX; 255 } else { 256 ballSpeedY = -ballSpeedY; 257 } 258 259 ballSpeedX += (random(-15, 16) / 100.0); 260 261 ballSpeedX = constrain(ballSpeedX, -MAX_X_SPEED, MAX_X_SPEED); 262 ballSpeedY = constrain(ballSpeedY, -MAX_Y_SPEED, MAX_Y_SPEED); 263 264 score += 1; 265 playSound(TONE_HIT, TONE_DURATION); 266 break; 267 } 268 } 269 } 270 } 271 272 // Check for level completion 273 if (allBricksDestroyed) { 274 levelCompleted = true; 275 level++; 276 if (level > 3) { 277 gameOver = true; 278 playSound(TONE_GAMEOVER, TONE_DURATION * 5); 279 } else { 280 if (level == 2) { 281 paddleWidth = 2; 282 } else if (level == 3) { 283 paddleWidth = 1; 284 } 285 initializeLevel(); 286 } 287 } 288 289 // Wall collisions 290 if (ballX <= 0 || ballX >= MATRIX_WIDTH - 1) { 291 ballSpeedX = -ballSpeedX; 292 ballX = (ballX <= 0) ? 0.1 : (MATRIX_WIDTH - 1.1); 293 294 if (abs(ballSpeedY) < MIN_Y_SPEED) { 295 ballSpeedY += (ballSpeedY > 0 ? MIN_Y_SPEED : -MIN_Y_SPEED) * 0.5; 296 } 297 298 playSound(TONE_WALL, TONE_DURATION); 299 } 300 301 if (ballY <= 0) { 302 ballSpeedY = abs(ballSpeedY); 303 ballY = 0.1; 304 playSound(TONE_WALL, TONE_DURATION); 305 } 306 307 // Paddle collision 308 if (ballY >= MATRIX_HEIGHT - 2 && ballY < MATRIX_HEIGHT - 1) { 309 if (ballX >= paddlePos && ballX < paddlePos + paddleWidth) { 310 ballY = MATRIX_HEIGHT - 2; 311 312 float hitPos = (ballX - paddlePos) / paddleWidth; 313 float angle = (hitPos - 0.5) * PI * 0.7; 314 315 float speed = sqrt(ballSpeedX * ballSpeedX + ballSpeedY * ballSpeedY); 316 ballSpeedX = sin(angle) * speed; 317 ballSpeedY = -abs(cos(angle) * speed); 318 319 if (abs(ballSpeedY) < MIN_Y_SPEED) { 320 ballSpeedY = -MIN_Y_SPEED; 321 } 322 323 playSound(TONE_PADDLE, TONE_DURATION); 324 } 325 } 326 327 // Check if the ball collapses next to paddle on the left or right side 328 if (ballY >= MATRIX_HEIGHT - 1 && (ballX < paddlePos || ballX >= paddlePos + paddleWidth)) { 329 gameOver = true; 330 playSound(TONE_GAMEOVER, TONE_DURATION * 5); 331 } 332 333 ballSpeedX *= SPEED_DECAY; 334 ballSpeedY *= SPEED_DECAY; 335 336 if (abs(ballSpeedX) < MIN_X_SPEED) { 337 ballSpeedX = (ballSpeedX < 0) ? -MIN_X_SPEED : MIN_X_SPEED; 338 } 339 if (abs(ballSpeedY) < MIN_Y_SPEED) { 340 ballSpeedY = (ballSpeedY < 0) ? -MIN_Y_SPEED : MIN_Y_SPEED; 341 } 342 343 ballSpeedX = constrain(ballSpeedX, -MAX_X_SPEED, MAX_X_SPEED); 344 ballSpeedY = constrain(ballSpeedY, -MAX_Y_SPEED, MAX_Y_SPEED); 345 346 if (ballY >= MATRIX_HEIGHT) { 347 gameOver = true; 348 playSound(TONE_GAMEOVER, TONE_DURATION * 5); 349 } 350} 351 352void drawGame() { 353 fill_solid(leds, NUM_LEDS, CRGB::Black); 354 355 // Draw bricks 356 for (int row = 0; row < 3; row++) { 357 for (int col = 0; col < 8; col++) { 358 if (bricks[row][col]) { 359 leds[getPixelIndex(col, row)] = brickColors[row][col]; 360 } 361 } 362 } 363 364 // Draw paddle 365 for (int i = 0; i < paddleWidth; i++) { 366 leds[getPixelIndex(paddlePos + i, MATRIX_HEIGHT - 1)] = CRGB::Blue; 367 } 368 369 // Draw ball 370 leds[getPixelIndex(int(ballX), int(ballY))] = CRGB::White; 371} 372 373void displaySmiley() { 374 fill_solid(leds, NUM_LEDS, CRGB::Black); 375 for (int row = 0; row < 8; row++) { 376 for (int col = 0; col < 8; col++) { 377 if (SMILEY[row] & (1 << (7 - col))) { 378 leds[getPixelIndex(col, row)] = CRGB::Yellow; 379 } 380 } 381 } 382 FastLED.show(); 383} 384 385int getPixelIndex(int x, int y) { 386 return y * MATRIX_WIDTH + x; 387}
Documentation
Schematic
...
Schematic.png

Comments
Only logged in users can leave comments