Final Project: Hello Kitty’s Adventure: The Car Dodging Challenge

Hello Kitty’s Adventure: The Car Dodging Challenge

I chose this project because for my midterm, I created an experience and for my final, I aimed to challenge myself by creating a game. Additionally, having received my license two months ago, it seemed like the perfect opportunity to develop a game involving cars.

This game, titled “Hello Kitty’s Adventure: The Car Dodging Challenge” lets you control a character resembling Hello Kitty, guiding her left and right to avoid colliding with cars. As you skillfully maneuver past every four cars, the game progresses to a new level, increasing in speed. Your score accumulates with each second you survive. This fun and engaging game is inspired by my long-time fondness for a character similar to Hello Kitty, which has been a favorite of mine since childhood.

P5 Structure : IMG_5852

How the Game Works : IMG_5846

prototype + Schematic (ultrasonic distance sensor, two LEDs) :

Exhibition

The Im showcase on Friday 15th was a blast! It was my first time being part of an exhibition and making a game, which turned out to be super fun. I really enjoyed it, especially chatting with people there. I got loads of compliments and comments on my project, like “Your project was the best,” “Did you make this? It’s amazing,” and “This game is addicting.” It feels great to see my hard work pay off. The course was fun and I’ve been  proud of  everything i achieved during the course. My friends showed up to support me, and seeing people smile while playing my game was the best part. Zion and Fausto even set the highest score at 77! Here are some pictures and videos from the showcase.

rose playing –> My friend Rose Playing my game

 

Structure + Challenges

The structure of the project is crafted using recycled cardboard and cork that I found at home, a gesture to honor COP28 held in the UAE. Unfortunately, I couldn’t access the resources and equipment in the IM lab, as I wasn’t available during the weekend. Nevertheless, I am proud of how the prototype turned out. It has a homemade aesthetic that truly showcases the effort and creativity I invested in it. *Update: I drew road markings on the pink paper, to easily direct left right and middle*

The challenges I encountered primarily involved reacquainting myself with p5. After a break spent focusing on Arduino, I felt a bit out of touch with p5 and needed to catch up on some of its features. However, I am pleased with the final result, particularly the pink color scheme I chose, which I feel represents me well. Another significant challenge was working with Arduino. I frequently faced connectivity issues with my laptop, requiring multiple restarts whenever it failed to upload. Additionally, getting the ultrasonic sensor to accurately measure distances was tricky, but after consulting numerous videos, I managed to overcome this obstacle. Despite these challenges, I am proud of what I accomplished. *Update: the issue of the arduino lagging was fixed by Muhammad Khan who was kind enough to fix the code for me, now Id say it works perfectly with no lag*

 

let car, helloKitty; // images for car and Hello Kitty character
let phase = "startScreen"; // current phase of the game: startScreen, game, endScreen, instructions
let speedCount = 0; // counter to track speed and timing of game elements
let speed = 1; // initial speed of moving cars
let cars = []; // array to hold car objects for collision detection and rendering
let gameOverImg; // image to be displayed when the game is over
let arduinoData = 0; // data received from Arduino for control
let useArduinoControl = true; // flag to enable or disable Arduino control
let rVal = 0; 


// -------------------------------------------------------------------------------------

// preloads game assets (images, fonts, sounds) 
// before starting the game

function preload() {
  car = loadImage("pink1.png"); 
  helloKitty = loadImage("hellokitty.png");
  StartUp = loadImage("workpls.png")
  customfontkitty = loadFont('kittyfont2.ttf');
  customfontsan = loadFont('san.ttf');
  gameOverSound = loadSound("ouch.mp3");
  introSound = loadSound("introsong.mp3");
  leftBorder = loadImage("leftBorder.png");
  rightBorder = loadImage("rightBorder.png");
  instructionImg = loadImage("instructions8.png");
  }

// ----------------------------------------------------------------------------

// sets up the initial state of the game canvas and variables

function setup() {
  createCanvas(400, 600);
  
  rectMode(CENTER);
  imageMode(CENTER);
  
  kittyX = 115; // initial X position for Hello Kitty
  kittyY = 550; // initial Y position for Hello Kitty
  score = 0; // initialize score
  level = 1; // initialize game level
}


// ----------------------------------------------------------------------------


// The main game loop, executed continuously to render the game


function draw() {
  
  background(50, 140, 80); // set the background color
  
  if (phase == "game") {
    fill(255,204,220);
    noStroke();
    
    // fisplay the left and right border images on the screen
    image(leftBorder, 20,height/2,leftBorder.width*0.55,leftBorder.height*0.55);
    image(rightBorder, 380,height/2,rightBorder.width*0.55,rightBorder.height*0.55);
    
    // draw the central playing area
    rect(width / 2, height / 2, 320, height);
    fill(255);
    
    // creates the road markings
    for (let i = 0; i < 4; i++) {
    for (let j = -1; j < 10; j++) {
    rect(75 + i * 80, (speedCount % 60) + 30 + 60 * j, 6, 50);
      }
    }
    
   // increment speedCount to control the moving speed of road markings
    speedCount += speed;
    
   // arduino Control: Move Hello Kitty based on Arduino input
      if (useArduinoControl && arduinoData !== null) {
  
      if (arduinoData < 10) { // move left
           kittyX = 115; 
        } else if (arduinoData >= 10 && arduinoData < 20) {  // Move center
           kittyX = 195; 
        } else if (arduinoData >= 20) {  // Move right
           kittyX = 275; 
        }
     }
    
    // returns the hello kitty to current position

    image(helloKitty, kittyX, kittyY, 70, 100);
    
  
    // on every 50th speed count a car will come down
    if (speedCount % 200 == 0) {
        cars.push({
            x: random([115, 115 + 80, 115 + 160]), // Random lane
            y: -40, // Start above the screen
        });
    }
    
    // move each car
    for (let i = 0; i < cars.length; i++) {
        image(car, cars[i].x, cars[i].y, 70, 100);
      
      
    // checks for collision with Hello Kitty
      if (dist(cars[i].x, cars[i].y, kittyX, kittyY) < 60) {
        phase = "gameOver";
        gameOverImg = get(0, 0, width, height);
        gameOverSound.play();
        introSound.stop();
    }
    
    // update car position
      cars[i].y += speed * 2;
    }
    
    
    // display score and level
    textSize(16);
    stroke(255);
    strokeWeight(2);
    fill("#9B858D");
    text("SCORE : " + score, 20, 30);
    text("LEVEL : " + level, 320, 30);
    
    // increment score over time
    if (frameCount % 60 == 0) {
      score++;
    }
   
    // increase speed and level after certain intervals
    if (speedCount % 1000 == 0) {
      speed += 1;
      level += 1;
    }
  }
  
// ----------------------------------------------------------------------------

 // display the game over screen and show score and level reached
  
  if (phase == "gameOver") {
    image(gameOverImg,width/2,height/2);
    textSize(16);
    strokeWeight(2);
    stroke(0);
    fill(0, 100);
    rect(width / 2, height / 2, 240, 150);
    fill(255);
    stroke(255);
    strokeWeight(1);
    text("Level " + level + " Reached", 145, 250);
    textSize(14);
    text("   You Scored " + score, 145, 360);
    text(" press Enter to Restart", 135, 330);
    fill(255);
    textSize(32);
    text(" GAME OVER", 105, 300);
  }
  
  // checks if the current game phase is the start screen
  if (phase == "startScreen") {
    if(!introSound.isPlaying()){
      introSound.loop();
    }
    

// ----------------------------------------------------------------------------
    
// start up image and text
    
    background("#9B858D");
    fill(255,192,203,0.60);
    image(StartUp, width/2, height/2, StartUp.width*0.7, StartUp.height*0.7);
    rect(width / 2, height / 2, 380, 580);
    stroke(255);
    
    fill(255);
    strokeWeight(6);
    stroke(255, 192, 230);
    fill(255, 105, 180);
    textFont(customfontkitty);
    textSize(86);
    text("hello kitty", 20, 120);
    
    fill(255, 105, 180)
    textSize(60);
    strokeWeight(2)
    stroke(255);
    textFont(customfontkitty);
    text("adventure", 70, 175);
    
    fill(171,209,158);
    rect(width / 2, 480, 200, 80, 50);
    fill(255);
    textFont(customfontkitty);
    text("start", 140, 500);
   
    stroke(171,209,158);
    strokeWeight(2)
    fill(255);
    textSize(28);
    text("   Press Enter for Instructions",10,570)
    
 
    // check if the mouse position is within a specified rectangular area
    if (
      mouseX > width / 2 - 100 &&
      mouseX < width / 2 + 100 &&
      mouseY > 460 &&
      mouseY < 540
    ) {
    
    // If the mouse is within the specified area it changes to game phase
    if (mouseIsPressed) {
      mouseIsPressed = false; 
      phase = "game";
      }
    }
  }
  
// -------------------------------------------------------------------------------------

// intruction page
  
  if (phase == "instruction"){
   
    image(instructionImg,200,300,400,600); // display the instruction image
  
    strokeWeight(2);
    stroke(0);
    textSize(60);
    textFont(customfontkitty);
    fill(0);
    text("How To Play",40,120);
    textFont();
    
    textFont(customfontsan);
    strokeWeight(1);
    textSize(20);
    text("      \n     1) move your hands left   \n          and right to move \n                  hello kitty  \n       2) try to avoid the cars   \n                3) BE SAFE!",60,330);
    textSize(20);
    text("      press enter to go back \n                 and start! ",70,500)
  }
}

// function to handle keyboard inputs


function keyPressed() {
  
  // game control using arrow keys during the 'game' phase
  if (phase == "game") {
    switch (key) {
      case "ArrowLeft":
        if (kittyX > 115) {
          kittyX -= 80;
        }
        break;
      case "ArrowRight":
        if (kittyX < 260) {
          kittyX += 80;
        }
        break;
      default:
        break;
    }
  }
  
  // restart the game when 'Enter' is pressed in the 'gameOver' phase
  if (phase == "gameOver") {
    if (key == "Enter") {
      score = 0;
      level = 1;
      speedCount = 0;
      speed = 0;
      cars = [];
      phase = "startScreen";
      kittyX = 75;
      kittyY = 550;
      key="";
    }
  }
  
  // handle key presses for navigating between instruction and start screen
  if(phase=="instruction"){
    
    if(key== "Enter"){
      phase = "startScreen";
      key="";
    }
  }
  
  if(phase=="startScreen"){
    if(key== "Enter"){
      phase = "instruction";
      key="";
    }
  }
  
  //  setup serial communication when spacebar is pressed
  if (key == " ") 
  {
    setUpSerial();
  }
}


// -------------------------------------------------------------------------------------

// arduino

function mouseIsPressed()
{
  readSerial();
}

// This function will be called by the web-serial library
// with each new line of data. The serial library reads
// the data until the newline and then gives it to us through
// this callback function

let lastDataReceivedTime = 0;

function readSerial(data) {
  if (data != null) {
    arduinoData = parseInt(data);
    lastDataReceivedTime = millis();
    useArduinoControl = true;
  } else if (millis() - lastDataReceivedTime > 2000) { // 2 seconds timeout
    useArduinoControl = false;
  }
  
    
 let sendToArduino = 0;
  switch (phase) {
    case 'game':
      sendToArduino = 1;
      break;
    case 'instruction':
    case 'startScreen':
      sendToArduino = 2;
      break;
    case 'gameOver':
      sendToArduino = 3;
      break;
  }
  writeSerial(sendToArduino + "\n");
}
const int trigPin = 9;  // Pin number for the ultrasonic sensor's trigger pin
const int echoPin = 10; // Pin number for the ultrasonic sensor's echo pin
const int ledgreen = 4; // Pin number for the green LED
const int ledred = 7;   // Pin number for the red LED
long duration;          // Variable to store the duration of the echo pulse
float distance;         // Variable to store the calculated distance

void setup()
{
  pinMode(trigPin, OUTPUT);  // Set the trigPin as an output
  pinMode(echoPin, INPUT);   // Set the echoPin as an input
  Serial.begin(9600);        // Start serial communication at 9600
  pinMode(ledgreen, OUTPUT); // Set the green LED pin as an output
  pinMode(ledred, OUTPUT);   // Set the red LED pin as an output
}

unsigned long ultrasonic_clock = 0;
unsigned long led_clock = 0;
float sum = 0;
int iterator = 0;

void loop()
{

  if (millis() - ultrasonic_clock >= 5) // Take reading every 5 ms
  {
    ultrasonic_clock = millis();

    digitalWrite(trigPin, LOW); // Clear the trigPin
    delayMicroseconds(2);
    digitalWrite(trigPin, HIGH); // Set the trigPin to HIGH state for 10 microseconds
    delayMicroseconds(10);
    digitalWrite(trigPin, LOW); // Set the trigPin to LOW state

    duration = pulseIn(echoPin, HIGH); // Read the echoPin, returns the sound wave travel time in microseconds
    distance = duration * 0.034 / 2;   // Calculate the distance

    sum += distance;
    iterator++;

    if (iterator == 5) // Average the last 5 readings
    {
      Serial.println(int(sum / 5));
      iterator = 0;
      sum = 0;
    }
  }

  if (millis() - led_clock >= 10) // Take reading every 10 ms
   {
  led_clock = millis(); //record the time this code ran

  // reading brightness level from serial
  //   int brightness = Serial.parseInt();
  if(Serial.read() == '\n'){
  
  // controlling LED based on brightness level
      if(brightness == 1){
         digitalWrite(ledgreen, HIGH);
        digitalWrite(ledred, LOW);
       }
     else if(brightness == 2){
        digitalWrite(ledgreen, LOW);
        digitalWrite(ledred, LOW);
       }
      else if(brightness == 3){
         digitalWrite(ledred, HIGH);
         digitalWrite(ledgreen, LOW);
       }
     }
    }
}

Improvements

For future improvements, I aim to rely less on external help, as I did encounter significant struggles. I plan to apply the skills acquired from tool training to craft an even better prototype using materials like wood and acrylic. Additionally, I’ll consider utilizing various components to enhance the game or possibly explore a different direction and different sensors for this project. Starting earlier will also be a priority, allowing ample time for revisions, enhancements, and potentially incorporating more features. Overall, I am proud of my work and have gained a deep appreciation for the effort, thought, and creativity required by those who do this professionally. Creating a project that not only reflects my aesthetic but also functions effectively has been a rewarding experience. I am extremely pleased with the outcome and feel a sense of accomplishment for what I have achieved.

Sources –

Song : https://www.youtube.com/watch?v=DTU2VpTJ0So

Ouch : https://www.youtube.com/watch?v=J6l4TVqTRpU

Hello Kitty : https://www.pinterest.com/pin/500884789807456421/

Instruction Background : https://www.pinterest.com/pin/2111131070738280/

Instruction Frame : https://www.pinterest.com/pin/4292562138085566/

Help –

https://www.youtube.com/watch?v=0Lhgd8PQmn0

https://www.youtube.com/watch?v=6F1B_N6LuKw

https://editor.p5js.org/azimovbob/sketches/LkvG5pT5g

https://editor.p5js.org/kellycarvalho2024/sketches/tDFpv6VLi

https://github.com/mzohaibnaz/CarRacing-p5

https://medium.com/@yyyyyyyuan/tutorial-serial-communication-with-arduino-and-p5-js-cd39b3ac10ce

https://www.youtube.com/watch?v=feL_-clJQMs

https://labor.99grad.de/typo3-docs/nnarduino/sensors/80_utrasonic/index.html

 

 

Leave a Reply