#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define BUTTON_GAS_PIN 2 // Gas button pin
#define BUTTON_BRAKE_PIN 3 // Brake button pin
#define BUTTON_LEFT_PIN 4 // Left button pin
#define BUTTON_RIGHT_PIN 5 // Right button pin
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
int carX = SCREEN_WIDTH - 30;
int carY = SCREEN_HEIGHT - 20;
bool moving = false;
bool gameStarted = false;
float speed = 0.0;
const float maxSpeed = 20.0; // Max speed increased for higher km/h
const float acceleration = 0.5; // Adjusted for faster acceleration
const float deceleration = 1.0; // Faster deceleration for braking
float roadOffset = 0;
bool gameOver = false;
unsigned long gameStartTime = 0;
float distanceDriven = 0.0; // Distance driven in meters
float highScore = 0.0; // High score in meters
struct Obstacle {
int x;
int y;
float baseSpeed;
float speed;
};
Obstacle obstacles[] = {
{35, -10, 2.0, 2.0}, {65, -30, 3.0, 3.0}, {95, -50, 4.0, 4.0}
};
const int numObstacles = sizeof(obstacles) / sizeof(obstacles[0]);
void setup() {
// Initialize the display
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
display.clearDisplay();
// Initialize buttons
pinMode(BUTTON_GAS_PIN, INPUT_PULLUP);
pinMode(BUTTON_BRAKE_PIN, INPUT_PULLUP);
pinMode(BUTTON_LEFT_PIN, INPUT_PULLUP);
pinMode(BUTTON_RIGHT_PIN, INPUT_PULLUP);
}
void drawRoad() {
display.clearDisplay();
// Draw side roads
display.drawLine(20, 0, 20, SCREEN_HEIGHT, SSD1306_WHITE);
display.drawLine(108, 0, 108, SCREEN_HEIGHT, SSD1306_WHITE);
// Draw middle road lines
for (int i = 0; i < SCREEN_HEIGHT; i += 20) {
display.drawLine(64, i + (int)roadOffset, 64, i + 10 + (int)roadOffset, SSD1306_WHITE);
}
// Draw lane boundaries
display.drawLine(20, 0, 20, SCREEN_HEIGHT, SSD1306_WHITE);
display.drawLine(108, 0, 108, SCREEN_HEIGHT, SSD1306_WHITE);
}
void drawHouses() {
// Draw left house with more details
for (int i = 0; i < SCREEN_HEIGHT; i += 40) {
int houseX = 0;
int houseY = i + (int)roadOffset;
// Main house body
display.fillRect(houseX, houseY, 15, 15, SSD1306_WHITE);
// Roof
display.fillTriangle(houseX, houseY, houseX + 15, houseY, houseX + 7, houseY - 10, SSD1306_WHITE);
// Windows
display.drawRect(houseX + 2, houseY + 2, 5, 5, SSD1306_BLACK);
display.drawRect(houseX + 8, houseY + 2, 5, 5, SSD1306_BLACK);
// Door
display.drawRect(houseX + 5, houseY + 10, 5, 5, SSD1306_BLACK);
// Trees near the house
int treeX = houseX - 10;
int treeY = houseY + 10;
// Tree trunk
display.fillRect(treeX, treeY, 3, 6, SSD1306_WHITE);
// Tree foliage
display.fillCircle(treeX + 1, treeY - 2, 5, SSD1306_WHITE);
display.fillCircle(treeX - 3, treeY, 5, SSD1306_WHITE);
display.fillCircle(treeX + 5, treeY, 5, SSD1306_WHITE);
}
// Draw right house with more details
for (int i = 0; i < SCREEN_HEIGHT; i += 40) {
int houseX = 113;
int houseY = i + 20 + (int)roadOffset;
// Main house body
display.fillRect(houseX, houseY, 15, 15, SSD1306_WHITE);
// Roof
display.fillTriangle(houseX, houseY, houseX + 15, houseY, houseX + 7, houseY - 10, SSD1306_WHITE);
// Windows
display.drawRect(houseX + 2, houseY + 2, 5, 5, SSD1306_BLACK);
display.drawRect(houseX + 8, houseY + 2, 5, 5, SSD1306_BLACK);
// Door
display.drawRect(houseX + 5, houseY + 10, 5, 5, SSD1306_BLACK);
// Trees near the house
int treeX = houseX + 22;
int treeY = houseY + 10;
// Tree trunk
display.fillRect(treeX, treeY, 3, 6, SSD1306_WHITE);
// Tree foliage
display.fillCircle(treeX + 1, treeY - 2, 5, SSD1306_WHITE);
display.fillCircle(treeX - 3, treeY, 5, SSD1306_WHITE);
display.fillCircle(treeX + 5, treeY, 5, SSD1306_WHITE);
}
}
void drawCar(int x, int y, bool isMainCar) {
if (isMainCar) {
// Draw main car (smaller size)
display.fillRect(x, y, 8, 12, SSD1306_WHITE); // Car body
display.fillRect(x + 1, y - 1, 6, 1, SSD1306_WHITE); // Car hood
display.fillRect(x + 1, y + 12, 6, 1, SSD1306_WHITE); // Car rear
// Draw wheels
display.fillRect(x + 1, y + 2, 1, 1, SSD1306_BLACK); // Front left wheel
display.fillRect(x + 6, y + 2, 1, 1, SSD1306_BLACK); // Front right wheel
display.fillRect(x + 1, y + 9, 1, 1, SSD1306_BLACK); // Rear left wheel
display.fillRect(x + 6, y + 9, 1, 1, SSD1306_BLACK); // Rear right wheel
} else {
// Draw obstacle car (smaller size)
display.fillRect(x, y, 8, 12, SSD1306_WHITE); // Car body
display.fillRect(x + 1, y - 1, 6, 1, SSD1306_WHITE); // Car hood
display.fillRect(x + 1, y + 12, 6, 1, SSD1306_WHITE); // Car rear
// Draw wheels
display.fillRect(x + 1, y + 2, 1, 1, SSD1306_BLACK); // Front left wheel
display.fillRect(x + 6, y + 2, 1, 1, SSD1306_BLACK); // Front right wheel
display.fillRect(x + 1, y + 9, 1, 1, SSD1306_BLACK); // Rear left wheel
display.fillRect(x + 6, y + 9, 1, 1, SSD1306_BLACK); // Rear right wheel
}
}
void drawSpeedometer(float speed) {
int speedKmH = speed * 10; // Convert to km/h for display
display.drawRect(0, 0, 64, 16, SSD1306_WHITE); // Draw box
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(2, 4);
display.print("Speed: ");
display.print(speedKmH);
display.print(" km/h");
// Display gear level
display.setCursor(2, 12);
if (speedKmH <= 50) {
display.print("Gear: 1");
} else if (speedKmH <= 100) {
display.print("Gear: 2");
} else if (speedKmH <= 150) {
display.print("Gear: 3");
} else if (speedKmH <= 200) {
display.print("Gear: 4");
} else {
display.print("Gear: 5");
}
}
void drawDistance(float distance) {
display.setCursor(70, 0);
display.print("Dist: ");
display.print(distance);
display.print(" m");
}
void drawHighScore(float score) {
display.setCursor(70, 8);
display.print("High: ");
display.print(score);
display.print(" m");
}
void drawGameOver() {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15, 15); // Adjusted position to make space for the message
display.print("GAME OVER");
display.setTextSize(1);
display.setCursor(5, 45); // Positioned at the bottom
display.print("Press the Gas Button");
display.display();
}
bool checkCollision(int x1, int y1, int x2, int y2, int w, int h) {
return (x1 < x2 + w && x1 + w > x2 && y1 < y2 + h && y1 + h > y2);
}
void resetGame() {
carX = SCREEN_WIDTH - 30;
carY = SCREEN_HEIGHT - 20;
moving = false;
gameStarted = false;
speed = 0.0;
roadOffset = 0;
gameOver = false;
gameStartTime = 0;
distanceDriven = 0.0;
for (int i = 0; i < numObstacles; i++) {
obstacles[i].x = random(21, 107);
obstacles[i].y = -15 * (i + 1);
obstacles[i].baseSpeed = random(1, 3);
obstacles[i].speed = obstacles[i].baseSpeed;
}
}
void loop() {
if (gameOver) {
drawGameOver();
if (digitalRead(BUTTON_GAS_PIN) == LOW) {
if (distanceDriven > highScore) {
highScore = distanceDriven;
}
resetGame();
}
return;
}
// Check button states
if (digitalRead(BUTTON_GAS_PIN) == LOW) {
if (!gameStarted) {
gameStarted = true; // Start the game when the gas button is pressed for the first time
gameStartTime = millis(); // Record the game start time
}
if (speed < maxSpeed) {
speed += acceleration;
}
moving = true;
} else if (digitalRead(BUTTON_BRAKE_PIN) == LOW) {
speed -= deceleration;
if (speed < 0) {
speed = 0;
moving = false;
}
} else {
if (speed > 0) {
speed -= deceleration * 0.5;
if (speed < 0) {
speed = 0;
moving = false;
}
}
}
if (digitalRead(BUTTON_LEFT_PIN) == LOW && carX > 21) {
carX -= 2; // Move left
if (carX < 21) carX = 21; // Keep car within left side limit
} else if (digitalRead(BUTTON_RIGHT_PIN) == LOW && carX < 108 - 10) {
carX += 2; // Move right
if (carX > 108 - 10) carX = 108 - 10; // Keep car within right side limit
}
if (gameStarted) {
unsigned long currentTime = millis();
if (currentTime - gameStartTime > 3000) { // Check if 3 seconds have passed since the game started
for (int i = 0; i < numObstacles; i++) {
obstacles[i].speed = obstacles[i].baseSpeed + speed * 0.01; // Adjust multiplier as needed
obstacles[i].y += obstacles[i].speed;
if (obstacles[i].y > SCREEN_HEIGHT) {
obstacles[i].x = random(21, 107);
obstacles[i].y = -15 * (i + 1);
obstacles[i].baseSpeed = random(1, 3); // Adjust range as needed
}
if (obstacles[i].x == 64) { // Check if obstacle is on the middle line
obstacles[i].x += (obstacles[i].x == 64) ? 10 : -10; // Move away from the middle line
}
if (checkCollision(carX, carY, obstacles[i].x, obstacles[i].y, 8, 12)) { // Smaller collision size
gameOver = true;
break;
}
}
}
roadOffset += speed * 0.1;
if (roadOffset > 20) {
roadOffset -= 20;
}
distanceDriven += speed * 0.01; // Update distance based on speed
drawRoad();
drawHouses(); // Draw more realistic houses with trees and windows
drawCar(carX, carY, true); // Draw the main car
if (currentTime - gameStartTime > 3000) { // Draw obstacles only if 3 seconds have passed
for (int i = 0; i < numObstacles; i++) {
drawCar(obstacles[i].x, obstacles[i].y, false); // Draw obstacle cars
}
}
drawSpeedometer(speed);
drawDistance(distanceDriven); // Display distance
drawHighScore(highScore); // Display high score
display.display();
}
}