Week 13: Final Project

 

Project concept:

For my final project, I stuck closely to the original concept and developed a straightforward yet engaging game. The premise involves safeguarding a car navigating a road from descending obstacles. Users are tasked to protect the car by moving it left and right using the buttons on the Arduino board. Failure to shield the car results is a game over, while successfully safeguarding it from at least 10 obstacles leads to victory. I introduced an element of challenge by ensuring that the car moves at a fast pace, adding a layer of difficulty for users as they skillfully escape the approaching obstacles.

User testing:

I tested the game with my friend Javeria, and given its straightforward design with limited options, she quickly understood the mechanics. During the experience, she said she was able to grasp the functionality effortlessly without any guidance. Her understanding of the game highlighted its user-friendly nature and accessibility.

Challenging part:

The core functionality of the game coding operates smoothly, demonstrating performance without encountering any errors related to its main features. Despite facing multiple challenges, the essential gameplay elements functioned flawlessly. However, the area requiring further improvement lies in the transition between pages. The aspect of navigating between different sections of the game could benefit from some enhancements.

I intentionally excluded the idea of an instruction page, as I, reflecting on my own experiences as a child, preferred the thrill of figuring things out independently. However, recognizing that not everyone shares this perspective, users may need guidance on understanding the fundamental operations of the game. 

For the arduino I implemented simple 2 switches coding. Initially my p5 coding was the movement of the car to right and left using the keys of the keyboard. But by utilizing the readSerial() function as the communication link between p5.js and Arduino, the Arduino code is structured to retrieve the statuses of two switches and transmit these values to p5.js.

Arduino code:

const int switch1Pin = 4;  // Replace with the actual pin for switch 1
const int switch2Pin = 8;  // Replace with the actual pin for switch 2

void setup() {
  Serial.begin(9600);
  pinMode(switch1Pin, INPUT_PULLUP);
  pinMode(switch2Pin, INPUT_PULLUP);
  

  while (Serial.available() <= 0 ){
    Serial.println("0,0");
    delay(300);
  }
}

void loop() {


  while(Serial.available()) {
    if (Serial.read() == '\n') {
      int switch1State = digitalRead(switch1Pin);
      delay(5);
      int switch2State = digitalRead(switch2Pin);
      
      Serial.print(switch1State);
      Serial.print(',');
      Serial.println(switch2State);
    }
  }


  // if (switch1State == LOW) {
  //   // Switch 1 is clicked, set output to 1
  //   Serial.println("1");
  //   while (digitalRead(switch1Pin) == LOW) {
  //     // Wait until switch 1 is released
  //   }
  // } else if (switch2State == LOW) {
  //   // Switch 2 is clicked, set output to 0
  //   Serial.println("0");
  //   while (digitalRead(switch2Pin) == LOW) {
  //     // Wait until switch 2 is released
  //   }
  // }
}

p5.js code:

let img;
let rock;
let bg;
let car;
let obstacles = [];
let score = 0;
let bgSpeed = 2; // Background scrolling speed
let y1 = 0;
let y2;
let switch1State, switch2State;
let start;
let restart;
let gameStarted = false;
let gameOver = false;
let gameWon = false;
let winThreshold = 5;
let win;
let music;

function preload() {
  img = loadImage('pinkcarsss.png');
  rock = loadImage('rockss.png');
  bg = loadImage('backgroundroad.png');
  start = loadImage('startpage123.png');
  restart = loadImage('restartpage.png');
  win = loadImage('winpage123.png');
  music = loadSound('gamemusic.mp3');
}


function setup() {
  createCanvas(500, 600);
  car = new Car();
  y2 = height;
  music.play();
}

function draw() {
  background(250);

  // displaying of pages according to win/lose
  if (gameWon) {
    // Player wins
    drawWinPage();
  } else if (gameOver) {
    // Player loses
    drawLosePage();
  } else {
    // Display the start page
    image(start, 0, 0, width, height);

    if (gameStarted) {
      drawGame();
    }
  }
}

function drawWinPage() {
  image(win, 0, 0, width, height);
}

function drawLosePage() {
  image(restart, 0, 0, width, height);
}

function restartGame() {
  gameOver = false;
  gameStarted = false;
  score = 0;
  obstacles = [];
  setupGame();
}

function winGame() {
  gameWon = true;
  gameOver = false;
  gameStarted = false;
}

function mousePressed() {
  if (gameOver || gameWon) {
    if (mouseX > 200 && mouseX < 328 && mouseY > 235 && mouseY < 300) {
      restartGame();
    }
  } else if (!gameStarted) {
    if (mouseX > 200 && mouseX < 328 && mouseY > 235 && mouseY < 300) {
      gameStarted = true;
      setupGame();
    }
  }
}

function drawGame() {
  y1 += bgSpeed;
  y2 += bgSpeed;

  if (y1 > height) {
    y1 = -height;
  }
  if (y2 > height) {
    y2 = -height;
  }

  // Draw background images
  image(bg, 0, y1, width, height);
  image(bg, 0, y2, width, height);

  car.show();
  car.move();

  if (frameCount % 80 === 0) {
    obstacles.push(new Obstacle());
  }

  for (let obstacle of obstacles) {
    obstacle.show();
    obstacle.move();

    if (car.hits(obstacle)) {
      gameOver = true;
    }

    if (obstacle.offscreen()) {
      score++;
      obstacles.shift();
    }
  }

  if (score >= winThreshold) {
    winGame();
  }

  // score
  showScore();
}

function setupGame() {
  obstacles = [];
  score = 0;
  y1 = 0;
  y2 = height;
  car = new Car();
  gameStarted = true;
  gameOver = false;
  gameWon = false;
}

function showScore() {
  fill(0);
  textSize(17);
  text(`Score: ${score}`, 20, 20);
}

class Car {
  constructor() {
    this.w = 80;
    this.h = 90;
    this.x = width / 2 - this.w / 2;
    this.y = height / 2 - this.h / 2;
  }

  show() {
    fill(0, 255, 0);
    image(img, this.x, this.y, this.w, this.h);
  }

  move() {
    // Car moves automatically in the vertical direction
    this.y -= 3;

    // Reset car's position when it goes off the top
    if (this.y < -this.h) {
      this.y = height - this.h - 20;
    }
  }

  moveLeft() {
    this.x -= 10;
  }

  moveRight() {
    this.x += 10;
  }

  hits(obstacle) {
    return (
      this.x < obstacle.x + obstacle.w &&
      this.x + this.w > obstacle.x &&
      this.y < obstacle.y + obstacle.h &&
      this.y + this.h > obstacle.y
    );
  }
}

class Obstacle {
  constructor() {
    this.w = 40;
    this.h = 50;
    this.x = random(width - this.w);
    this.y = -this.h;
  }

  show() {
    fill(255, 0, 0);
    image(rock, this.x, this.y, this.w, this.h);
  }

  move() {
    this.y += 5;
  }

  offscreen() {
    return this.y > height;
  }

As for the p5, it contains the main logic of the game, with four different pages. There are multiple images added and also the elements like moving car and the rock are all png images. A happy music was also implemented in p5.

Looking ahead, I aspire to elevate the game beyond its current straightforward nature, by infusing it with more excitement and thrill. In terms of future enhancements, my goal is to inject more excitement into the game, moving beyond its current straightforward design. I’m also eager to explore and incorporate additional physical elements, further enhancing the interactive and immersive aspects of the gaming experience.

Video:

Leave a Reply