You'll see the arrays I set up for each level, they take up a lot of memory, I need ideas on a better way to do this.
I have not completed the game over routine so you can play forever with negative lives at the top.
I like the auto play where you can still play if you want. The computer never misses, but will sometimes get stuck in a loop with the ball going straight up and down.
Code: Select all
/***************************************************
Break Out by Joel Krueger 1-16-2023
Using an arduino Uno and an
Adafruit 2.8" TFT Capacitive touch screen sheild
https://www.adafruit.com/product/1947
****************************************************/
#include <Adafruit_GFX.h> // Core graphics library
#include <SPI.h> // this is needed for display
#include <Adafruit_ILI9341.h>
#include <Wire.h> // this is needed for FT6206
#include <Adafruit_FT6206.h>
// The FT6206 uses hardware I2C (SCL/SDA)
Adafruit_FT6206 ctp = Adafruit_FT6206();
// The display also uses hardware SPI, plus #9 & #10
#define TFT_CS 10
#define TFT_DC 9
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
int paddleX = 110;
int paddleY = 240;
int paddleLastX = 1;
int paddleW = 40;
int ballX = 110;
int ballY = paddleY;
int ballLastX = ballX;
int ballLastY = ballY;
int ballXDir = 0;
int ballXDirMax = 8;
int ballYDir = -5;
int ballRadius = 3;
unsigned long time;
unsigned long waitUntil;
int ballDelay = 20;
const int bricksTall = 12;
const int bricksWide = 11;
int totalBricks;
int bricksHit = 0;
unsigned int score = 0;
int lives = 3;
int level = 0;
const int highestLevel = 7;
// cycles per second variables
int cycleCount = 0;
int lastCycleCount = cycleCount;
long nextStep = millis() + 1000;
bool count = false;
bool debug = false;
bool autoPlay = false;
byte brick[bricksTall][bricksWide];
byte brickLevels[highestLevel][bricksTall][bricksWide] = {
{
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, //Level 1
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, //Level 2
{1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, //Level 3
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
},
{
{0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0}, //Level 4
{1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1},
{1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1},
{1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1},
{1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1},
{1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1},
{1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
},
{
{1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1}, //Level 5
{1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
{1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
{1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
{1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
{1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1},
{1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
},
{
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}, //Level 6
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
},
{
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, //Level 7
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
},
};
void setup() {
randomSeed(analogRead(A1));
while (ballXDir == 0) {
ballXDir = random(-4, 4);
}
if (debug) {
Serial.begin(9600);
}
tft.begin();
ctp.begin(40);
autoplayChoice();
loadBricks();
newScreen();
}
void loop() {
if (count) { //cycles per second routine
cycleCount++;
if (millis() > nextStep) {
//Serial.print("Cycles per second = ");
//Serial.println(cycleCount);
showCycles();
cycleCount = 0;
nextStep = millis() + 1000;
}
}
paddle();
time = millis();
if (time > waitUntil) {
waitUntil = time + ballDelay;
moveBall();
}
if (autoPlay) {
paddleX = ballX - (paddleW / 2);
}
}
void moveBall() {
//check if the ball hits the side walls
if (ballX < ballXDir * -1 + ballRadius + 1 | ballX > 238 - ballXDir - ballRadius) {
ballXDir = -ballXDir;
}
//check if the ball hits the top of the screen
if (ballY < ballYDir * -1 + ballRadius + 11) {
ballYDir = -ballYDir;
}
// /******************************************************
//check if ball hits bottom of bricks
for (int y = 0; y < bricksTall; y++) {
for (int x = 0; x < bricksWide; x++) {
if (brick[y][x] == 1) {
if (ballX > x * 20 + 10 - ballRadius & ballX < x * 20 + 29 + ballRadius
& ballY > y * 10 + 25 - ballRadius & ballY < y * 10 + 34 + ballRadius) {
tft.fillRect(x * 20 + 10, y * 10 + 25, 19, 9, ILI9341_BLACK);
brick[y][x] = 0;
if ((ballX < x * 20 + 10 & ballXDir > 0) | (ballX > x * 20 + 29 & ballXDir < 0)) {
ballXDir = -ballXDir;
} else {
ballYDir = -ballYDir;
}
bricksHit ++;
score = score + 10;
showScore();
break;
}
}
}
}
// *********************************************************/
//check if the ball hits the paddle
if (ballX > paddleX - ballXDir - ballRadius & ballX < paddleX + paddleW + ballXDir * -1 + ballRadius
& ballY > paddleY - ballYDir - ballRadius & ballY < paddleY + 4 + ballRadius) {
ballXDir = ballXDir - (((paddleX + paddleW / 2) - ballX) * .3);
if (ballXDir < -ballXDirMax) {
ballXDir = -ballXDirMax;
}
else if (ballXDir > ballXDirMax) {
ballXDir = ballXDirMax;
}
ballYDir = -ballYDir;
}
//check if the ball went past the paddle
if (ballY > paddleY + 10) {
for (int i = 0; i < 10; i++) {
tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(6);
tft.setCursor(50, 120);
tft.print("BANNED");
tft.setTextColor(ILI9341_RED);
tft.setCursor(50, 120);
tft.print("BANNED");
}
delay(1000);
lives--;
paddleLastX = 1;
newScreen();
ballY = paddleY;
ballLastY = ballY;
ballYDir = -5;
ballXDir = 4;
//bricksHit = 0;
//score = 0;
}
if (bricksHit == totalBricks) {
tft.fillCircle(ballX, ballY, ballRadius, ILI9341_BLACK);
tft.setTextColor(ILI9341_CYAN); tft.setTextSize(4);
tft.setCursor(30, 60);
tft.print("LEVEL " + String(level + 1));
tft.setTextColor(ILI9341_CYAN); tft.setTextSize(4);
tft.setCursor(20, 100);
tft.print("COMPLETE");
delay(2000);
level++;
if (level == highestLevel) {
level = 0;
}
ballDelay--;
loadBricks();
newScreen();
ballY = paddleY;
ballLastY = ballY;
ballYDir = -5;
ballXDir = 4;
bricksHit = 0;
}
ballX = ballX + ballXDir; //move the ball x
ballY = ballY + ballYDir; //move the ball y
tft.fillCircle(ballLastX, ballLastY, ballRadius, ILI9341_BLACK); //erase the old ball
tft.fillCircle(ballX, ballY, ballRadius, ILI9341_WHITE); // draw the new ball
ballLastX = ballX;
ballLastY = ballY;
}
void paddle() {
if (ctp.touched()) {
TS_Point p = ctp.getPoint(); // Retrieve a point
paddleX = map(p.x, 50, 189, 239, 1) - paddleW / 2;
}
if (paddleX < 1) { //dont let the paddle go to far left
paddleX = 1;
} else if (paddleX > 239 - paddleW) { //dont let the paddle go to far right
paddleX = 239 - paddleW;
}
if (paddleLastX != paddleX) {
tft.fillRect(paddleLastX, paddleY, paddleW, 4, ILI9341_BLACK); //erase the old paddle
tft.fillRect(paddleX, paddleY, paddleW, 4, ILI9341_CYAN); //draw the new paddle
paddleLastX = paddleX;
}
}
void newScreen() {
showBorder();
scoreBoard();
showLevel();
showLives();
showScore();
paddle();
showBricks();
if (! autoPlay) {
waitForUser();
}
}
void showBorder() {
tft.fillScreen(ILI9341_BLACK);
tft.drawFastVLine(0, 10, paddleY, ILI9341_YELLOW);
tft.drawFastHLine(0, 10, 239, ILI9341_YELLOW);
tft.drawFastVLine(239, 10, paddleY, ILI9341_YELLOW);
for (int i = 3; i < 240; i = i + 10) {
tft.drawFastHLine(i, paddleY + 10, 5, ILI9341_RED);
}
}
void scoreBoard() {
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); tft.setTextSize(1);
tft.setCursor(10, 0);
tft.print("Level");
tft.setCursor(90, 0);
tft.print("Lives");
tft.setCursor(160, 0);
tft.print("Score");
}
void showLevel() {
tft.setCursor(50, 0);
//tft.fillRect(50, 0, 18, 7, ILI9341_BLACK);
tft.print(String (level + 1));
}
void showLives() {
tft.setCursor(130, 0);
//tft.fillRect(130, 0, 18, 7, ILI9341_BLACK);
tft.print(String (lives));
}
void showScore() {
tft.setCursor(200, 0);
//tft.fillRect(200, 0, 30, 7, ILI9341_BLACK);
tft.print(String (score));
}
void showCycles() {
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK); tft.setTextSize(1);
tft.setCursor(5, 300);
//tft.fillRect(5, 300, 25, 8, ILI9341_BLACK);
tft.print(String (cycleCount)+" ");
}
void showBricks() {
for (int y = 0; y < bricksTall; y++) {
for (int x = 0; x < bricksWide; x++) {
if (brick[y][x] == 1) {
tft.fillRoundRect(x * 20 + 10, y * 10 + 25, 19, 9, 3, y * 4630 + 1630); //65535/12=5461 colors
tft.fillRoundRect(x * 20 + 14, y * 10 + 28, 11, 3, 1, y * 4630 + 6260); //65535/12=5461 colors
}
if (debug) {
Serial.print(level);
Serial.print(" ");
Serial.print(y);
Serial.print(" ");
Serial.print(x);
Serial.print(" ");
Serial.print(brick[y][x]);
Serial.print(" ");
Serial.print(brickLevels[level][y][x]);
Serial.print(" ");
Serial.println(totalBricks);
}
}
}
}
void loadBricks() {
totalBricks = 0;
for (int y = 0; y < bricksTall; y++) {
for (int x = 0; x < bricksWide; x++) {
brick[y][x] = brickLevels[level][y][x];
if (brick[y][x] == 1) {
totalBricks++;
}
}
}
}
void waitForUser() {
tft.setTextColor(ILI9341_YELLOW); tft.setTextSize(2);
tft.setCursor(30, 280);
tft.print("TAP TO CONTINUE");
while (! ctp.touched()) {
//waiting for touch screen to be pressed
}
tft.setTextColor(ILI9341_BLACK);
tft.setCursor(30, 280);
tft.print("TAP TO CONTINUE");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(1);
}
void autoplayChoice() {
showBorder();
scoreBoard();
showLevel();
showLives();
showScore();
paddle();
tft.fillRoundRect(25, 40, 190, 60, 9, ILI9341_BLUE); //65535/12=5461 colors
tft.drawRoundRect(25, 40, 190, 60, 9, ILI9341_WHITE); //65535/12=5461 colors
tft.fillRoundRect(25, 140, 190, 60, 9, ILI9341_BLUE); //65535/12=5461 colors
tft.drawRoundRect(25, 140, 190, 60, 9, ILI9341_WHITE); //65535/12=5461 colors
tft.setTextColor(ILI9341_GREEN); tft.setTextSize(3);
tft.setCursor(40, 60);
tft.print("Play Game");
tft.setTextColor(ILI9341_MAGENTA);
tft.setCursor(40, 160);
tft.print("Auto Play");
tft.setTextColor(ILI9341_YELLOW);
tft.setCursor(50, 280);
tft.print("BREAKOUT");
while (true) {
if (ctp.touched()) {
TS_Point p = ctp.getPoint(); // Retrieve a point
int XPOS = map(p.x, 10, 230, 239, 0);
int YPOS = map(p.y, 10, 310, 319, 0);
if (XPOS > 25 & XPOS < 215) {
if (YPOS > 40 & YPOS < 100) {
return;
} else if (YPOS > 140 & YPOS < 200) {
autoPlay = true;
return;
}
}
}
}
}