Final Project and User testing

1962

The three of us (Tarek, Aadil, and I) collectively decided to make something similar to an arcade machine as it was a very exciting part of our childhoods. Therefore, the project is a collection of three games that are designed to be played using an Arduino controller. The games include Flappy Bird, a popular mobile game in which the player controls a bird and navigates through obstacles; Racing Game, in which the player controls a race car and avoids colliding with the randomly generated cars as the race car overtakes them; and Space Invaders, a classic arcade game in which the player controls a spaceship and fights against invading aliens. The project was named 1962 in commemoration of the first arcade opened in 1962.

Arduino to P5js communication:

int button = 2;
int pot = A0;

int button1 = 4;
int button2 = 7;
int button3 = 8;

int lastPotValue;
int lastbutton;

long previousmill = 0;
long timebutton = 500;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(button, INPUT_PULLUP);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);

  pinMode(pot, INPUT);
}

int getpot(){
  int potValue = analogRead(pot)/255  ;
  int temp;
  if(potValue == 2 || potValue == 1){
    temp = 1;
  }else if(potValue == 3 || potValue == 4){
    temp = 2;
  }else{
    temp = 0;
  }
  return temp;
}

void loop() {
  int potValue = getpot();
  int buttonState = !digitalRead(button);
  long currentmill = millis();

  int buttonstate1 = !digitalRead(button1);
  int buttonstate2 = !digitalRead(button2);
  int buttonstate3 = !digitalRead(button3);

  int game = 0;

  if(buttonstate1 == 1){
    game = 1;
  }
  
  if(buttonstate2 == 1){
    game = 2;
  }
  
  if(buttonstate3 == 1){
    game = 3;
  }
  
  Serial.println(String(buttonState) + "," + String(potValue) + "," + String(game));
  if(buttonState == 1 && currentmill - previousmill >= timebutton){
    previousmill = currentmill;
    lastbutton = buttonState;
  }
}
We implemented this using the web serial. Here is how it briefly works:
  1.  The user connects an Arduino board to their computer using a USB cable.
  2.       The user writes and uploads a sketch (see above for the code) to the Arduino board that defines the behavior of the board and the data that it will send to the computer.
  3.     The user opens a P5.js sketch in their web browser and includes the p5.webserial.js library in their code.
  4.       The user adds event listeners to their P5.js sketch that will be called when the user connects or disconnects from the Arduino board, when the Arduino board is ready to be connected to, when there is an error communicating with the Arduino board, or when data is received from the Arduino board.
  5.       The user calls the getPorts() method of the p5.WebSerial object to check for any available Arduino boards. If an Arduino board is available, the portavailable event listener is called, which can be used to open a connection to the Arduino board.
  6.       Once the connection to the Arduino board is established, the user can send data to the Arduino board using the send() method of the p5.WebSerial object. The user can also receive data from the Arduino board using the data event listener, which is called whenever data is received from the Arduino board.
  7.       The user can use the received data from the Arduino board to control the behavior and appearance of their P5.js sketch. The user can also send data from the P5.js sketch to the Arduino board to control the behavior of the Arduino board.
  8.       When the user is finished using the Arduino board, they can close the connection to the board using the close() method of the p5.WebSerial object.

Flappy Bird:

Code:

function restart(){
  menu = 0;
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
  SCORE = 0;
  SCROLL_SPEED = 4;
  lives = 5;
}

function getRndInteger(min, max) {
  // https://www.w3schools.com/js/js_random.asp
  return Math.floor(Math.random() * (max - min)) + min;
}


function StartGame(){
  background(bg);
  fill("#7cfc00");
  rect(0, HEIGHT - GROUND_HEIGHT, WIDTH, HEIGHT);

  bird.draw();
  bird.update();
  bird.checkDeath(pipes);
  
  pipes.update();
  pipes.drawPipes();

  fill(255);
  textSize(60);
  textAlign(CENTER);
  text(SCORE, WIDTH / 9, HEIGHT / 7);
  textSize(30);
  text("lives: "+lives,WIDTH - WIDTH / 4, HEIGHT / 9);
  textAlign(CORNER);
}


class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}

The getRndInteger() function is a helper function that returns a random integer between two given values. This function is used to randomly generate the heights of the pipes in the game. The Bird and Pipes classes define the objects that appear in the game. The Bird class has a draw() method that is used to draw the bird on the screen, an update() method that is used to update the bird’s position and velocity, a flap() method that causes the bird to jump upwards, and a checkDeath() method that checks if the bird has collided with any of the pipes and ends the game if necessary. The Pipes class has an update() method that updates the positions of the pipes and a drawPipes() method that draws the pipes on the screen. Overall, the code defines a simple game in which the player controls a bird and must avoid colliding with pipes by jumping over them. The game keeps track of the player’s score and ends if the bird hits a pipe.

Racing Game:

The generateCars() function is used to randomly generate cars that appear on the screen and the displayCars() function is used to draw the cars on the screen. The displayScore() function is used to display the player’s current score on the screen. The potentiometer returns three readings: 0,1, and 2 based on the positioning. Based on the number being returned by the potentiometer – we handle the car movement.

Space Invaders: 

// both games =================================== function score(){ textSize(32); fill(250); text("Score: "+currentScore,20,50); } function increaseD(){ if(currentScore === 1 + prevScore){ difficulty += 0.5; prevScore = currentScore; // console.log(difficulty); } return random(1,5)+difficulty; } 

//space invadors ======================================
function startPage(){
  textSize(27);
  fill(250);
  text("Space invador",27,250);
  textSize(15);
  text("press enter to start",52,290);
}

function removeRocks(){
  rocks.splice(0,rocks.length);
  rocksctr = 0;
}



function displaybullets(){
    for(let i = 0; i < bullets.length; i++){
      bullets[i].display();
      
      if(bullets[i].y < 0){
        bullets.splice(i,1);
        numBullets--;
      }
      
    }
  // console.log(numBullets);
}

function generaterocks(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          rocks[rocksctr] = new boulders();
          rocks[rocksctr].display();
          // console.log(rocksctr);
          rocksctr++;
        }
     }
  }
}

function displayrocks(){
  for(let i = 0; i < rocks.length; i++){
    rocks[i].display();
    // console.log(">",rocks.length);
    
    let temp = false;
    for(let j = 0; j < bullets.length; j++){
      if(bullets[j].didcollide(rocks[i])){
        temp = true;
        bullets.splice(i,1);
        numBullets--;
      }
    }
    
    if(mainship.didcollide(rocks[i])){
      rocks.splice(i,1);
      rocksctr--;
      gamestatus = "end";
      bomb.play();
      losing1.play();
      
    }else if(rocks[i].y > height || temp){
      rocks.splice(i,1);
      rocksctr--;
    }
  }
}
 var timechecker = 0.5;

function makebullet(x,y){

  // console.log(x);
  bullets[numBullets] = new bulletClass(x,y);
  m0 = millis(); //time when laser created 
  if(timechecker>0.3){
  bullets[numBullets].display();}
  numBullets++;
  m = millis();
  timechecker = m - m0;
} //tried to disable continous shooting maybe look into it later 



// end space invadors ================================

// start racing car game functions=====================
function startPage2(){
  textSize(27);
  fill(250);
  text("Car racing",63,255);
  textSize(15);
  text("press enter to start",52,290);
}


function generateCars(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0 && carrs.length < 4){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          carrs[carsctr] = new cars();
          carrs[carsctr].display();
          // console.log(carsctr);
          carsctr++;
        }
     }
  }
}

function displayCars(){
  for(let i = 0; i < carrs.length; i++){
    carrs[i].display();
    // console.log(">",carrs.length);
    
    let temp = false;
    
    if(maincar.didcollide(carrs[i])){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore = 0;
      // bomb.play();
      gamestatus = "end";
      losing2.play();
      // gamestatus = "end";
      // bomb.play();
    }else if(carrs[i].y > height || temp){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore++;
      cargoing.play();
    }
  }
}


function checklanes(x,other){
  
  if(x === 1){
     if(lanes2[other.temp] === 1){
       other.temp = int(random(0,4));
       other.x = lanes[other.temp];
       checklanes(1,other);
     }else{
       lanes2[other.temp] = 1;
     }
  }else if(x === 0){
    lanes2[other.temp] = 0;
  }
}

function removeCars(){
  carrs.splice(0,carrs.length);
  carsctr = 0;
}

Initialization:

we initialized a lot of variables that would be used by the serial and the three games. the pre load function was also used to prepare the necessary pictures and sounds as well as fonts. the we set up what was necessary in the set up function.

let whichgame = 0;
let whichgameprev = 0;
// space invadors
let mainship;
let bullets = [];
let numBullets = 0;
let arrRocks;
let rocks = []
let lasersound;
let rocksctr = 0;

let difficulty = 0; // both games
let currentScore = 0;  //both games
let prevScore = 0;  //both games
let gamestatus = "start"; // both games
let rate = 0; // both games
let widthh = 400;

//racing game
let arrCars;
let carrs = [];
let carsctr = 0;
let lanes = [8,88,168,248,328];
let lanes2 = [0,0,0,0,0];


//flappy bird
var menu = 0; 
var SCROLL_SPEED = 4;
var SCORE = 0;
let oof; 
let bruh;
let music; 
var bird ;
var pipes;
var lives = 5;
const GRAVITY = 8.81;
const JUMP_HEIGHT = 6.0;
const GROUND_HEIGHT = 20; 
const WIDTH = 600;
const HEIGHT = 550;

//-------------arduino----------------
// let x=0;
// var c; 
let values = [];
// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial(); 

// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let outByte = 0;   

function preload() {
  main = loadImage('mainpicture.png');
  
  //space invadar
  img = loadImage('Hs4QN2.gif');
  startScreen = loadImage('startscreen.gif');
  ship = loadImage('Untitled-1.png');
  bullet = loadImage('bullet.png');
  soundFormats('mp3', 'ogg');
  lasersound = loadSound('lasersound.mp3');
  bomb = loadSound('explo.mp3');
  rock1 = loadImage('rock1.png');
  rock2 = loadImage('rock2.png');
  rock3 = loadImage('rock3.png');
  gameoverpng = loadImage('gameover.png');
  mainFont = loadFont('PressStart2P-vaV7.ttf');
  alternateFont = loadFont('metal lord.otf');
  losing2 = loadSound('losing2.wav');
  arcade = loadSound('arcade.mp3');  
  sp = loadSound('space.wav');
  
  //racing car game;
  imgg = loadImage('maincar.png');
  car1 = loadImage('car1.png');
  car2 = loadImage('car2.png');
  car3 = loadImage('car3.png');
  backgroundd = loadImage('background.png');
  backgrounddd = loadImage('final.gif');
  cargoing = loadSound('mixkit-fast-car-drive-by-1538.wav');
  startscreen = loadImage('startscreen.png');
  done = loadImage('gameovercar.png');
  losing1 = loadSound('losing1.wav');
  carpassing = loadSound('carpassing.wav');
  extraedge = loadImage('extraedge.png');
  
  //flappy bird
  music = loadSound("bgmusic.mp3");
  bg = loadImage('bg11.png');
  home = loadImage('homescreem.png');
  b = loadImage('bird.png');
  jump = loadSound('flap-1.mp3');
  oof = loadSound('oof.mp3');
  
}


function setup() {
  
  createCanvas(600, 550);
  arcade.play();
  
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
  
  //space invadors
  mainship = new spaceship();
  arrRocks = [rock1,rock2,rock3] ;
  textFont(mainFont);
  
  //racing
  maincar = new main_car();
  arrCars = [car1,car2,car3] ;
  
  //flappy bird
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
}

The draw function: this is where all the functions get called:

The draw() function starts by clearing the background of the canvas with background(0), then it checks the value of whichgame and renders the appropriate game. The code uses several other functions, such as controls()score(), and startPage(), to handle game mechanics and display game elements.

In the first if statement, the code checks if whichgame is equal to 0, and if so, it displays two images: main and extraedge and this is like the default screen. In the second if statement, the code checks if whichgame is equal to 1 and, if so, it displays the game for whichgame 1 which is space invadors. This game has several possible states (running, end, start) and the code uses if statements to handle each state. The third and fourth if statements do the same thing for games 2 and 3, car race and flappy bird respectively.

function draw() {
  //console.clear();
  var y=int(values[0]); //button value 1 or 0 
  var x = int(values[1]); //potentiometer reading 0,1,2
  var prewhichgame= int(values[2]);
  if(prewhichgame != whichgame && prewhichgame != 0 &&!isNaN(prewhichgame)){
    whichgame = prewhichgame;
  }
  values.splice(0);
  background(0);
  if(whichgame != whichgameprev){
    gamestatus = "start";
    whichgameprev = whichgame;
    currentScore = 0;
    difficulty = 0;
    prevScore = 0;
    restart();
  }

  if(whichgame == 0){ 
    
    
    image(main,0,0);
    image(extraedge,400,0);

    
  }if(whichgame == 1){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(img, 0, 0);
      controls(x,y);
      mainship.display();
      displaybullets();
      generaterocks();
      displayrocks();
      score();
    }else if(gamestatus == "end"){
      image(gameoverpng, 0, 0);
      removeRocks();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
      controls2(x,y);
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage();
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 2){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(backgrounddd,0,0);
      maincar.display();
      controls(x,y);
      generateCars();
      displayCars();
      score();
    }else if(gamestatus == "end"){
      image(done, 0, 0);
      controls2(x,y);
      
      removeCars();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage2();
      // background(startscreen);    
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 3){
    arcade.pause();
    if(menu==0){
      controls2(x,y);
      
      background(home);   
      textSize(25);
      textFont()
    }else if(menu==1){
      StartGame();
      controls(x,y);
    }
  }
  
}

the classes: 

The boulders class represents the falling boulders, and the bulletClass class represents bullets that the player can shoot to destroy the boulders. The spaceship class represents the player’s spaceship, and the cars class represents the cars that the player must avoid. The main_car class is a subclass of spaceship, and it appears to have the same functionality as the spaceship class.

The boulders class has a display() function that is used to draw the boulder on the screen, a move() function that is used to update the boulder’s position, and a width() function that is used to determine the width of the boulder. The bulletClass class has a display() function that is used to draw the bullet on the screen, a move() function that is used to update the bullet’s position, and a didcollide(other) function that is used to check if the bullet has collided with another object.

The spaceship and main_car classes have a display() function that is used to draw the spaceship or car on the screen, a move() function that is used to update the spaceship or car’s position, and a didcollide(other) function that is used to check if the spaceship or car has collided with another object. The cars class has the same functions as the boulders class, but it is used to represent cars rather than boulders.

// space invador classes start
class boulders{
  constructor(){
    this.x = random(0,widthh-50);
    this.y = -20;
    this.rocktype =  int(random(0, 3));
    this.rateFall = increaseD();
  }
  display(){
    image(arrRocks[this.rocktype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    if(this.rocktype == 0){
      return 71;
    }else if(this.rocktype == 1){
      return 48;
    }else if(this.rocktype == 2){
      return 91;
    }
  }
 
  
}



class bulletClass{
  constructor(x,y){
    this.x = x;
    this.y = y;
    lasersound.play();
    this.collision = false;
  }
  display(){
    image(bullet,this.x,this.y);
    this.move();
  }
  move(){
    this.y -= 7;
  }
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        currentScore++;
        return true;
      }      
      
    }
  }
  
}


class spaceship{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(ship,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//start racing car classes: ================================

class cars{
  constructor(){
    this.temp = int(random(0,5));
    this.x = lanes[this.temp];
    this.y = -20;
    this.cartype =  int(random(0, 3));
    this.rateFall = increaseD();
    //checklanes(1,this); commenting this cuz you've used recursion its causing stack overflow theres no base case here  
  }
  
  display(){
    image(arrCars[this.cartype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    return 70;
  }
 
  
}





class main_car{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(imgg,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (90 + other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//end racing car classes=========================



//flappy bird classes==================================

class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}
// end flappy bird classes =============================

Game Controls

Flappy Bird: Use the button on the Arduino or the UP key to jump.

Racing Game: Use the potentiometer or left and right keys to control the car’s steering.

Space Invaders: Use the potentiometers or left and right keys to control the spaceship’s movement and button or UP key to fire lasers.

User testing of Final game with physical controls:

What we are proud of:

Firstly, we are particularly proud of the artistic expression we have been able to give through the games and the console set-up. We had a lot of fun painting and wiring the console so that parts of our personalities were expressed. We are also happy that we were able to optimize the code over time to make the games smoother, this was a different experience to just debugging the code.

Here is the final sketch:

The console:

We wanted to design a simple console that allows the suer to toggle through the game and also play the game efficiently. We also had the goal of using as minimal resources as possible: hence we decided to use an old shoe box that was made out of recycled materials and apply paint over it.

 

Final Project – 1962

Project overview:

The three of us collectively decided to make something similar to an arcade machine as it was a very exciting part of our childhoods. Therefore, the project is a collection of three games that are designed to be played using an Arduino controller. The games include Flappy Bird, a popular mobile game in which the player controls a bird and navigates through obstacles; Racing Game, in which the player controls a race car and avoids colliding with the randomly generated cars as the race car overtakes them; and Space Invaders, a classic arcade game in which the player controls a spaceship and fights against invading aliens.

Arduino to P5js communication:

int button = 2;
int pot = A0;

int button1 = 4;
int button2 = 7;
int button3 = 8;

int lastPotValue;
int lastbutton;

long previousmill = 0;
long timebutton = 500;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(button, INPUT_PULLUP);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(button3, INPUT_PULLUP);

  pinMode(pot, INPUT);
}

int getpot(){
  int potValue = analogRead(pot)/255  ;
  int temp;
  if(potValue == 2 || potValue == 1){
    temp = 1;
  }else if(potValue == 3 || potValue == 4){
    temp = 2;
  }else{
    temp = 0;
  }
  return temp;
}

void loop() {
  int potValue = getpot();
  int buttonState = !digitalRead(button);
  long currentmill = millis();

  int buttonstate1 = !digitalRead(button1);
  int buttonstate2 = !digitalRead(button2);
  int buttonstate3 = !digitalRead(button3);

  int game = 0;

  if(buttonstate1 == 1){
    game = 1;
  }
  
  if(buttonstate2 == 1){
    game = 2;
  }
  
  if(buttonstate3 == 1){
    game = 3;
  }
  
  Serial.println(String(buttonState) + "," + String(potValue) + "," + String(game));
  if(buttonState == 1 && currentmill - previousmill >= timebutton){
    previousmill = currentmill;
    lastbutton = buttonState;
  }
}

 

We implemented this using the web serial. Here is how it briefly works:
  1.       The user connects an Arduino board to their computer using a USB cable.
  2.       The user writes and uploads a sketch (see above for the code) to the Arduino board that defines the behavior of the board and the data that it will send to the computer.
  3.     The user opens a P5.js sketch in their web browser and includes the p5.webserial.js library in their code.
  4.       The user adds event listeners to their P5.js sketch that will be called when the user connects or disconnects from the Arduino board, when the Arduino board is ready to be connected to, when there is an error communicating with the Arduino board, or when data is received from the Arduino board.
  5.       The user calls the getPorts() method of the p5.WebSerial object to check for any available Arduino boards. If an Arduino board is available, the portavailable event listener is called, which can be used to open a connection to the Arduino board.
  6.       Once the connection to the Arduino board is established, the user can send data to the Arduino board using the send() method of the p5.WebSerial object. The user can also receive data from the Arduino board using the data event listener, which is called whenever data is received from the Arduino board.
  7.       The user can use the received data from the Arduino board to control the behavior and appearance of their P5.js sketch. The user can also send data from the P5.js sketch to the Arduino board to control the behavior of the Arduino board.
  8.       When the user is finished using the Arduino board, they can close the connection to the board using the close() method of the p5.WebSerial object.

Flappy Bird:

Code:

function restart(){
  menu = 0;
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
  SCORE = 0;
  SCROLL_SPEED = 4;
  lives = 5;
}

function getRndInteger(min, max) {
  // https://www.w3schools.com/js/js_random.asp
  return Math.floor(Math.random() * (max - min)) + min;
}


function StartGame(){
  background(bg);
  fill("#7cfc00");
  rect(0, HEIGHT - GROUND_HEIGHT, WIDTH, HEIGHT);

  bird.draw();
  bird.update();
  bird.checkDeath(pipes);
  
  pipes.update();
  pipes.drawPipes();

  fill(255);
  textSize(60);
  textAlign(CENTER);
  text(SCORE, WIDTH / 9, HEIGHT / 7);
  textSize(30);
  text("lives: "+lives,WIDTH - WIDTH / 4, HEIGHT / 9);
  textAlign(CORNER);
}


class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}

The getRndInteger() function is a helper function that returns a random integer between two given values. This function is used to randomly generate the heights of the pipes in the game. The Bird and Pipes classes define the objects that appear in the game. The Bird class has a draw() method that is used to draw the bird on the screen, an update() method that is used to update the bird’s position and velocity, a flap() method that causes the bird to jump upwards, and a checkDeath() method that checks if the bird has collided with any of the pipes and ends the game if necessary. The Pipes class has an update() method that updates the positions of the pipes and a drawPipes() method that draws the pipes on the screen. Overall, the code defines a simple game in which the player controls a bird and must avoid colliding with pipes by jumping over them. The game keeps track of the player’s score and ends if the bird hits a pipe.

Racing Game:

The generateCars() function is used to randomly generate cars that appear on the screen and the displayCars() function is used to draw the cars on the screen. The displayScore() function is used to display the player’s current score on the screen. The potentiometer returns three readings: 0,1, and 2 based on the positioning. Based on the number being returned by the potentiometer – we handle the car movement.

Space Invaders: 

 

 

 

// both games =================================== function score(){ textSize(32); fill(250); text("Score: "+currentScore,20,50); } function increaseD(){ if(currentScore === 1 + prevScore){ difficulty += 0.5; prevScore = currentScore; // console.log(difficulty); } return random(1,5)+difficulty; } 

//space invadors ======================================
function startPage(){
  textSize(27);
  fill(250);
  text("Space invador",27,250);
  textSize(15);
  text("press enter to start",52,290);
}

function removeRocks(){
  rocks.splice(0,rocks.length);
  rocksctr = 0;
}



function displaybullets(){
    for(let i = 0; i < bullets.length; i++){
      bullets[i].display();
      
      if(bullets[i].y < 0){
        bullets.splice(i,1);
        numBullets--;
      }
      
    }
  // console.log(numBullets);
}

function generaterocks(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          rocks[rocksctr] = new boulders();
          rocks[rocksctr].display();
          // console.log(rocksctr);
          rocksctr++;
        }
     }
  }
}

function displayrocks(){
  for(let i = 0; i < rocks.length; i++){
    rocks[i].display();
    // console.log(">",rocks.length);
    
    let temp = false;
    for(let j = 0; j < bullets.length; j++){
      if(bullets[j].didcollide(rocks[i])){
        temp = true;
        bullets.splice(i,1);
        numBullets--;
      }
    }
    
    if(mainship.didcollide(rocks[i])){
      rocks.splice(i,1);
      rocksctr--;
      gamestatus = "end";
      bomb.play();
      losing1.play();
      
    }else if(rocks[i].y > height || temp){
      rocks.splice(i,1);
      rocksctr--;
    }
  }
}
 var timechecker = 0.5;

function makebullet(x,y){

  // console.log(x);
  bullets[numBullets] = new bulletClass(x,y);
  m0 = millis(); //time when laser created 
  if(timechecker>0.3){
  bullets[numBullets].display();}
  numBullets++;
  m = millis();
  timechecker = m - m0;
} //tried to disable continous shooting maybe look into it later 



// end space invadors ================================

// start racing car game functions=====================
function startPage2(){
  textSize(27);
  fill(250);
  text("Car racing",63,255);
  textSize(15);
  text("press enter to start",52,290);
}


function generateCars(){
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0 && carrs.length < 4){
    if(rand2 % 3 == 0){
      if(rand2 % 2 == 0 && rand % 2 == 0){
          carrs[carsctr] = new cars();
          carrs[carsctr].display();
          // console.log(carsctr);
          carsctr++;
        }
     }
  }
}

function displayCars(){
  for(let i = 0; i < carrs.length; i++){
    carrs[i].display();
    // console.log(">",carrs.length);
    
    let temp = false;
    
    if(maincar.didcollide(carrs[i])){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore = 0;
      // bomb.play();
      gamestatus = "end";
      losing2.play();
      // gamestatus = "end";
      // bomb.play();
    }else if(carrs[i].y > height || temp){
      checklanes(0,carrs[i]);
      carrs.splice(i,1);
      carsctr--;
      currentScore++;
      cargoing.play();
    }
  }
}


function checklanes(x,other){
  
  if(x === 1){
     if(lanes2[other.temp] === 1){
       other.temp = int(random(0,4));
       other.x = lanes[other.temp];
       checklanes(1,other);
     }else{
       lanes2[other.temp] = 1;
     }
  }else if(x === 0){
    lanes2[other.temp] = 0;
  }
}

function removeCars(){
  carrs.splice(0,carrs.length);
  carsctr = 0;
}


Initialization:

we initialized a lot of variables that would be used by the serial and the three games. the pre load function was also used to prepare the necessary pictures and sounds as well as fonts. the we set up what was necessary in the set up function.

let whichgame = 0;
let whichgameprev = 0;
// space invadors
let mainship;
let bullets = [];
let numBullets = 0;
let arrRocks;
let rocks = []
let lasersound;
let rocksctr = 0;

let difficulty = 0; // both games
let currentScore = 0;  //both games
let prevScore = 0;  //both games
let gamestatus = "start"; // both games
let rate = 0; // both games
let widthh = 400;

//racing game
let arrCars;
let carrs = [];
let carsctr = 0;
let lanes = [8,88,168,248,328];
let lanes2 = [0,0,0,0,0];


//flappy bird
var menu = 0; 
var SCROLL_SPEED = 4;
var SCORE = 0;
let oof; 
let bruh;
let music; 
var bird ;
var pipes;
var lives = 5;
const GRAVITY = 8.81;
const JUMP_HEIGHT = 6.0;
const GROUND_HEIGHT = 20; 
const WIDTH = 600;
const HEIGHT = 550;

//-------------arduino----------------
// let x=0;
// var c; 
let values = [];
// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial(); 

// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let outByte = 0;   

function preload() {
  main = loadImage('mainpicture.png');
  
  //space invadar
  img = loadImage('Hs4QN2.gif');
  startScreen = loadImage('startscreen.gif');
  ship = loadImage('Untitled-1.png');
  bullet = loadImage('bullet.png');
  soundFormats('mp3', 'ogg');
  lasersound = loadSound('lasersound.mp3');
  bomb = loadSound('explo.mp3');
  rock1 = loadImage('rock1.png');
  rock2 = loadImage('rock2.png');
  rock3 = loadImage('rock3.png');
  gameoverpng = loadImage('gameover.png');
  mainFont = loadFont('PressStart2P-vaV7.ttf');
  alternateFont = loadFont('metal lord.otf');
  losing2 = loadSound('losing2.wav');
  arcade = loadSound('arcade.mp3');  
  sp = loadSound('space.wav');
  
  //racing car game;
  imgg = loadImage('maincar.png');
  car1 = loadImage('car1.png');
  car2 = loadImage('car2.png');
  car3 = loadImage('car3.png');
  backgroundd = loadImage('background.png');
  backgrounddd = loadImage('final.gif');
  cargoing = loadSound('mixkit-fast-car-drive-by-1538.wav');
  startscreen = loadImage('startscreen.png');
  done = loadImage('gameovercar.png');
  losing1 = loadSound('losing1.wav');
  carpassing = loadSound('carpassing.wav');
  extraedge = loadImage('extraedge.png');
  
  //flappy bird
  music = loadSound("bgmusic.mp3");
  bg = loadImage('bg11.png');
  home = loadImage('homescreem.png');
  b = loadImage('bird.png');
  jump = loadSound('flap-1.mp3');
  oof = loadSound('oof.mp3');
  
}


function setup() {
  
  createCanvas(600, 550);
  arcade.play();
  
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
  
  //space invadors
  mainship = new spaceship();
  arrRocks = [rock1,rock2,rock3] ;
  textFont(mainFont);
  
  //racing
  maincar = new main_car();
  arrCars = [car1,car2,car3] ;
  
  //flappy bird
  bird = new Bird(WIDTH / 2, HEIGHT / 2, 30);
  pipes = new Pipes(60, 200, 130);
}

The draw function: this is where all the functions get called:

The draw() function starts by clearing the background of the canvas with background(0), then it checks the value of whichgame and renders the appropriate game. The code uses several other functions, such as controls(), score(), and startPage(), to handle game mechanics and display game elements.

In the first if statement, the code checks if whichgame is equal to 0, and if so, it displays two images: main and extraedge and this is like the default screen. In the second if statement, the code checks if whichgame is equal to 1 and, if so, it displays the game for whichgame 1 which is space invadors. This game has several possible states (running, end, start) and the code uses if statements to handle each state. The third and fourth if statements do the same thing for games 2 and 3, car race and flappy bird respectively.

function draw() {
  //console.clear();
  var y=int(values[0]); //button value 1 or 0 
  var x = int(values[1]); //potentiometer reading 0,1,2
  var prewhichgame= int(values[2]);
  if(prewhichgame != whichgame && prewhichgame != 0 &&!isNaN(prewhichgame)){
    whichgame = prewhichgame;
  }
  values.splice(0);
  background(0);
  if(whichgame != whichgameprev){
    gamestatus = "start";
    whichgameprev = whichgame;
    currentScore = 0;
    difficulty = 0;
    prevScore = 0;
    restart();
  }

  if(whichgame == 0){ 
    
    
    image(main,0,0);
    image(extraedge,400,0);

    
  }if(whichgame == 1){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(img, 0, 0);
      controls(x,y);
      mainship.display();
      displaybullets();
      generaterocks();
      displayrocks();
      score();
    }else if(gamestatus == "end"){
      image(gameoverpng, 0, 0);
      removeRocks();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
      controls2(x,y);
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage();
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 2){
    arcade.pause();
    
    if(gamestatus == "running"){
      image(backgrounddd,0,0);
      maincar.display();
      controls(x,y);
      generateCars();
      displayCars();
      score();
    }else if(gamestatus == "end"){
      image(done, 0, 0);
      controls2(x,y);
      
      removeCars();
      currentScore = 0;
      difficulty = 0;
      prevScore = 0;
    }else if(gamestatus == "start"){
      controls2(x,y);
      background(startScreen);
      startPage2();
      // background(startscreen);    
    }
    
    image(extraedge,400,0);
    
  }else if(whichgame == 3){
    arcade.pause();
    if(menu==0){
      controls2(x,y);
      
      background(home);   
      textSize(25);
      textFont()
    }else if(menu==1){
      StartGame();
      controls(x,y);
    }
  }
  
}

the classes: 

The boulders class represents the falling boulders, and the bulletClass class represents bullets that the player can shoot to destroy the boulders. The spaceship class represents the player’s spaceship, and the cars class represents the cars that the player must avoid. The main_car class is a subclass of spaceship, and it appears to have the same functionality as the spaceship class.

The boulders class has a display() function that is used to draw the boulder on the screen, a move() function that is used to update the boulder’s position, and a width() function that is used to determine the width of the boulder. The bulletClass class has a display() function that is used to draw the bullet on the screen, a move() function that is used to update the bullet’s position, and a didcollide(other) function that is used to check if the bullet has collided with another object.

The spaceship and main_car classes have a display() function that is used to draw the spaceship or car on the screen, a move() function that is used to update the spaceship or car’s position, and a didcollide(other) function that is used to check if the spaceship or car has collided with another object. The cars class has the same functions as the boulders class, but it is used to represent cars rather than boulders.

// space invador classes start
class boulders{
  constructor(){
    this.x = random(0,widthh-50);
    this.y = -20;
    this.rocktype =  int(random(0, 3));
    this.rateFall = increaseD();
  }
  display(){
    image(arrRocks[this.rocktype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    if(this.rocktype == 0){
      return 71;
    }else if(this.rocktype == 1){
      return 48;
    }else if(this.rocktype == 2){
      return 91;
    }
  }
 
  
}



class bulletClass{
  constructor(x,y){
    this.x = x;
    this.y = y;
    lasersound.play();
    this.collision = false;
  }
  display(){
    image(bullet,this.x,this.y);
    this.move();
  }
  move(){
    this.y -= 7;
  }
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        currentScore++;
        return true;
      }      
      
    }
  }
  
}


class spaceship{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(ship,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//start racing car classes: ================================

class cars{
  constructor(){
    this.temp = int(random(0,5));
    this.x = lanes[this.temp];
    this.y = -20;
    this.cartype =  int(random(0, 3));
    this.rateFall = increaseD();
    //checklanes(1,this); commenting this cuz you've used recursion its causing stack overflow theres no base case here  
  }
  
  display(){
    image(arrCars[this.cartype],this.x,this.y);
    this.move();
  }
  move(){
    this.y += this.rateFall;
  }
  width(){
    return 70;
  }
 
  
}





class main_car{
  constructor(){
    this.x = 200;
    this.y = 450;
    this.display();
  }
  
  display(){
    imageMode(CENTER);
    image(imgg,this.x,this.y);

    this.move();
    this.checkboundries();


    imageMode(CORNER);
  }
  
  move(){
    this.x += rate;
  }
  
  checkboundries(){
    if(this.x > widthh){
      this.x = 0;
    }else if(this.x < 0){
      this.x = widthh;
    }
  }
  
  didcollide(other){
    if ( (this.x <= (other.x + other.width())) && (this.x >= other.x)) {
      if ((this.y <= (90 + other.y + other.width())) && (this.y  >= other.y)){
        // print("Collision");
        return true;
      }      
      
    }
  }

}

//end racing car classes=========================



//flappy bird classes==================================

class Bird {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
    this.vely = 0;
  }

  draw() {
    //fill("#eaff00");
    //circle(this.x+112.5, this.y-112.5, this.size);
    image(b,this.x-this.size, this.y-this.size,this.size, this.size);
    //image("cow.jpg",this.x, this,y);
  }

  update() {
    this.y += this.vely;
    this.vely = lerp(this.vely, GRAVITY, 0.05);
    this.y = Math.max(this.size / 2, Math.min(this.y, HEIGHT - GROUND_HEIGHT - this.size / 2));
  }

  flap() {
    this.vely = -JUMP_HEIGHT;
    jump.play();
  }

  checkDeath(pipes) {
    for (var pipe of pipes.pipes_list) {
      if (this.x + this.size / 2 > pipe.x && pipe.height && this.x - this.size / 2 < pipe.x + pipes.width) {
        if (this.y - this.size / 2 <= pipe.height || this.y + this.size / 2 >= pipe.height + pipes.gap) {          
          // window.location.reload();
          lives--;
          oof.play();
          if(lives === 0){
              // losing1.play();
              // restart(); 
              // break;
            window.location.reload();
          }
          pipes.pipes_list.splice(pipes.pipes_list.indexOf(pipe),1);
          pipes.retq();
          
        }
      }
      if (this.x - this.size / 2 > pipe.x + pipes.width && pipe.scored == false) {
        SCORE += 1;
        pipe.scored = true;
      }
    }
  }
}


class Pipes {
  constructor(width, frequency, gap) {
    this.width = width;
    this.frequency = frequency;
    this.gap = gap;

    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  }

  update() {   
    for (var pipe of this.pipes_list) {
      pipe.x -= SCROLL_SPEED;
      if (pipe.x + this.width <= 0) {
        pipe.x = WIDTH;
        pipe.height = getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap - this.gap);
        pipe.scored = false;
      }
    }
  }
  
  retq(){
    this.pipes_list = [
      { x: 500, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false },
      { x: 500 + this.width + this.frequency, height: getRndInteger(this.gap, HEIGHT - GROUND_HEIGHT - this.gap), scored: false }
    ];
  
  }

  drawPipes() {
    fill((0),(150),(0));
    for (var pipe of this.pipes_list) {
      rect(pipe.x, 0, this.width, pipe.height);
      rect(pipe.x, HEIGHT - GROUND_HEIGHT, this.width, -HEIGHT + pipe.height + GROUND_HEIGHT + this.gap);
    }
  }

}
// end flappy bird classes =============================

 

Game Controls

Flappy Bird: Use the button on the arduino or the UP key to jump.

Racing Game: Use the potentiometer or left and right keys to control the car’s steering.

Space Invaders: Use the potentiometers or left and right keys to control the spaceship’s movement and button or UP key to fire lasers.

Conclusion

This project demonstrates how to create and play games using p5.js and Arduino. The project includes three games that can be controlled using potentiometers and push buttons, and can be easily extended to include additional games and custom controller designs. We’re particularly proud of the aesthetics of the games – we were able to recreate what we initally had in mind. Furthermore, we had a lot of bugs which wouldn’t let the games run smoothly. We figured out how to implement the games smoothly by making changes in our algorithms and by handling specific types of errors which were mostly given by the arduino board. However, there is no proper restart function for flappy bird – if you lose, the canvas simply starts from scratch.

My contribution to the project was coding the handling the serial communication and the design of the physical user interface. I did some of the error handling and debugging towards the end as well.

 

Week 11 Exercise

Exercise 1:

Here is the p5js code for exercise 1:

// variable to hold an instance of the p5.webserial library:
const serial = new p5.WebSerial();
 
// HTML button object:
let portButton;
let inData;                   // for incoming serial data
let outByte = 0;              // for outgoing data
 
function setup() {
  createCanvas(400, 300);          // make the canvas
  // check to see if serial is available:
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // if serial is available, add connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
}
 
function draw() {
  
   background(0);
   fill(255);
   text("sensor value: " + inData, 30, 50);
 
}
// if there's no port selected, 
// make a port select button appear:
function makePortButton() {
  // create and position a port chooser button:
  portButton = createButton("choose port");
  portButton.position(10, 10);
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}
 
// make the port selector window appear:
function choosePort() {
  if (portButton) portButton.show();
  serial.requestPort();
}
 
// open the selected port, and make the port 
// button invisible:
function openPort() {
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);
 
  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
}
 
// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}
// read any incoming data as a string
// (assumes a newline at the end of it):
function serialEvent() {
  inData = Number(serial.read());
  console.log(inData);
}
 
// try to connect if a new serial port 
// gets added (i.e. plugged in via USB):
function portConnect() {
  console.log("port connected");
  serial.getPorts();
}
 
// if a port is disconnected:
function portDisconnect() {
  serial.close();
  console.log("port disconnected");
}
 
function closePort() {
  serial.close();
}

This is the Arduino code we used for the exercise:

void setup() {
  Serial.begin(9600); // initialize serial communications
}
 
void loop() {
  // read the input pin:
  int potentiometer = analogRead(A0);                  
  // remap the pot value to fit in 1 byte:
  int mappedPot = map(potentiometer, 0, 1023, 0, 255); 
  // print it out the serial port:
  Serial.write(mappedPot);                             
  // slight delay to stabilize the ADC:
  delay(1);                                            
  
  // Delay so we only send 10 times per second and don't
  // flood the serial connection
  delay(100);
}

Here is the demo:

Exercise 2:

The p5js code for excercise 2:

let serial; // variable to hold an instance of the serialport library
let portName = "COM3"; // fill in your serial port name here
let xPos=0;
let yPos=240;
let onOff=0;
let val;
function setup() {
  createCanvas(640, 480);
  serial = new p5.SerialPort(); // make a new instance of the serialport library
  serial.on("list", printList); // set a callback function for the serialport list event
  serial.on("connected", serverConnected); // callback for connecting to the server
  serial.on("open", portOpen); // callback for the port opening
  serial.on("data", serialEvent); // callback for when new data arrives
  serial.on("error", serialError); // callback for errors
  serial.on("close", portClose); // callback for the port closing
  serial.list(); // list the serial ports
  serial.open(portName); // open a serial port
}
function draw() {
  background(255);
  val = map(mouseX, 0, width, 0, 255);
}
// get the list of ports:
function printList(portList) {
  // portList is an array of serial port names
  for (let i = 0; i < portList.length; i++) {
    // Display the list the console:
    print(i + " " + portList[i]);
  }
}
function serverConnected() {
  print("connected to server.");
}
function portOpen() {
  print("the serial port opened.");
}
function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  let inString = serial.readLine();
  serial.write(val);
}
function serialError(err) {
  print("Something went wrong with the serial port. " + err);
}
function portClose() {
  print("The serial port closed.");
}

The Arduino code :

void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  pinMode(5, OUTPUT);
  while (Serial.available() <= 0) {
    Serial.println("0,0"); // send a starting message
    delay(200);      
  }
}
void loop() {
  while (Serial.available() > 0) {
    // read the incoming byte:
    int inByte = Serial.read();
    analogWrite(5,inByte);
    Serial.println();
  }
}
Exercise 3:

P5js code:

let serial; // variable to hold an instance of the serialport library
let portName = "COM7"; // fill in your serial port name here
let velocity;
let gravity;
let position;
let acceleration;
let wind;
let drag = 0.99;
let mass = 50;
let windVal;
function setup() {
  createCanvas(640, 360);
  noFill();
  position = createVector(width/2, 0);
  velocity = createVector(0,0);
  acceleration = createVector(0,0);
  gravity = createVector(0, 0.5*mass);
  wind = createVector(0,0);
  
  serial = new p5.SerialPort(); // make a new instance of the serialport library
  serial.on("list", printList); // set a callback function for the serialport list event
  serial.on("connected", serverConnected); // callback for connecting to the server
  serial.on("open", portOpen); // callback for the port opening
  serial.on("data", serialEvent); // callback for when new data arrives
  serial.on("error", serialError); // callback for errors
  serial.on("close", portClose); // callback for the port closing
  serial.list(); // list the serial ports
  serial.open(portName); // open a serial port
}
// get the list of ports:
function printList(portList) {
  // portList is an array of serial port names
  for (let i = 0; i < portList.length; i++) {
    // Display the list the console:
    print(i + " " + portList[i]);
  }
}
function serverConnected() {
  print("connected to server.");
}
function portOpen() {
  print("the serial port opened.");
}
function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  let inString = serial.readLine();
  print(inString);
  if (inString.length > 0){
    windVal = map(inString, 0, 1023, -3, 3);
  }
}
function serialError(err) {
  print("Something went wrong with the serial port. " + err);
}
function portClose() {
  print("The serial port closed.");
}
function draw() {
  background(255);
  
  wind.x = windVal;
  
  applyForce(wind);
  applyForce(gravity);
  velocity.add(acceleration);
  velocity.mult(drag);
  position.add(velocity);
  acceleration.mult(0);
  ellipse(position.x,position.y,mass,mass);
  
  if (position.y > height-mass/2 - 25){
    serial.write(255);
  }
  else serial.write(0);
  
  if (position.y > height-mass/2) {
      velocity.y *= -0.9;  // A little dampening when hitting the bottom
      position.y = height-mass/2;
    }
}
function applyForce(force){
  // Newton's 2nd law: F = M * A
  // or A = F / M
  let f = p5.Vector.div(force, mass);
  acceleration.add(f);
}
function keyPressed(){
  if (keyCode==LEFT_ARROW){
    wind.x=-1;
  } 
  if (keyCode==RIGHT_ARROW){
    wind.x=1;
  }
  if (key==' '){
    mass=random(15,80);
    position.x=width/2;
    position.y=-mass;
    velocity.mult(0);
  }
}

Arduino code:

Arduino code
void setup() {
  Serial.begin(9600);
  pinMode(2, OUTPUT);
  while (Serial.available() <= 0) {
    Serial.println("0"); // send a starting message
    delay(300);              // wait 1/3 second
  }
}
void loop() {
  while (Serial.available() > 0) {
    // read the incoming byte:
    int inByte = Serial.read();
    analogWrite(2, inByte);
    int WindPower = analogRead(A0);
    Serial.println(WindPower);
    
  }
}

 

Final Project Proposal

Aadil, Tarek and Janindu

Proposal – Space destroyer game with Joystick

We plane to make a project that allows you to control a space ship with the potentiometer and use a push button switch to shoot lasers at the incoming rocks. There will be a light that lights up when you get hit by asteroids.

We will use OOP concepts to first make the game and control it through keyboard input. Then we will construct a relevant Arduino framework to provide input and then we will combine the two to make a user interface.

Week 10- Musical Instrument (Drill beat maker)

Concept

We wanted to make a device that allows artists to simulate high-hats in drill beats according how they bob their heads. We realized that sound engineers move their body in a unique way. We used a ultrasonic sensor and novel equation to track this movement and produce a drill beat with a correlated frequency.

Implementation

#include "pitches.h"
#define echoPin 2 
#define trigPin 3 

int pushButton = 7;
const int sPin = 1;
int melody[] = {
  NOTE_C4, NOTE_G3, NOTE_G3, NOTE_A3, NOTE_G3, 0, NOTE_B3, NOTE_C4
};

int noteDurations[] = {
  4,4,5,4,5,4,5
};

unsigned long currentNoteStartedAt = 0;

int thisNote = 0;

int millisToNextNote = 0;

int currentLedState = LOW;
unsigned long currentLedStateStartedAt = 0;
long ledDelay = 100;


long duration;
int distance; 

void setup() {
  pinMode(trigPin, OUTPUT); 
  pinMode(echoPin, INPUT); 
  Serial.begin(9600); 
  
  pinMode(pushButton, INPUT);
}
void loop() {
  
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  duration = pulseIn(echoPin, HIGH);
 
  distance = duration * 0.034 / 2; 
    unsigned long currentMillis = millis();

  if (currentMillis - currentNoteStartedAt >= millisToNextNote) {

    int noteDuration = 1000 / noteDurations[thisNote];

    tone(3, melody[thisNote], noteDuration);
    millisToNextNote = log10(distance) * 1300;
    currentNoteStartedAt = currentMillis;
    thisNote++;

   
    if ( thisNote >= 8 ) {
      thisNote = 0;
    }
  }

  
  if (currentMillis - currentLedStateStartedAt >= ledDelay) {

   
    if (currentLedState == LOW) {
      currentLedState = HIGH;
    } else {
      currentLedState = LOW;
    }
    currentLedStateStartedAt = currentMillis;
  }

  int buttonState = digitalRead(pushButton);
  if (buttonState==true){
    Serial.println(buttonState);
    delay(100);}

}

We used a ultrasonic senor to obtain distance measurements, a switch to implement a function to introduce delay and slow the sound down.  Also the inspiration for the code to produce a melody was inspired by Prof. Michael Shiloh’s implementation.

We used these inputs to make a device that produces a sound continuously sampled and responds to the distance of the user from the sensor.

Here are the schematics for the circuit implementation:


Reflections

We wanted to implement a loudness altering function to the piezo speaker and tested the schematic on tinker cad as well, however during the implementation it did not seem to work out. Some improvements that could made are the ability to give the user more flexibility with what sounds to choose.

 

Week 9: Analog and Digital sensors – Picture Perfect

Concept

I have always wondered why it is not possible to take the perfect picture of yourself. I realized that two of the most important controllable variables in photography are the lighting and the focus distance. Hence the project was designed to give an indication of the perfect lighting for the surroundings and the distance for the person taking the photo.

Implementation

const int buttonPin = 7; 
int onOff = 0;
int sensorPin = A0;   
int ledPin = 13;  
int sensorValue = 0; 
const int blueLEDPin = 12;
const int yellowLEDPin = 8;
const int goLED = 10;
#define echoPin 2 
#define trigPin 3
long duration; 
int distance;
void setup() {
  // put your setup code here, to run once:
  
  pinMode(ledPin, OUTPUT);
  digitalWrite(blueLEDPin, OUTPUT);
  digitalWrite(yellowLEDPin, OUTPUT);
  digitalWrite(goLED, OUTPUT);
  pinMode(buttonPin, INPUT);
  Serial.begin(9600);

  //Ultra
  pinMode(trigPin, OUTPUT); // Sets the trigPin as an OUTPUT
  pinMode(echoPin, INPUT); // Sets the echoPin as an INPUT

}

void loop() {
  onOff = digitalRead(buttonPin);
//LDR
  sensorValue = analogRead(A0);
  Serial.println(sensorValue);
  digitalWrite(ledPin, LOW);
  digitalWrite(blueLEDPin, LOW);
  delay(sensorValue/2);
  digitalWrite(ledPin, HIGH);
  digitalWrite(blueLEDPin, HIGH);
  delay(sensorValue/2);

  //Ultrasonic sensor
  digitalWrite(trigPin, LOW);
  delay(2);
  digitalWrite(trigPin, HIGH);
  delay(10);
  digitalWrite(trigPin, LOW);
  duration = pulseIn(echoPin, HIGH);
  distance = duration * 0.034 / 2; 
  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.println(" cm");

  if ((distance>40&&distance<55)){
    digitalWrite(yellowLEDPin, HIGH);
    
  }
  else {
    digitalWrite(yellowLEDPin, LOW);
    delay(distance*2);
    digitalWrite(yellowLEDPin, HIGH);
  }
  Serial.println(onOff);
  if ((distance>40&&distance<55)&&(sensorValue>600 &&sensorValue<900)&&(onOff == LOW)){
    digitalWrite(goLED, HIGH);
    digitalWrite(yellowLEDPin, HIGH);
    digitalWrite(blueLEDPin, HIGH);
    delay(10000);
    digitalWrite(goLED, LOW);
    digitalWrite(yellowLEDPin, LOW);
    digitalWrite(blueLEDPin, LOW);
  
  }

}

The program takes in the lighting of the surroundings using the LDR and then makes the blue LED turn on and off proportional to the intensity of the light. This shows if you need to increase the light in surroundings or if it getting closer to the perfect level. Likewise with the Ultrasonic sensor, it detects the distance from the object right in front and inputs distance data and the yellow led blinks proportionally to the distance from sensor. When button is pressed and conditions are met, the lights stay on for 10 seconds.

Reflections

Initially, I wanted to use Tarek’s creative switch idea to make sure that the subject is flexing for the picture. However, I realized that it would require a considerable length of wire to obtain a reasonable distance to take a good picture. I also wanted to make the iPhone take the photo itself via voice commands (where a recoding of me sating “hi Siri, take a picture” would be output once the correct conditions are met). However, iOS security regulations and the lack of complexity in the speaker would not allow this function.

Week 7 – Unusual Switch

Calf Extension Switch
Idea

I realized that I cannot make my claves bigger regardless of how much I trained them. There were three probable reasons for this: inefficient training, insufficient training, or bad genetics. Hence, I came up with a solution that lets me overcome all three obstacles by efficiently training my calves all day efficiently.

Implementation

As seen above the switch if formed by connecting a 330 ohm resistor and bulb in series configuration, and then using two pieces of aluminum foil (one attached to my heel and the other to my shoes inner sole). Resting the foot completes the circuit by making the sheets of foil come in contact and thus lighting the bulb to indicate that you have failed in flexing your calf muscle.

Reflections and improvements

Something I would improve in the design is definitely the mobility of the switch. I have to be able to move with the switch to further activate my calves. Also the look of the switch is very unappealing, it is something I would like to make much more visually appealing.

Week 6- Data visualisation

Concept

I wanted to visualize data from important and relevant statistics such as global climate statistics and data. I also realized that representing these statics can also pave way for an art form where the statistics come together graphically to convey an abstract artistic motif.

Implementation
var data_stored = [];
var data_in;
var colors=[];

function preload (){
  data_in = loadTable ("global_csv.csv", "csv","header"); 
}

function setup() {
  randomSeed();
  createCanvas(400, 400);
  noStroke();
  background(0,0,0);
  //Hard code the selection of data required
  for (var i = 0; i<260; i++){
    for (var j = 0; j<2; j++){
      //push into data stored array
      data_stored.push(data_in.getNum(i,j));
          
    }
  }
  //random colour generator
  for (var c_=0; c_<244; c_++){
      colors[c_]=c_+1;
    }
//drawing the shape
  var base= 30;
  for (var k =0; k<data_stored.length; k++){
    var data_points= data_stored[k];
    var x = width/2;
    var y = height/2;
    //fixing data to correct proportions
    var h = map(data_points, 0, max(data_stored), 0 , (height/2-base)*1.3);
    var r = map(k, 0, data_stored.length, 0 , 2*PI); 
    fill (random(colors), 100, 100);
    push ();
    translate(x,y);
    rotate(r);
    rect(0, -base, 1, h)
    pop(); 
  }
}

The code implemented simply loads a csv file containing raw data, then takes the data on to an array. Then it uses the values from the array to produce heights of the bars that will be visualized. Then these heights are mapped onto a suitable scale and displayed.

Design

Reflections and Improvements

I would definitely like to make the application take in any arbitrary data set and visualize it. Here I had to hard code some of the properties. Some of the functions to manipulate the array did not seem to work. Also, I would like to add a bit more meaningful color to the presentation of data to give it an extra dimension.

Mid Term Project

BUBBLE POPPER – AN EDUCATIONAL APPROACH TO BUBBLE POPPING:)

  • Concept

The project is an educational game for children learning how to spell. The letters fall down from the top and the user (child in this case) can select the letters to make up the word that shows up on the screen.

The game was inspired by this small toy that my father bought for me when I was 5 years old. It had a small screen and the letters would drop down slowly.

The game is also just meant to be satisfying. During midterm season, I would just sit in the library and just pop the bubbles in the game just to relieve stress.

  • Description

The game works by creating circles with a letter associated with the circle. The circle is derived as a object of the droppedShape class. The objects populate an array that deletes them after the shapes or circles exit the screen. This was done as to not use up too much memory. The object also has access to the letter it hold. This letter is therefore, when clicked, on added to a new string that checks in every iteration if the target word had been made up.

The surrounding functions help display text and display the level/score. These variables are all globally stored. Which is conventionally a bad practice but help access the variable and manipulate in many different functions uniquely.

I’m particularly happy about the droppedShape class as it has been a running template for me to learn OOP. Outside of the project, I’ve tried to implement different things to this class and its nice to see it working well in the project.

//declare global variables 
let s;
let bubbleArr = [];
let numShapes = 10;
let howmanycircles = 10;
let deletedcircles = 0;
let rr = 0;
let bb = 0;
let gg = 0;
let img;
let timer = 5;
let newLetter;
var word;
var newword="";
let flag=true;
var new_wordSound;
var bpopped_sound;
let level=1;
let menu =0;
let gOverimage;
let bpop;

//alphabet array
const texts1 = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ];

//letter concentration
let texts = ["A", "B", "C", "D", "E", "F", "G", "T", "T","U","H","R"];

//word bank

const words= ["CAT", "BAT", "HAT", "RAT", "MAT", "CAN", "JAM", "RAN"];

function preload(){
  img=loadImage("cat.webp");
  //loadSound('assets/new word sound.mp3');
  gOverimage=loadImage("game_over.gif");
  bpop = loadSound('sfx-pop.mp3');
}

//function for detecting the mouse press
function mousePressed(){
  for(let i = 0; i < bubbleArr.length; i++){
    bubbleArr[i].clicked(mouseX,mouseY,i);
    
  }
}

//text on top
class topText{
  constructor(){
    this.word=random(words);
    
  }
  disp(){
    if (flag==false){
      fill("red");
      for(let i=0;i<3;i++){
         while (newLetter==this.word[i]){
         textAlign(CENTER);
         text(this.word[i],50,200);
      }
     
    }
    }
    fill("white");
    //stroke("black");
    //strokeWeight(2);
    
    for(let i=0;i<3;i++){
      textSize(100);
      textAlign(CENTER);
      text(this.word[i],50,200);
    }
    
    
  }
  success(){
              if(newLetter==this.word[1]&&newLetter==this.word[2]&&newLetter==this.word[3]){
      flag=false;
      disp();
    
    }
    if(newLetter==this.word[1]||newLetter==this.word[2]||newLetter==this.word[3]){
      
    }
  }
  
}

//Shape being dropped 
class droppedCircle{
  constructor() {
    this.x = random(width);
    this.y = 0;
    this.width = random(38, 45);
    this.speed = random(0.5,3);
    this.r = random(250);
    this.g = random(250);
    this.b = random(250);
    this.text= texts[Math.floor(Math.random() * texts.length)]
  }
   move() {
    this.y += this.speed;
  }

  display() {
    fill(this.r,this.g,this.b);
    stroke(255);
    strokeWeight(2);
    ellipse(this.x, this.y, this.width, this.width);
    for (let i = 0; i < bubbleArr.length; i++){
          strokeWeight(1);
          fill("#000000");
          textAlign(CENTER);
          textSize((bubbleArr[i].width)/2);
          text(bubbleArr[i].text, bubbleArr[i].x, bubbleArr[i].y);
        
      
    }

    this.move();
  }

  clicked(px,py,index){
    let d = dist(px,py,this.x,this.y);
    if(d < this.width){
      rr = this.r;
      bb = this.b;
      gg = this.g;
      //insert sound
      bpop.play();
      
      bubbleArr.splice(index,1);
      numShapes--;
      newLetter=this.text;
      //console.log(newLetter);
      newword+=newLetter;
        
      
      //console.log(newword);
      if (match(newword, word)==word){
        //console.log("Success");
        word=random(words);
        level++;
        //new_wordSound.play();
        //if (new_wordSound.isPlaying()){
        //  new_wordSound.stop();
        //}
      }
    }
    
  }
  
}

function setup() {
  createCanvas(400, 400);
  s = new droppedCircle();
  
  for(let i = 0; i < numShapes; i++){
    bubbleArr[i] = new droppedCircle();
  }
  //m= new topText();
  //topText.disp();
  word=random(words);
}

//prints word to screen
function drawWord(Aword){
  let success = 0;
  while (success!=1){
    textSize(100);
    textAlign(CENTER);
    text(Aword,200,100);
    
    success=1;
  }
}

function printScore(L){
    textSize(10);
    textAlign(CENTER);
    text("LEVEL: ", 20,10);
    text(L,50,10);
}

function makeArr(arrWord){
  for (let i=0; i<length.arrWord; i++){
    
    append(texts,arrWord[i]);
    
    
  }
  for (let i=0;i<level;i++){
    append(texts, random(texts1));
  }
}

//HOME SCREEN
function homeScreen(){
  background(img);
  textSize(30);
  textAlign(CENTER);
  text("PWEESE PWES ENTER",200,50);
}


//GAME
function playGame(){
  
  background(rr,gg,bb);
  //image(img,0,0);
  smooth();
  fill("white");
  stroke(20);
  textSize(50);
  text(timer, 350, 50);
  if (frameCount % 60 == 0 && timer > 0) { 
    timer --;
  }
  if (timer == 0) {
    text("GAME OVER", width/2, height*0.7);
    //noLoop();
  }
   
  printScore(level);
  //console.log(word);
  drawWord(word);
  makeArr(word);
  
  //s.display();
  
  for(let i = 0; i < numShapes; i++){
    bubbleArr[i].display();
    if(bubbleArr[i].y > height){
      bubbleArr.splice(i,1);
      numShapes--;
      deletedcircles++;
    }
   for(let i = 0; i < words.size; i++){
     //while ()
    
       
    }
  }
  
  
  // print("numshapes:",numShapes,howmanycircles,deletedcircles);
  
  
  let rand = int(random(0, 100));
  let rand2 = int(random(0, 100));
  if(rand % 7 == 0){
    if(rand2 % 3 == 0){
    bubbleArr[numShapes] = new droppedCircle();
    numShapes++;
      howmanycircles++;
     }
  }
  // print(numShapes,rand);
}

//EXIT SCREEN
function exitScreen(){
    background("black");
    image(gOverimage, 0, 0);
    textAlign(LEFT);
    fill("red");
    text("Press ESC", 250,300);
}

//togoling between game modes
function keyPressed(){
  if (keyCode === ENTER) {
    menu=1;
  } 
  if (keyCode===' '){
    menu=1;
  }
  if (keyCode===ESCAPE){
    menu=0;
  }
}

//main function
function draw() {
  if (timer==1){
    exitScreen();
    
    
    if (menu==0){
      homeScreen();
      timer=30;
    }
  }
  else if (menu==0){
    homeScreen();
    if (keyPressed==1) { menu=1;}
  }
  else if(menu ==1){
    playGame();
  }
  
  //playGame();
  //console.log(timer)
  
  
  
  

}

And the following is the sketch:

  • Problems

A large I could not overcome was the proper implementation of the diplayedWord class, this was intended to highlight every letter of the target word in a different color when the bubble containing the letter is clicked. However, I believe my implementation of the call had a incorrect constructor not allowing the object to be properly created.

The second problem was the program slowing down significantly after the first iteration. This is probably because of the program does not reset the game mode when replaying.

The third problem was the randomization. It proves to be the fatal flaw in the program keeping in mind the purpose and the intended audience. The letters that fall are much too randomized and must have been more concentrated to have provided a better gaming/ learning experience.

 

Midterm Progress

For the midterm project, I will be drawing inspiration from the Snake game on old Nokia phones. The aesthetics of the game will be the same as the one seen below.

However, I want to increase the complexity of program which will randomly generate a moving maze. This is to incorporate a sense of randomness to the game.

Firstly I will have to use the lessons learned in the OOP lectures, to create the Snake and the food class. The snake class will also have a sub class depicting each body component (for length of snake). I have started work on the snake class and body classes.

After this is done we will implement a maze class where certain walls will be in a fixed position and other walls will appear and disappear randomly. I want to make this more frequent when the hallway is further away from snakes head.