1962 – Final Intro to IM project

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.

user testing:

 

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.

Work contributed: Car racing game, Space invaders, while helping in integrating the games together, and using my graphic design skills in making 1962 more user friendly. Also helped making the circuit necessary to send data.

 

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 11 assignment – Tarek Nabih, Janindu Nanayakkara, Aadil Zakareya

Exercise 1:

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();
}

Arduino code for exercise 1:

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);
}

Demo:

 

Exercise 2:

P5js code:

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.");
}

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);
    
  }
}

 

Distance musical instrument, Tarek Nabih & Aadil Zakareya

Concept:

We wanted to create a musical instrument that works without touching anything (of course but the switch button). So we used the ultrasonic sensor to control the frequency of the sound outputted by the buzzer. While coding we found it very annoying for the buzzer to run all of the time, without any option to stop it. Hence, we found it critical to add a button that controls whether the ultrasonic sensor and the buzzer are running. We also wanted to make it fancy in a way that you don’t hold the button for it to work but just press on it to start and press again to stop it, and that’s what we did. The ultrasonic measures the distance by the duration of time of the ultrasonic wave takes to come back, and by that controls the frequency of the buzzer. We also created a feedback loop from the serial by which we can know the distance and the frequency outputted.

Code:

The pins were declared:

int buttonPin = 2;

int echo = 6;
int trig = 5;

int speaker = 9;

long duration;
long distance;

bool isPressed = false;

then the ultrasonic trig and echo were settled up to the pins and the serial was set up as well:

void setup() {
  // setup pin modes
  Serial.begin(9600);

  pinMode(speaker, OUTPUT);
  pinMode(trig, OUTPUT); 
  pinMode(echo, INPUT);
  // pinMode(buttonPin, INPUT);
  // pinMode(buttonPin, INPUT_PULLUP);

}

the void loop basically has everything to do with the switch. a global variable was declared so that we save the last state of the ultrasonic and the buzzer, and when the button is pressed it switches the state. so when if the sensor is not running it makes it start running and vice versa. Moreover, I noticed that a delay had to be added because the void loop runs numerous times while the button is being pressed so it basically turns the sensor on and off numerous times giving us a 50% possibility of tuning the sensors on when the button is released. Hence, the delay is crucial for the project to work.

oid loop() {
  digitalWrite(trig, LOW);
  delayMicroseconds(2);

  int buttonState = digitalRead(buttonPin);
  if(buttonState == HIGH){
    Serial.println("button pressed");
    // delay(5000);
    if(isPressed == false){
      isPressed = true;
      delay(1000);
      
    }else if(isPressed == true){
      Serial.print("came pressed ");
      noTone(speaker);
      isPressed = false;
      Serial.println(isPressed);
      delay(1000);
    }    
    // blink(2);
  }
  // Serial.println(isPressed);
  if(isPressed == true){
    updateSensor();
  }


}

Finally, the function that makes the sensor work, as in Tarek’s previous project the sensor calculates the distance by measuring the time it takes for the waves to go out and come back to the sensor again. based on that it scales the distance from 0 to 100 cm to the frequencies from 0 to 455. then it makes the buzzer start the sound in that specific frequency.

void updateSensor(){
  digitalWrite(trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(trig, LOW);
  duration = pulseIn(echo, HIGH);
  distance = duration * 0.034 / 2; 

  int frequency = ((int)(distance * 4.55) );
  frequency = (frequency > 455)? 455 : frequency;

  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.print(" cm, ");
  Serial.print("frequency: ");
  Serial.println(frequency);

  tone(speaker,frequency);

  delay(200);

}

 

Reflections:

While working on the project we noticed the potential that we have with just this Arduino and minor sensors like the ultrasonic and small gadgets like the buzzer. It also spiked our curiosity to find out what else can be done with such gadgets.

Week 9: Analog and Digital Sensor

Concept:

To construct this project, I had to learn how to use the ultrasonic sensor online, which measures the distance between the sensor and an item in front of it by sending signals and calculating the time it takes for it to return. I set up the ultrasonic sensor but I wanted to control the brightness of an LED lamp by the distance measured by it. Hence, I made the scale of the brightness of the lamb range between 0 cm away from the ultrasonic sensor and 100 cm (1m). 1m(and beyond) being the brightest and 0 being the dullest. To achieve this, I had to multiply the distance measured by the ultrasonic sensor by 2.55; if the distance is more than 100 cms, it tunes it down to 255.

Afterward, I wanted to activate the ultrasonic sensor only when a button is pressed while acknowledging the user that the ultrasonic sensor. Therefore I made two LEDs dependent upon the button that blinks for around 2 seconds, while the ultrasonic sensor takes the input and alters the brightness of the LED bulb that is dependent upon the distance.

Code:

At the beginning of the code, the global variables are initialized with the duration it takes the ultrasonic takes back the variable distance to store it in. with the pins the echo and the trig are connected to.

The LEDS are connected to dynamic or analog pins according to their usage. as well as one pin to check if the button is pressed.

int buttonPin = A0;
int redLed = 2;
int blueLed = 4;

int greenLed = 6;

int echo = 11;
int trig = 10;

long duration;
long distance;

In setup, the pins for the leds and ultrasonic are set up as well as the Serial to know the distance exactly and the brightness of the main LED.

void setup() {
  // setup pin modes
  Serial.begin(9600);
  pinMode(redLed, OUTPUT);
  pinMode(blueLed, OUTPUT);

  pinMode(trig, OUTPUT); 
  pinMode(echo, INPUT);
}

In the loop function, the program continuously checks if the button is pressed, and if it is pressed it calls the function blink.

void loop() {
  digitalWrite(trig, LOW);
  delayMicroseconds(2);

  int buttonState = digitalRead(buttonPin);
  if(buttonState == HIGH){
    blink(2);
  }
}

The blink function makes the red and blue LEDs blink while calling a function to make the ultrasonic sensor start getting data and alter the brightness of the green LED.

void blink(int period){
  period = period * 5;
  for(int i = 0; i < period; i++){
    digitalWrite(redLed, LOW);
    digitalWrite(blueLed, HIGH);
    delay(100);
    digitalWrite(redLed, HIGH);
    digitalWrite(blueLed, LOW);
    delay(100);
    updateSensor();
  }
  digitalWrite(redLed, LOW);
  digitalWrite(blueLed, LOW);

}

The update sensor function gets data for the ultrasonic sensor by getting the time difference it takes for the ultrasonic waves to go out and come back, and by this data calculates the distance. then transforms the distance to a number that can be converted to an analog brightness.

void updateSensor(){
  digitalWrite(trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(trig, LOW);
  duration = pulseIn(echo, HIGH);
  distance = duration * 0.034 / 2; 

  int brightness = ((int)(distance * 2.55) );
  brightness = (brightness > 255)? 255 : brightness;

  Serial.print("Distance: ");
  Serial.print(distance);
  Serial.print(" cm, ");
  Serial.print("brightness: ");
  Serial.println(brightness);


  analogWrite(greenLed,brightness);
}

 

Reflection:

In this project, to be honest I achieved everything I was looking for. However, I was thinking about alerting the user while the ultrasonic sensor is running by a speaker and making the program start in another more interactive way than a button like a sound command recognition. I am not sure how can that be possible since it’s complex and needs AI, but if it’s manageable I am looking forward to working on a project like that.

In my project, the distance is changing is the brightness of the LED lamp. I was thinking about making it change to something more interactive like the speed of a motor.

 

 

 

Muscle flexing switch

Concept:

For this project, I took brainstorming to another level. I actually did multiple projects but since I like gyming (although results not showing) so I decided to do the flexing the way our fathers and grandfathers did it: bicep flexing. Something that bothered me in this is that while connecting the circuit it burned my skin so much that my hands began shaking. However, I liked the project so I just went on with it.

Circuit:

a simple circuit and attach two pieces of aluminum foil on both ends of the wire where the switch would normally go. Attach one piece of foil on the bicep and the other on the forearm. When I begin flexing – the circuit is completed.

Result:

 

Reflection:

It felt really good engaging gyming in any way in an academic course. Looking forward to engaging more with circuits and Arduino.

Assignment 4: Tarek Nabih

Concept:

I had a project in mind: making sentences pop up on the canvas. So I tried making that But I got stuck in the part where I should formulate the sentence. Hence, I got peer help. Ayazhan Gabitkyzy’s code helped me a lot in figuring out what could be done and how to debug my code. There were even useless blocks of code that I realized after I have gotten Aya’s code help. I changed the word combination to predict more logical sentences. And I was pretty satisfied with the result.

 

Code:

let VERB1 = 0;
let WORD1 = 1;
let VERB2 = 2;
let WORD2 = 3;
let words = [];
let wordctr = 0;

let strings = [];

function setup() {
  createCanvas(600,600);
  background(153,0,0);
  strings = loadStrings("words.csv");
  
}

let csvRowNumber = 0;

function generatemessages(){
  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){
          words[wordctr] = new sentences();
          words[wordctr].display();
          wordctr++;
        }
     }
  }
}

function displaymessages(){
  for(let i = 0; i < words.length; i++){
    words[i].display();
    if(words[i].y > height){
      words.splice(i,1);
      wordctr--;
    }
  }
}

function draw() {
//   generatemessages();
//   displaymessages();
  frameRate(3);
  if(wordctr > 30){
    clear();
    background(153,0,0);
    wordctr = 0;
  }
  generate_text();
  
  
}

function generate_text(){
  let singleRow = [];
 
  message = "I want to " ;
  singleRow = split(strings[int (random(strings.length))], ',');
  message += singleRow[VERB1];
  message+=" ";
  singleRow = split(strings[int (random(strings.length))], ',');
  message += singleRow[WORD1];
  
  

  message2 = "I don't want to " ;
  singleRow = split(strings[int (random(strings.length))], ',');
  message2 += singleRow[VERB2];
  message2+=" ";
  singleRow = split(strings[int (random(strings.length))], ',');
  message2 += singleRow[WORD2];
  

  fill(245,213,10);
  textSize(20);
  textFont('Georgia');
  
  let randnumber=int(random(1,3));
  if (randnumber==1){
    text(message,random(0,width-60),random(0,height-2));
    // return message;
    // text(this.message,this.x,this.y);
    wordctr++;
  }
  else if(randnumber==2){
    text(message2,random(0,width-60),random(0,height-2));
    wordctr++;
    // return message2;
    // text(this.message,this.x,this.y);
  }
  
  
  
}


class sentences{
  constructor(){
    this.x = 20;
    this.y = -20;
    this.message = generate_text();
  }
  
  display(){
    // this.tempo.remove();
    this.tempo = text(this.message,this.x,this.y);
    this.y+=20;
  }
  
}

 

Project:

Reflection:

I would love to make add effects to the text in the future like a shadow or make them move on their own.. or add an illustration by which instead of disappearing, the sentences by animation make another sentence.

I was wondering as well if the sentences can be more coherent and more logical. However, I feel like for that to take place there should be a use of AI or machine learning so that the sentences can make sense and will sound like it was written by a human not a machine.

Midterm Project- Tarek Nabih

Inspiration:

I came up with the game while thinking about my childhood games. Before starting to work on the project I kept brainstorming possible ideas that I can work on. One thing in particular that crossed my mind was to make the snake game as it was the main game that we all played on our Nokia devices when we were kids. But I noticed that a lot of people are doing it already so I changed my mind about the second option: “spaceship destroyer”. When I was a kid there was this well-known game in my community and among my friends, we called it “chicken.” In that game, there was this spaceship that was flying in the air and it was firing lasers at chickens while avoiding colliding with their eggs. Of course, it was way more advanced than that as it had multiple powerups and multiple levels and it seemed to have a story. However, I wanted to do something similar. And that’s when I decided to make the game I made. 

 

Challenges:

I think I experienced challenges in every step of coding that game. It all started with setting the size of the canvas. I think I overthought it as I was trying to find the perfect canvas size for the game. I ended up choosing a portrait-like canvas. 

Then I had a problem controlling the spaceship. The arrows were not doing the job. The spaceship’s movement was very weird. I tried multiple ways to make it smoother, but a lot of them didn’t work at first. I eventually found a good setup for the arrows. And I figured out a way to make it go to the right and appear from the left and vice versa to make the user experience smooth.

I think the biggest challenge was to make the bullets hit the rocks falling down. It was just going through it for hours without making any obvious thing at all. After hours of debugging, I figured out a way to make it the rock disappear when the laser hits it.

Code:

controlling the spaceship:

function keyPressed() {
  if(keyCode === ENTER){
     gamestatus = "running";
  }
  if (keyCode === UP_ARROW){
    makebullet(mainship.x,mainship.y);
  }
  if(keyCode === LEFT_ARROW){
    rate = -5;
  }
  if(keyCode === RIGHT_ARROW){
    rate = 5;
  }
  if(keyCode === BACKSPACE){
    rocks.splice(0,rocks.length);
    rocksctr = 0;
  }
  
  
}

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


function keyReleased() 
{
  
  if (keyCode === RIGHT_ARROW || keyCode === LEFT_ARROW) 
  {
    rate = 0;
  }
  if(keyCode === ENTER){
     gamestatus = "running";
  }
  if(keyCode === BACKSPACE){
    rocks.splice(0,rocks.length);
    rocksctr = 0;
  }
  
}

Generating rocks:

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++;
        }
     }
  }
}

catching collisions and displaying rocks:

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();
    }else if(rocks[i].y > height || temp){
      rocks.splice(i,1);
      rocksctr--;
    }
  }
}

Main spaceship class:

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 > width){
      this.x = 0;
    }else if(this.x < 0){
      this.x = width;
    }
  }
  
  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;
      }      
      
    }
  }

}

The game:

 

Reflections:

Even though I faced several difficulties when creating the game and my original concept had to be modified, I am quite pleased with the outcome. Through making mistakes, I gained a lot of knowledge. I also discovered that it’s a pretty good approach to advance to break the problem into smaller challenges. I began the code with simple shapes before starting to add my assets, animation, point system, and other things. I examined the animation code independently and just inserted it once it began to move.

Midterm Project for Tarek Nabih And Aadil zakerya

Concept:

I am aware that none of you enjoyed losing when you played the snake game. In order to avoid seeing the “Game Over” warning, we all loved looking for shortcuts as children. However, as techies, I know you would want to make this “Snake” dance to your beats. In this midterm Project on the Snake Game in P5js, I will demonstrate this to you all.

The goal of the game is to develop the snake that you control using the keyboard’s arrow keys. If the snake strikes an edge or its own body, the game is ended. Collecting apples that are positioned at random on the game board will allow you to grow.

 

Setup:

According to this concept, the gaming board is divided into blocks. Block size and block count are set in separate variables.

The snake advances one block in the direction it is currently travelling in with each loop iteration. The direction is also predetermined in a separate variable and is only altered when the user clicks an arrow key. Nothing more than a few blocks with a unique color scheme make up the snake. Every snake block has a rectangle drawn for it with a background other than the canvas once the location of the blocks is saved in an array.

When the snake slams into its own body or an edge of the game board, the game is over. In both cases, just the head needs to be taken into account because the body always moves in lockstep with the head.

Movement:

When the user hits an arrow key, the snake shifts its direction of travel. The framework’s thekeyPressed() method makes it simple to intercept this event.

The Snake is traveling left if the value in the x-direction is negative, and upwards if the value is negative in the y-direction

 

Food processing:

The most challenging aspect of the overall process is undoubtedly food processing. By selecting random x and y values, the apple is positioned on the board. The apple is only ever placed on a position, though, if no other portion of the snake’s body is currently there. The apple will be placed at random in another location if the snake consumes it. Additionally, the snake expands by one block and the score rises as well.

 

Game scoring:

Last but not least, the start-stop-pause procedure must be built. For this, we use a normal event listener for a button that was previously placed in the HTML. A click then changes a variable that sets the state. This is queried in the game loop. If it is set to pause, most processing is skipped.

 

Code:

the current one goes as follows:

NUM_ROWS = 20;
NUM_COLS = 20;
DIMENSION = 600;

MENU = 0;


TILE_WIDTH = DIMENSION/NUM_COLS;
TILE_HEIGHT = DIMENSION/NUM_ROWS;

SCORE = 0;

function preload(){
    main = loadImage('menu.jpg');
    headleft = loadImage('head_left.png');
    headup= loadImage('head_up.png');
    headright = loadImage('head_right.png');
    headdown= loadImage('head_down.png');
    
    myFont = loadFont('SnakeGameDemoRegular.ttf');
}

function setup(){
    createCanvas(DIMENSION,DIMENSION);
    //frameRate(12);

}

class Head{
    constructor(row, col){
        this.row = row;
        this.col = col;
        this.vc = 0;
        this.rc = 0;
        // this.headup=loadImage('head_up.png');
        // this.headleft = loadImage('head_left.png');
        this.key_handler = {UP: false, DOWN: false,LEFT: false, RIGHT: true};
        this.direction = '';
    }
    update(){
        if (this.key_handler.RIGHT){
            this.direction=RIGHT;
     
            this.headimg = image(headright,((this.col)*TILE_WIDTH)+45,((this.row)*TILE_HEIGHT)+15);
            this.vc = 1; 
            this.vr = 0;
            text("RIGHT",100,100);
           
            
        }
        if (this.key_handler.LEFT){
            this.direction=LEFT;
            this.headimg = image(headleft,(this.col)*TILE_WIDTH,(this.row)*TILE_HEIGHT);
            this.vc = -1; 
            this.vr = 0;
            text("left",100,100);
        }
        if (this.key_handler.UP){
           // this.direction=UP;
            this.headimg = image(headup,(this.col)*TILE_WIDTH,(this.row)*TILE_HEIGHT);
            this.vc = 0; 
            this.vr = -1;
        }
        if (this.key_handler.DOWN){
            //this.direction=DOWN;
            this.headimg = image(headdown,(this.col)*TILE_WIDTH,(this.row)*TILE_HEIGHT);
            this.vc = 0; 
            this.vr = 1;
        }


        if (this.key_handler.RIGHT){
            for(let x = (g.arr.length)-2; x<=1; x--){
                g[i].row = g[i-1].row
                g[i].col = g[i-1].col
                g[i].display()}
            g.arr[1].row = this.row + 1;
            g.arr[1].col = this.col + 1;
            g.arr[1].display();
            
            //this.headimg;
        }
        else if(this.key_handler.UP){
            for(let x = g.arr.length-2; x<=1; x--){
                g.arr[i].row = g.arr[i-1].row
                g.arr[i].col = g.arr[i-1].col
                g.arr[i].display();
        }
        g.arr[1].row = this.row + 1
        g.arr[1].col = this.col + 1
        g.arr[1].display();
        this.headimg;}

    else if(this.key_handler.DOWN){
        for(let x = g.arr.length-2; x<=1; x--){
            g.arr[i].row = g.arr[i-1].row
            g.arr[i].col = g.arr[i-1].col 
            g.arr[i].display()
        }
        g.arr[1].row = this.row + 1
        g.arr[1].col = this.col + 1
        g.arr[1].display();
        this.headimg
        }

    else if(this.key_handler.LEFT){
        for(let x = g.arr.length-2; x<=1; x--){
            g.arr[i].row = g.arr[i-1].row
            g.arr[i].col = g.arr[i-1].col
            g.arr[i].display()
        }
        g.arr[1].row = this.row + 1
        g.arr[1].col = this.col + 1
        g.arr[1].display()
        this.headimg
    }

        this.headimg;

        this.row += this.vr; 
        this.col += this.vc;

    }
    display(){
        this.update();
        this.headimg;
    }


}


class SnakeElement{
    constructor(col, row){
        this.row = row;
        this.col = col;
        //this.clr = clr; 
    }

    display(){
        text("HERE MEOW",100,100);
        if (y.key_handler.RIGHT){
            ellipse((this.col) * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30)}
        if (y.key_handler.UP){
            ellipse((this.col) * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30)}
        if (y.key_handler.DOWN){ 
            ellipse((this.col)  * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30)}
        if (y.key_handler.LEFT){
            ellipse((this.col)  * TILE_WIDTH, (this.row)* TILE_HEIGHT, 30, 30) }                       
        // if (this.clr == 'green'){
        //     fill(80, 153, 32) }
        // if (this.clr =='red'){
        //     fill(173, 48, 32)}
        // if (this.clr =='yellow'){
        //     fill( 251, 226, 76)}
        y.headimg;
    }
}

// class Snake extends Array{
 
//     constructor(){
//         this.push(y);
//         for(let i=0; i<3; i++){
//             this.push(SnakeElement(((NUM_COLS/2))*TILE_WIDTH ,(NUM_COLS/2)*TILE_WIDTH));
//         }
//         this.score = this.length(2-3;
//     }
//     addElem(elem) {
//         this.push(elem);
//     }
//     display(){
//         fill(0,0,0);
//         text("HERE",100,100);
//         this[0].display();
//     }
    
// }


class Snake{
    constructor(){
        this.arr = [];
        this.arr.push(y);
        for(let i=0; i<3; i++){
            let x = new SnakeElement(((NUM_COLS/2))*TILE_WIDTH ,(NUM_COLS/2)*TILE_WIDTH);
            this.arr.push(x);
        }
        this.score = this.arr.length-3;
        //text(this.score,200,200);
    }
    addElem(elem){
        this.arr.push(elem);
    }
    display(){
        this.arr[0].display();
    }
}













let y = new Head(NUM_ROWS/2,NUM_COLS/2);
let g =  new Snake();



function draw(){

    
    
    if (MENU==0){
        background(main);
        textSize(50);
        textFont(myFont);
        fill('white');
        text("SNEK GAME",240,100);


        textSize(30);
        
        fill('white');
        text('START', 450, 240);
        text('Instructions',450, 290);
    }

    if (MENU==1){
        
        if (frameCount%12==0){
            background('black');
            g.display();
        }
        //h.update();
        
        
    }

    if(MENU==2){
        background('white');
    }
    
    print(mouseX, mouseY);


    
    


}



function mouseClicked(){
    if(MENU==0){
        if((mouseX < 516 && mouseX > 448)&&(mouseY < 238 && mouseY > 218)){
            MENU=1;
        }
        if((mouseX < 583 && mouseX > 450)&&(mouseY<291 && mouseY>265)){
            MENU=2;
        }
    }
}

function keyPressed() {
    if (keyCode === UP_ARROW) {
      y.key_handler.UP = true;
      y.key_handler.DOWN = false;
      y.key_handler.LEFT = false;
      y.key_handler.RIGHT = false;

    } 
    if (keyCode === DOWN_ARROW) {
        y.key_handler.UP = false;
        y.key_handler.DOWN = true;
        y.key_handler.LEFT = false;
        y.key_handler.RIGHT = false;
     
    }
    if (keyCode === LEFT_ARROW) {
        y.key_handler.UP = false;
        y.key_handler.DOWN = false;
        y.key_handler.LEFT = true;
        y.key_handler.RIGHT = false;
      
    } 
    
    if (keyCode === RIGHT_ARROW) {
        y.key_handler.UP = false;
        y.key_handler.DOWN = false;
        y.key_handler.LEFT = false;
        y.key_handler.RIGHT = true;
 
    }
    
  }

Reflections:

Currently, our code is not working, we are still debugging to figure out the current problem with it then we are gonna upgrade our program. We are trying to make the snake move in animation and not just be moving in the standard way of a moving square. we hope to make it colorful as well not just black and white as most snake games are these days.

 

 

 

 

Assignment 3: Spaceship

Concept:

At first, I wanted to try to make a race track and multiple race cars running on the track by making boundaries for them so they will never work backward. Then I faced the difficulty of how to make the car title sideways in the bird’s eye view since it’s seen in a birds-eye view. Then I faced the difficulty of making the cars stuck within the boundaries of the race track. So I gave up on that Idea instead I wanted to make spaceships that would move elegantly in the space while the player is controlling one of them.

Code:

let first;
let arr = [];
let numofspaceships = 5;
let player;


function preload() {
  img = loadImage('215c7fdca6033092baa04b35c17466bd.gif');
}

function setup() {
  createCanvas(600, 400);
  first = new Spaceshuttle();
  
  player = new playerShuttle();
  
  for(let i = 0; i < numofspaceships;i++){
    arr[i] = new Spaceshuttle(); 
  }
  
}


function draw() {
  background(220);
  image(img, 0, 0);
  
  
  first.display();
  player.display();
  
  for(let i = 0; i < numofspaceships;i++){
    arr[i].display();
  }
  
  
  
  
  
  
  
}


class Spaceshuttle {
  constructor() {
    this.x = random(width);
    this.y = random(height);
    // this.diameter = random(10, 30);
    this.speed = 10;
    this.redd = random(255);
    this.greenn = random(255);
    this.bluee= random(255);
    this.randomx = random(100);
    this.randomy = random(100);
  }

  move() {
    this.x =  map(noise(this.randomx),0,1,0,width+150);
    this.y =  map(noise(this.randomy),0,1,0,height+150);
    this.randomx += 0.005;
    this.randomy += 0.005;
  }

  display() {
    noStroke();
    fill(0);
    strokeWeight(2);
    stroke(51);
    line(this.x+10,this.y,this.x+20,this.y+15);
    line(this.x-10,this.y,this.x-20,this.y+15);
    stroke(0);
    fill(this.redd,this.greenn,this.bluee);
    ellipse(this.x, this.y, 80, 25);
    fill(0,179,255);
    arc(this.x, this.y, 40, 40, PI, 2*PI , CHORD);
    this.move();
  }
}

class playerShuttle {
  constructor() {
    this.x = random(width);
    this.y = random(height);
    // this.diameter = random(10, 30);
    this.speed = 10;
    this.redd = random(255);
    this.greenn = random(255);
    this.bluee= random(255);
    this.speedx = 0;
    this.speedy = 0;
  }

  move() {
    let rate = 0.1;
    let maxspeed = 3;
    if(keyIsPressed){
      if(keyCode == LEFT_ARROW){
        this.speedx -= rate;
      }else if(keyCode == RIGHT_ARROW){
        this.speedx += rate;
      }
      if(keyCode == UP_ARROW){
        this.speedy -= rate;
      }else if(keyCode == DOWN_ARROW){
        this.speedy += rate;
      }
      
    }else{
      if(this.speedx != 0){
        if(this.speedx > 0){
          this.speedx -= rate * 2;
        }else{
          this.speedx += rate * 2;
        }
      }
      if(this.speedy != 0){
        if(this.speedy > 0){
          this.speedy -= rate * 2;
      }else{
          this.speedy += rate * 2;
        }
      }
    }
    
    if(this.speedx>maxspeed){
      this.speedx = maxspeed;
    }
    
    if(this.speedy>maxspeed){
      this.speedy = maxspeed;
    }
    
    this.x += this.speedx;
    this.y += this.speedy;
  }

  display() {
    noFill();
    strokeWeight(2);
    stroke(250);
    ellipse(this.x, this.y, 80, 80);
    
    noStroke();
    fill(0);
    strokeWeight(2);
    stroke(51);
    line(this.x+10,this.y,this.x+20,this.y+15);
    line(this.x-10,this.y,this.x-20,this.y+15);
    stroke(0);
    fill(this.redd,this.greenn,this.bluee);
    ellipse(this.x, this.y, 80, 25);
    fill(0,179,255);
    arc(this.x, this.y, 40, 40, PI, 2*PI , CHORD);
    
    if(this.x > width+2){
      this.x = 0;
    }else if(this.x <= 0){
      this.x = width;
    }
    
    if(this.y > height+2){
      this.y = 0;
    }else if(this.y <= 0){
      this.y = height;
    }
    
    
    this.move();
  }
}

I think one part, in particular, in the code which took some time online to understand which is figuring out Perlin noise and how can I use it to power the computer spaceships. At first, it was outputting the same value and the spaceships were kind of moving diagonally. Then I figured out that the value inside of “noise()” needs to be changed every time so that it can come up with a new different value. And to fix the diagonal thing just increment the x movement by a number different from the y movement.

Moreover, moving the player’s spaceship with the arrows was also pretty difficult. I tried to use the basic libraries but I think they were not enough so I watched a video that explains how it should be done.

Also making the classes themselves and understanding how the classes work was very interesting. As I at the beginning didn’t understand what does the term “this” mean. But sooner I learned about it and realized the potential in classes. Manipulating the array to make objects of spaceship class was a challenge as well.

The Sketch:

 

Reflections:

Regarding my piece, I’m not really sure of my feelings. I put a lot of effort into it, but I was unable to achieve the degree of originality I had in mind. On the plus side, I think I’ve picked up a lot of knowledge, and I still have a lot to learn. I would improve the piece by making it more intricate, complicated, and possibly animated.