#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeSerif9pt7b.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define potPin1 A3
#define potPin2 A2
int count = 0;
int enemyPositionX = 6;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
const unsigned char Car[] PROGMEM = {
0x7c, 0xfe, 0xfe, 0x7c, 0x7c, 0xfe, 0xfe, 0x7c
};
const unsigned char roadAnimation[3][230] PROGMEM = {
// 'road line 1', 33x46px
{
0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88,
0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88,
0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88,
0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88,
0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88,
0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88,
0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88,
0x80, 0x88, 0x88, 0x88, 0x88, 0x80
},
// 'road line 2', 33x46px
{
0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88,
0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88,
0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88,
0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88,
0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80,
0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88,
0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88,
0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88,
0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x88, 0x88, 0x88, 0x88, 0x80
},
// 'road 3', 33x46px
{
0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88,
0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88,
0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88,
0x88, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88,
0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80,
0x88, 0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88,
0x88, 0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88,
0x88, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88,
0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x88, 0x88, 0x88, 0x88, 0x80, 0x88, 0x88, 0x88, 0x88,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80
}
};
const unsigned char Beaker[] PROGMEM = {
// 'beaker', 12x15px
0x40, 0x20, 0xa0, 0x50, 0xbf, 0xd0, 0xb9, 0xd0, 0xb1, 0xd0, 0xb9, 0xd0, 0xb9, 0xd0, 0x79, 0xe0,
0x30, 0xc0, 0x1f, 0x80, 0x0f, 0x00, 0x06, 0x00, 0x06, 0x00, 0x1f, 0x80, 0x3f, 0xc0
};
struct Obstacle {
int startX;
int x;
int y;
bool active;
unsigned long waitTime;
bool isMoving;
};
Obstacle obstacles[8];
void setup() {
Serial.begin(115200);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 allocation failed");
for (;;);
}
randomSeed(analogRead(A0));
startGame();
initializingObstacles();
}
void loop() {
display.clearDisplay();
setupGame();
display.drawBitmap(5, 1, roadAnimation[count / 3], 37, 46, WHITE);
display.drawBitmap(45, 1, roadAnimation[count / 3], 34, 46, WHITE);
unsigned long currentMillis = millis();
static int car1X = 5;
static int car2X = 45;
car1X = map(analogRead(potPin1), 0, 1023, 6, 30);
car2X = map(analogRead(potPin2), 0, 1023, 46, 70);
for (int i = 0; i < 8; i++) {
if (obstacles[i].active) {
if (!obstacles[i].isMoving) {
if (obstacles[i].waitTime == 0) {
obstacles[i].waitTime = currentMillis + random(1000, 10000);
}
if (currentMillis >= obstacles[i].waitTime) {
obstacles[i].isMoving = true;
obstacles[i].y = -8;
}
} else {
obstacles[i].y++;
if (obstacles[i].y > 47) {
obstacles[i].y = -8;
obstacles[i].waitTime = currentMillis + random(2000, 11000);
obstacles[i].isMoving = false;
}
}
display.drawBitmap(obstacles[i].x, obstacles[i].y, Car, 7, 8, WHITE);
if (obstacles[i].isMoving && checkCollision(car1X, 38, obstacles[i].x, obstacles[i].y)) {
gameOver(2);
}
/*
if (obstacles[i].isMoving && checkCollision(car2X, 38, obstacles[i].x, obstacles[i].y)) {
gameOver(1);
}
*/
}
}
displayCar(car1X, 38);
displayCar(car2X, 38);
count++;
if (count == 9) {
count = 0;
}
delay(40);
display.display();
}
bool checkCollision(int carX, int carY, int obstacleX, int obstacleY) {
return (carX < obstacleX + 7 && carX + 7 > obstacleX && carY < obstacleY + 8 && carY + 8 > obstacleY);
}
void displayCar(int x, int y) {
display.drawBitmap(x, y, Car, 7, 8, WHITE);
}
void startGame() {
display.clearDisplay();
display.invertDisplay(true);
display.setTextSize(1);
display.setTextColor(WHITE);
//display.setRotation(2);
display.drawRect(0, 0, 84, 48, WHITE);
display.setCursor(10,16);
display.println("CARS CRASH");
display.setCursor(7,32);
display.println("1P");
display.setCursor(60,32);
display.println("2P");
display.display();
delay(3000);
}
void setupGame() {
display.drawRect(0, 0, 84, 48, WHITE);
display.fillRect(1, 1, 4, 46, WHITE);
display.fillRect(38, 1, 7, 46, WHITE);
display.fillRect(78, 1, 5, 46, WHITE);
}
void gameOver(int PlayerNumber) {
display.clearDisplay();
display.drawRect(0, 0, 84, 48, WHITE);
display.setTextSize(1);
display.setCursor(18, 6);
display.drawBitmap(32, 18, Beaker, 12, 15, WHITE);
if (PlayerNumber == 1) {
display.println("1P WINS");
} else {
display.println("2P WINS");
}
display.display();
while (true);
}
void initializingObstacles() {
for (int i = 0; i < 8; i++) {
if (i == 4) {
obstacles[i].startX = enemyPositionX + 8;
obstacles[i].x = enemyPositionX + 8;
obstacles[i].y = -8;
obstacles[i].active = true;
obstacles[i].waitTime = 0;
obstacles[i].isMoving = false;
enemyPositionX += 16;
} else {
obstacles[i].startX = enemyPositionX;
obstacles[i].x = enemyPositionX;
obstacles[i].y = -8;
obstacles[i].active = true;
obstacles[i].waitTime = 0;
obstacles[i].isMoving = false;
enemyPositionX += 8;
}
}
}