Arduino LED Puzzle

3 hours ago I was sitting in front of my Arduino kit and was absolutely terrified: What on Earth do I do with all these lights and buttons?!!

After a couple of YouTube tutorials and going back to our class recording, I slowly figured out where all these wires go, how to connect a button, and an LED light.

Soo…I managed to get a circuit of 4 buttons and 3 LED lights (let’s call them B1, B2, and G):

  • button #0 does not trigger anything,
  • button #3 triggers two lights – B1 and G,
  • button #2 triggers B1,
  • button #1 triggers G.

If you may have noticed, B2 cannot be triggered by pressing on any of the single buttons, so there has to be some trick around this! Your task is to figure out which and trigger all three lights at the same time (preferably with a minimum number of buttons pressed).

The solution is to press button #0 and #1 simultaneously to trigger B2 and G, and then press button #3 to trigger B1.

Amina’s Arduino

Here is the code:

// declare pins and buttons
int ledPin1 = 2;
int buttonPin1 = 3;

int ledPin2 = 4;
int buttonPin2 = 5;

int ledPin3 = 7;
int buttonPin3 = 6;

int buttonPin4 = 8;

void setup() {
  // put your setup code here, to run once:
  pinMode(ledPin1, OUTPUT);
  pinMode(buttonPin1, INPUT);
  Serial.begin(9600);

  pinMode(ledPin2, OUTPUT);
  pinMode(buttonPin2, INPUT);
  Serial.begin(9600);
}

void loop() {
  // green light
  int buttonState1 = digitalRead(buttonPin1);
  Serial.println(buttonState1);
  digitalWrite(ledPin2, buttonState1); // HIGH = 1 or true

  // green light
  int buttonState2 = digitalRead(buttonPin2);
  Serial.println(buttonState2);
  digitalWrite(ledPin2, buttonState2);

  // blue light
  int buttonState3 = digitalRead(buttonPin3);
  Serial.println(buttonState3);
  digitalWrite(ledPin3, buttonState3);

  // blue and green
  int buttonState4 = digitalRead(buttonPin4);
  if (buttonState4 == buttonState1)
    digitalWrite(ledPin1, buttonState4);
}

Week 7: LED Puzzle (Arduino)

Challenge: Turn on the blue LED!

For this first Arduino exercise, I wired up three LEDs and three buttons in parallel. On my board, they are all mapped logically with the same color of button and LED next to each other. But when clicking, this mental mapping does not completely translate to the output. The red and yellow button work as we would expect them to and turn on the red and yellow LED respectively. The blue button, however, turns on both the red and yellow LEDs but not the blue one. How then can we turn on the blue LED?

Arduino setup.

Watch the video here.

int blueledPin = 2; 
int bluebuttonPin = 3; 
int yellowledPin = 4;
int yellowbuttonPin = 5;
int redledPin = 6;
int redbuttonPin = 7;

void setup() {
  //BLUE button + LED
  pinMode(blueledPin, OUTPUT); 
  pinMode(bluebuttonPin, INPUT);
  Serial.begin(9600); 

  //YELLOW button + LED
  pinMode(yellowledPin, OUTPUT); 
  pinMode(yellowbuttonPin, INPUT);
  Serial.begin(9600); 

  //RED button + LED
  pinMode(redledPin, OUTPUT); 
  pinMode(redbuttonPin, INPUT);
  Serial.begin(9600); 
}

void loop() {
  //BLUE button triggers RED and YELLOW LED
  int bluebuttonState = digitalRead(bluebuttonPin);
  Serial.println(bluebuttonPin); 
  digitalWrite(redledPin, bluebuttonState);
  digitalWrite(yellowledPin, bluebuttonState);

  //YELLOW button triggers YELLOW LED
  int yellowbuttonState = digitalRead(yellowbuttonPin);
  Serial.println(yellowbuttonPin); 
  digitalWrite(yellowledPin, yellowbuttonState);

  //RED button triggers RED LED
  int redbuttonState = digitalRead(redbuttonPin);
  Serial.println(redbuttonPin); 
  digitalWrite(redledPin, redbuttonState);

  //if YELLOW and RED button pressed turn on BLUE LED
  if (redbuttonState == yellowbuttonState) {
    digitalWrite(blueledPin, redbuttonState);
    }
}

Solution: Pressing the red and yellow button together will turn on the blue LED.

Mid term final documentation- The Shining.

Documentation:

  • The project started out as not knowing which artistic direction to take. Initially I wanted to create a tricycle based POV going through the halls of the outlook hotel inspired by the shining.
  • It was too challenging, so I tried to make a candy based game where when points were won, the character’s waist would grow in size.
  • I achieved some elements of it. But not fully (you can find the documentation for that in older posts.)
  • Finally, I decided to combine the two. A ghost going through the out look hotel, to find Johnny in the secret door if he does not collide with the falling candy droplets.
  • What I learned:
  • How to structure code more efficiently.
  • How to use classes and objects.
  • Deal with growing code.
  • compartmentalize
  • writing better comments
  • What I can improve on:
  • complex game mechanics
  • isolating code
  • sticking with one idea

mid term – zip file

Midterm: It’s all under control…

Building upon what I had already, I decided to develop my game much further.

Initial issues

I had many issues at first which include: pressing any key would make Trump move in place, if you held down an arrow key and jumped you stopped moving once you landed, the jump was very abrupt and strictly vertical which was weird for the level of realism in the game, shooting would stop the motion of Trump, and the bullets would appear but not move at all.

Resolving issues, help from Jack

I resolved these issues using many different lines of code where instead of doing most of the work in the main code, I kept creating functions which do the tasks in order for me to be able to keep calling them in multiple situations. I made sure to add “keyCode = …” under each if condition if keyPressed and otherwise just keep direction = direction, which even enabled me to allow motion using w, a, s, d as well. Instead of just using keyPressed in draw loop for all, I used the function one outside to call on the act of shooting and jumping just once whenever the keys are pressed and not every frame.

By asking Jack for some help, we figured out I must also use the keyReleased function outside the draw loop to decide what the game should look like the moment the player releases the important keys to keep the playing going. Jack also helped me with the concept of flying the bullet where I had the code in the main tab and the motion was called upon once in the draw loop but we made it a function in the class instead that is called upon continuously. I also added a small increment/decrement in the x-dimension for Trump when he jumps sideways to make it more realistic.

Jack also introduced the concept of saving the previous frame to me, where I used it to make sure no two bullets are created within a certain amount of frames, and similarly with the coronas. This made the game so much more realistic and intuitive, especially when you keep holding the shooting key.

Further development

I then brought an image of a sanitizer and made it smaller, and then made it seem as though Trump is holding it in his right hand (because he is right-handed) by ordering the image printing accordingly. I had many options for choosing the corona character but I chose this funky one because it is rather satirical, which is the obvious theme of my game, and because it is visible in the green without disturbing the eye like if it was red.

I chose the title based on the actual quote of Trump about COVID-19, and I chose the sentence printed out when you lose as also another quote from Trump about COVID-19, which just fits the theme perfectly. I added the US flag and used an online photo editor called “Pixlr X” to make it more transparent and to add the individual instructions all on the background. I decided to make the instructions visually aesthetic so as to not bore the player and to be very intuitive, and because the game is satirical in nature so it need not be too complicated or serious. I added that face at the end and then added the sound from a YouTube video of a mashup of Trump saying ‘no’ to resemble his hilarious failure and annoyance from getting infected with the virus, which happened in real life despite him making fun of COVID’s repercussions. Also, the sound is annoying to encourage the player to restart the game.

original flag background
background after edits

I added some sound effects for the shooting (squirt sound), killing a corona particle (pop sound) which were both dropped down in amplitude to not be too noisy, and a background music of the US national anthem but an 8bit version of it from YouTube to go along with the clipart theme of the game while maintaining Trump’s Republican Party’s patriotic notion. The music drops down in volume as soon as you start playing so as to draw you attention to the visuals, so you can hear the pop and squirt sounds, and so it does not become too noisy and loud.

I used processing examples to build the button used for playing the game, making it clickable and changing its color when you hover over it.

As suggested in the class, I added a black fence resembling the real one in the White House to show where exactly Trump cannot run anymore on the ground in a visibly clear manner.

fence I used to show the limit of trump’s motion

Personal Feeling

The game has a fun a political taste to it while being very relevant to our current situation in the COVID-19 pandemic. I am a Muslim Arab man and Trump is a very significant man in my life because of the actions he has done towards immigrants of my race and because he is the president of the most influential country in the world. Even though I am an American citizen, I am still very pissed off by what he has done and I have many loved ones who were harmed by the consequences of his actions. As an Egyptian, our way of expressing emotions is always through satire and comedy, and this is my simple way of expressing how I feel about this man right now in 2020.

starting page with game instructions
how the game itself looks
what comes up when you lose

Final Version

If you want to try out the game yourself, here is a zip file of the code:

midterm

Main Code:

import processing.sound.*;    //importing sound library

PImage whiteHouse, sanitizer, laser, corona, trumpBack, americaFlag, fence;     //all images needed
PImage spritesheet;      //sprites of trump
boolean gameOn;        //condition for game
float prevFrame = 0;      //initializing value for previous frame
float randX, randY;      //random numbers for coordinates of creation of corona particles
ArrayList <Laser> lasers;    //initializing arraylist for sanitizer bullets
ArrayList <Corona> coronas;    //initializing array list for corona particles
int score = 0;          //initializing score to 0
PFont f;              //f object for all the texts in the game
int luck;            //randomizing number to make corona particles start motion towards the left or the right
int buttonX, buttonY;      // Position of button
int buttonWidth, buttonHeight;     //dimensions of play button
color buttonColor, baseColor;    //colors of play button
color buttonHighlight;
color currentColor;
boolean buttonOver;
boolean instructions;        //condition for whether instructions page is open or not
SoundFile click, squirt, pop, trumpSound, anthem;      //all sounds in the game

Trump trumps;      //initializing trump character 


//button logic inspired from https://processing.org/examples/button.html

void setup() {
  size(720,477);
  
  instructions = true;      //start game with instructions page
  buttonOver = false;      //start with condition of button pressed false
  gameOn = false;          //don't start game till play is clicked
  
  
  f = createFont("Raleway-Bold.ttf",72);      //aesthetic font that fits teh theme
  
  whiteHouse = loadImage("house4.png");      //white house background pic
  spritesheet = loadImage("trump.png");    //sprites of trump
  sanitizer = loadImage("sanitizer.png");    //sanitizer image
  laser = loadImage("laser.png");            //sanitizer bullet image
  corona = loadImage("corona3.png");          //corona particle image
  trumpBack = loadImage("trumpbackground.jpg");      //trump face when loses image
  americaFlag = loadImage("americaFlag3.jpg");      //america flag background in the beginning with the instructions image
  fence = loadImage("fence4.png");      //black fence image
  
  click = new SoundFile (this, "click.mp3");      //sound when you click
  squirt = new SoundFile (this, "squirt.mp3");       //sound when you shoot
  pop = new SoundFile (this, "pop.mp3");      //sound when you kill corona particle
  trumpSound = new SoundFile (this, "no.mp3");      //long sound playing when you lose until you click anywhere to restart
  anthem = new SoundFile (this, "anthem (6).mp3");      //8bit anthem background music
  
  trumps = new Trump(width/2, height -40);        //creates instance of class trump
  lasers = new ArrayList();        //initializes sanitizer bullets list
  coronas = new ArrayList();      //initializes coronas list
  
  buttonColor = color(0);
  buttonHighlight = color(51);
  baseColor = color(102);
  currentColor = baseColor;
  buttonWidth = 150;
  buttonHeight = 60;
  buttonX = width/2;
  buttonY = 3*height/4 + 30;        //initializations of the button color, color when hovered upon, dimensions, and position
  
  anthem.loop();      //keep the anthem playing on loop always
  trumpSound.amp(0.001);      //reduce the amplitude of trump sound to nothing so it is not heard now
  trumpSound.loop();      //keep the trump sound playing continuously in the background
  
  rectMode(CENTER);
  imageMode(CENTER);
}

void draw() {
  
  if (gameOn == true && instructions == false) {      //if game is one the instructions page is not opened
    //trumpSound.pause();
    background(whiteHouse);
    image(fence, width/6, height -145, width/3, 80);
    image(fence, width/3 + width/6, height -145, width/3, 80);
    image(fence, 2*width/3 + width/6, height -145, width/3, 80);      //draw the fence three times side by side
    
    if (keyPressed) {
        if (keyCode == DOWN || key == 's') {
          keyCode = DOWN;                    //handling keyCode when key = s is pressed instead, because I use keyCode throughout teh code
          trumps.direction = 0;
          trumps.move();
        }
          
        else if (keyCode == LEFT || key == 'a') {
          keyCode = LEFT;
          trumps.direction = 3;
          trumps.move();
        }
        else if (keyCode == RIGHT || key == 'd') {
          keyCode = RIGHT;
          trumps.direction = 1;
          trumps.move();
        }
        
        else if (keyCode == UP || key == 'w') {
          keyCode = UP;
          trumps.direction = 2;
          trumps.move();                                    //handling the moements of trump, whether using keys or using w, a, s, d
        } 
        else {
          trumps.direction = trumps.direction;
        }
    }
      
      if (trumps.direction == 3) {
        pushMatrix();
        translate(trumps.x - 23, trumps.y + 15);
        //rotateY(radians(180));
        image(sanitizer, 0, 0);
        popMatrix();                      //draw sanitizer direction right
      }
      
       if (trumps.direction == 2) {
        image(sanitizer, trumps.x + 20, trumps.y + 8);      //draw sanitizer direction up
      }
      
  
      image(trumps.sprites[trumps.direction][trumps.step], trumps.x, trumps.y);      //drawing trump himself
      trumps.update();                    //calling on gravity
      
      if (trumps.direction == 1) {
        image(sanitizer, trumps.x + 23, trumps.y + 15);      //draw sanitizer direction left
      }
      
      if (trumps.direction == 0) {
        image(sanitizer, trumps.x - 15, trumps.y + 20);        //draw sanitizer direction down
      }
      
      if (frameCount % 100 == 0) {
        if (coronas.size() < 8) {            //setting limit of 8 coronas at a time
           luck = (int)random(100);
           randX = random(100, width - 100);
           randY = random (100, height - (190 + trumps.h));
           
           
           Corona temp = new Corona(randX, randY, luck);
           coronas.add(temp);
        }
      }                                                   //create coronas after a certain number of frames at random starting positions and random starting direction of left or right
      
      for (int i = coronas.size()-1; i >= 0; i--) {
        Corona current = coronas.get(i);
        current.display();
        current.move();                                    //call on functional methods of corona class for each of them
        
        if (current.checkTrump(trumps.x, trumps.y)) {          //checking if corona collided with trump
          anthem.stop();          //stop the background music
          gameOn = false;          //game condition false
          break;                //break from the conditions to enter others
        }
      }
      
      for (int i = lasers.size()-1; i >= 0; i--) {
        Laser laser = lasers.get(i);
        laser.display();
        laser.fly();                //call on functional methods of sanitizer bullets for each object
        
        if (laser.checkEdges()) {        //check if laser is out of frame to remove it
          lasers.remove(laser);
        }
        
        for (int j = coronas.size()-1; j >= 0; j--){        //iterate through all coronas
          Corona important = coronas.get(j);
          if (laser.checkCollision(important.coronaX, important.coronaY, important.coronaWidth)) {      //if bullet hits corona
            pop.amp(0.1);
            pop.play();      //make pop sound
            coronas.remove(important);       //remove hit corona
            lasers.remove(laser);           //remove the bullet that hit it too
            score += 10;        //increment score by 10
          }
          //pop.stop();
        }
        
      }
      
      textAlign(CORNER);
      String Score = "Score: ";          //constantly show score at the corner
      //fill(0);
      //rect(width - 130 , height - 100, textWidth(Score) + 10, 50);
      textFont(f,20);
      fill(255);
      
      
      text(Score + score, width - 115, height - 20);        //score text bottom right
    
  }
  
  else if (instructions == true && gameOn == false) {        //starting phase of instructions pre-game
    
    background(255);
    update();
  
    if (buttonOver) {
      fill(buttonHighlight);            //if mous hovers over button change color
    } 
    else {
      fill(buttonColor);
    }
    
    image(americaFlag, width/2, height/2);        //image of flag with game instructions on it
    
    stroke(255);
    rect(buttonX, buttonY, buttonWidth, buttonHeight, 20);      //play button
    
    textFont(f,25); 
    textAlign(CENTER, CENTER);
    fill(255);
    String play = "PLAY";          //text in play button to clarify it
    text(play, buttonX , buttonY - 3);
  }
  
  else if (instructions == false && gameOn == false) {      //if player loses
    anthem.stop();      //stop background music
    //trumpSound.play();
    trumpSound.amp(0.8);      //increase trump saying no to be heard

    
    
    background(trumpBack);      //trump's face
    textAlign(CENTER);
    
    textFont(f,40); 
    //same shade as in the flag
    fill(#BF0A30);
    String first = "'This was unexpected...'";
    text(first, width/2 , (height/4) - 90);
    
    textFont(f,24);
    //same shade as in the flag
    fill(#002868);
    String second = "CLICK ANYWHERE TO TRY AGAIN";
    text(second, width/2 - 5, height/2 + 215);      //game over text
    
    textAlign(CORNER);
    textFont(f,20);
    fill(0);
    String third = "Score: ";
    text(third + score, width - 115, height -20);      //final score text
    
   
  }
}

void keyPressed()
{
   if(key == ' '){
      if (frameCount - prevFrame > 15) {      //only allow jumping constantly after certain number of frames
        prevFrame = frameCount;
        trumps.jump();      
      }
   }
   
   if(key == 'e') {          //shoot sanitizer bullet
   trumps.recoil();           //recoil when trump shoots
      if (frameCount - prevFrame > 20) {        //only allow multiple shooting after certain number of frames if key is held
        squirt.amp(0.2);        //play squirt sound
        squirt.play();
        prevFrame = frameCount;
        Laser temp = new Laser(trumps.x, trumps.y, trumps.direction);        //create new bullet
        lasers.add(temp);      //add it to teh list
      }
    }
}

void update() {
  if ( overButton(buttonX, buttonY, buttonWidth, buttonHeight) ) {      //check if mouse is over the button to change color
    buttonOver = true;
  } 
  else {
    buttonOver = false;
  }
}

void keyReleased() {
  if(key == ' ') {          //keep trump in correct direction and keep moving even while/after jumping
    if (trumps.direction == 0) {
        keyCode = DOWN;              
      }
        
      if (trumps.direction == 3 ) {
        keyCode = LEFT;
      }
      if (trumps.direction == 1) {
        keyCode = RIGHT;
      }
      
      if (trumps.direction == 2) {
        keyCode = UP;
      } 
  }
  
  if(key == 'e') {        //keep trump in correct direction and keep moving even while/after shooting
    //squirt.stop();
    if (trumps.direction == 0) {
        keyCode = DOWN;
      }
        
      if (trumps.direction == 3 ) {
        keyCode = LEFT;
      }
      if (trumps.direction == 1) {
        keyCode = RIGHT;
      }
      
      if (trumps.direction == 2) {
        keyCode = UP;
      } 
  }
}

boolean overButton(int x, int y, int buttonWidth, int buttonHeight)  {            //check if mouse is over button
  if (mouseX >= x - buttonWidth/2 && mouseX <= x + buttonWidth/2 && mouseY >= y - buttonHeight/2 && mouseY <= y + buttonHeight/2) {
    return true;
  } else {
    return false;
  }
}

void mouseClicked() {
  if (buttonOver) {      //check if mouse is clicked over button then start game
    
      click.amp(0.3);
      click.play();
      instructions = false;
      
     if (gameOn == false){        //if mouse is clicked when the game is off, turn it on and reset everything to zero and empty lists
        //trumpSound.stop();
        //instructions = false;
        score = 0;
        gameOn = true;
        anthem.amp(0.4);
        anthem.loop();        //replay background music
        trumpSound.amp(0.001);
        for (int i = coronas.size()-1; i >= 0; i--) {       //reset coronas list
          coronas.remove(i); 
        }
        for (int i = lasers.size()-1; i >= 0; i--) {         //reset bullets list
          lasers.remove(i);   
        }
      
    }
    
  }
  
}

Corona class, contains three functions: displaying, moving around, and checking if it collided with Trump:

class Corona {
  float coronaX, coronaY;
  float xSpeed;
  float ySpeed;
  float xDirection, yDirection;
  float coronaWidth, coronaHeight;
  int rand;
  
  
  Corona(float _x, float _y, int _rand) {
    
    coronaX = _x;
    coronaY = _y;
    
    coronaWidth = 100;
    coronaHeight = 100;
    
    xDirection = 1;
    yDirection = 1;
    
    xSpeed = 2;
    ySpeed = 2;
    
    rand = _rand;
    
  }
  
  void display() {
    image(corona, coronaX, coronaY, coronaWidth, coronaHeight);      //display corona image
  }
  
  void move() {
    
    if (rand > 50) {              //if random number is this then move in right direction
      coronaX += (xSpeed * xDirection);
      coronaY += (ySpeed * yDirection);
    }
    
    else {                    //if random number is less tha or equal to 50, move in the left direction
      coronaX -= (xSpeed * xDirection);
      coronaY -= (ySpeed * yDirection);
    }
    
    if ((coronaX + (coronaWidth/2) > width) || (coronaX - (coronaWidth/2) < 0)) {      //if corona hits either walls horizontally switch direction of motion to bounce
      xDirection *= -1;
    }
    
    if ((coronaY + (coronaHeight/2) > height) || (coronaY - (coronaHeight/2) < 0)) {        //if corona hits either walls vertically switch direction of motion to bounce
      yDirection *= -1;
    }
   
  }
  
  boolean checkTrump(float _x, float _y) {          //check if it collided with trump
     float otherX = _x;
     float otherY = _y;
     if (dist(coronaX, coronaY, otherX, otherY) < coronaWidth/2) {
       return true;
     } 
     else {
       return false;
     }
  }


}

Sanitizer Class, has 4 functions: displaying the sanitizer bullet, flying the bullet in the appropriate direction, checking if it collided with a corona particle, and checking if it is outside the frame of the game:

//code inspired from https://forum.processing.org/two/discussion/1324/how-to-create-a-bullet-array-for-asteroids-game
class Laser {

  int laserDirection;
  float laserX, laserY;
  float xSpeed;
  float ySpeed;
  float laserWidth, laserHeight;


  Laser(float _x, float _y, int direction) {
    
    laserDirection = direction;
    xSpeed = 8;
    ySpeed = 8;
    
    if (laserDirection == 0) {
      laserX = _x - 15;
      laserY = _y + 23;
    }
    if (laserDirection == 1) {
      laserX = _x + 23;
      laserY = _y + 15;
    }
    if (laserDirection == 2) {
      laserX = _x + 23;
      laserY = _y - 15;
    }
    if (laserDirection == 3) {
      laserX = _x - 23;
      laserY = _y + 15;
    }
                                          //initialize direction of laser so it remains so even if trump changes his direction later
    laserWidth = 60;
    laserHeight = 20;
    
   }
  
   void display() {
     if (laserDirection == 2){
       pushMatrix();
         translate(laserX, laserY - 20);
         rotate(HALF_PI);
         image(laser, 0, 0, laserWidth, laserHeight);
       popMatrix();
       
     }                                                  //rotate bullet to shoot up
     
     else if( laserDirection==0) {
     pushMatrix();
         translate(laserX, laserY + 25);
         rotate(HALF_PI);
         image(laser, 0, 0, laserWidth, laserHeight);
       popMatrix();
     }                                            //rotate bullet to shoot down
     
     else if ( laserDirection==1){
       image(laser, laserX + 28, laserY - 18, laserWidth, laserHeight);      //bullet shoot right
     }
     
     else if ( laserDirection==3){
       image(laser, laserX - 28, laserY - 18 , laserWidth, laserHeight);      //bullet shoot left
     }
   }
   
   void fly() {
     if (laserDirection == 3) {        //left
       laserX -= xSpeed;
     }
     
     if (laserDirection == 1) {        //right
       laserX += xSpeed;
     }
     
     if (laserDirection == 2) {        //up
       laserY -= ySpeed;
     }
     
     if (laserDirection == 0) {        //down
       laserY += ySpeed;
     }
                                                        //movement according to the direction
   }
   
   boolean checkCollision (float _x, float _y, float w) {      //check collision with corona coordinates
     float otherX = _x;
     float otherY = _y;
     if (dist(laserX, laserY, otherX, otherY + 10) < w/2 ) {
       return true;
     } 
     else {
       return false;
     }
   }
   
   boolean checkEdges() {
     if ((laserX - laserWidth/2 > width) || (laserX + laserWidth/2 < 0) || (laserY - laserHeight/2 > height) || (laserY + laserHeight/2 < 0)) {      //check if laser is outside frame to remove it
       return true;
     }
     else {
       return false;
     }
   }
  

}

Trump class, has 4 functions: updating the y-position of Trump according to gravity,  moving the character around in the appropriate directions, recoiling Trump backwards whenever he shoots to make it more realistic, and jumping:

class Trump {
  
  PImage[][] sprites;
  int direction = 1;
  int step = 0;
  int w, h;
  float x, y;
  float xspeed = 6;
  float yspeed = 6;
  float gravity;
  
  Trump(float _x , float _y){
  sprites = new PImage[4][6];
  
  w = spritesheet.width/6;
  h = spritesheet.height/4;
  
  gravity = 0.0001;

  for (int y=0; y < 4; y++) {
    for (int x=0; x< 6; x++) {
      sprites[y][x] = spritesheet.get(x*w, y*h, w, h);      //creating each instance's individual sprite from spritesheet
    }
  }

  x = _x;
  y =_y;
  
  }
  
  void update() {        //if trump is in the air make him go down through gravity
  
    if (y - h/2 < height - 190.5) {
      yspeed = yspeed + gravity;
      y += yspeed;
    }
  }
  
  void move() {
    if (direction == 0) {
      if (y + (h/2) < height) {    //move down until frame limit
          y += yspeed;
      }
    }
    if (direction == 1) {      //right till wall
      if (x + (w/2) < width) {
          x += xspeed;
        }
    }
    if (direction == 2) {      //up till fence
      if (y - (h/2) > height - 189) {
          y -= yspeed;
        }
    }
    if (direction == 3) {      //left till wall
      if (x - (w/2) > 0) {
          x -= xspeed;
        }
    }                                //move according to direction
    
    if (frameCount % xspeed == 0) {      //transition from one sprite to another
        step = (step+1) % 6;
    }
  }
  
  void recoil() {        //push trump slightly backwards when he shoots to be more realistic
    if (direction == 0) {      //down
      if (y - (h/2) > height - 189) {    //go up till fence
          y -= yspeed/4;
      }
    }
    if (direction == 1) {      //right
      if (x - (w/2) > 0) {      //left till wall
          x -= xspeed/4;
        }
    }
    if (direction == 2) {      //up
      if (y + (h/2) < height) {    //down till frame limit
          y += yspeed/4;
        }
    }
    if (direction == 3) {      //left
      if (x + (w/2) < width) {      //right till wall
          x += xspeed/4;
        }
    }
    
    //if (frameCount % xspeed == 0) {
    //    step = (step+1) % 6;
    //}
  }

  void jump() {
    
    if (y - (h/2) > height - 191) {      //if trump is not already flying make him jump
      y -= 150;
      
      if (direction == 3 ) {      //if he was moving to left push him slightly to left
        if (x - (w/2) > 0) {
          x-= 20;
        }
      }
      else if (direction == 1) {      //if he was moving to right push him slightly to right
        if (x + (w/2) < width) {
          x += 20;
        }
      }
    }
      
  }
  
  
}

 

 

Week 6: Midterm Project (Sounds Like NYUAD)

Remind me to never create a sound-based game again.

This project was a roller coaster, there were some days where I would take the entire day just to debug one tiny thing, and some days where I could make a huge advancement with little resistance. Thankfully, I am happy to where I got.

Trying to make sense/ organzie/ my old code

Since I already had a python based code, starting the game was easy. Myfirst few elements, the image, the map, the player were easy to achieve, but it changed fast. When I got to the borders, I had to redo a large aspect of what it used to be. I refined them, which was time-consuming because of how I had to keep track of X and Y values of the borders since I individually drew them. Then figuring out the movement of the player in relation to the borders was a nightmare. It was nothing related to my previous code, and after spending the whole day on it, I resorted to pencil and paper, trying to figure out how to code the movement. Here’s my sad sketch:

After that celebratory dance, I was on to other elements, like the cats. This proved to be relatively easy, since I had coded this kind of thing before. You hit a cat, you die. It was simple. It was so easy, as a matter of fact, I thought it would be great if I would add another element, humans. I thought “they’re so simple, and I could just make it so that if I hit them, I lose points and time, I don’t die.” After spending a few hours sketching 5 illustrations of humans, and adding it to my code, I realised I had misjudged how ‘easy’ it was. My issue was that I had my code so that whenthe distance between the player and the object is less than an amount, the score decreases. The issue with this is that it would continuously decrease until the object is out of bounds when I just wanted it to decrease once. Eventually, I did it in a way where once it hits the object, a boolean is triggered to true, you lose the points, and then after 1 second, the boolean goes back to false, and you can hit it again. (you probably shouldn’t though).

Other elements I was excited to add were things like adding a score, where it was calculated based on your time (the faster the better) and each sound you find, and it tells you your score at the end. (A very small thing I added was changing the score to green when it’s positive, red when it’s negative). Another thing was also adding a tally on the top left of the screen, to see how many sounds left to find, as playing it I realized I was always wondering how many I had left.

Another element I added (although it doesn’t look as nice as I wanted it to), was having the location you are at glow in gold for a second to indicate that you found the sound in that spot. I also made a ‘ding’ sound to be heard when you get a point, so that you know it happened. I changed the main players’ face to my off-brand illustration of the NYUAD falcon, which I’m not a fan of, but someone pointed out to me that with the addition of humans, the player cannot be sure which one they are controlling.

The sound was tricky, because there were so many bugs that kept showing up. Java only allows sound in the main program, not in classes, so I had to keep experimenting and trying to see how to fix these issues. For example, having the sounds in an array didn’t work. Often times the sounds would overlap because of the draw loop triggered over and over again, especially when mixed with restarting, the sounds would layer over each other. My main solution was to make sure that when the game runs, all the sounds are playing but on mute, so when a location is triggered, the sound unmutes, instead of just playing at a random tempo. Also made sure for the song to keep playing when you win, so you can hear your music, but to stop playing when you die.

Moving on to the restart, instruction, and game over screens. These where also a struggle to figure out, because instead of having most of my initiations in a class like I would normally do, I had it in the main page to correlate with the sounds. Eventually, I figured it out, having functions for each screen, and then debugging for a while. I also had to redesign a few elements for the screens.

int gameScreen = 0;
PImage dead, won, instructions;
boolean trigger = false;
Game game;
Creature creature;
Cats[] cats;
Humans[] humans;
Borders[] horizentalBorders;
Borders[] verticalBorders;
Locations[] locations;
//SoundFile[] sounds;
SoundFile backgroundSound, meow, ding, baraha, alarm, dining, library, knock, palms, theatre;

void setup() {
  //This trigger is just fir the cat meow
  trigger = false;
  creature = new Creature (50, 450, 10);
  game = new Game (1024, 786, 585);
  cats = new Cats[3];
  humans = new Humans[3];
  //for displaying the cats and humans, in random places and speeds
  for ( int g = 0; g< 3; g++) {
    cats[g] = new Cats (int(random(200, 900)), int(random(200, 600)), 15, 70, 70, 200, 800);
    humans[g] = new Humans (int(random(200, 900)), int(random(200, 900)), 15, 70, 70, 200, 800);
  }
  //Creating the locations for the sound to br triggered. These dont change
  locations = new Locations[7];
  locations[0] = new Locations (250, 270, 100); // library
  locations[1] = new Locations ( 250, 470, 100); // Palms
  locations[2] = new Locations (400, 310, 50); //baraha
  locations[3] = new Locations (790, 250, 100); //theatre
  locations[4] = new Locations (950, 400, 100); //dining
  locations[5] = new Locations (800, 620, 200); // knock
  locations[6] = new Locations (570, 570, 200); // alarm
  size(1024, 768);
  background(255, 255, 255);
  //borders of the map, where you cannot enter from
  horizentalBorders = new Borders[14];
  verticalBorders = new Borders[13];
  horizentalBorders[1] = new Borders (444, 286, 575, 286); //h
  horizentalBorders[2] = new Borders (442, 346, 520, 346); //h 
  horizentalBorders[3] = new Borders (402, 470, 502, 470); //h
  horizentalBorders[4] = new Borders (590, 472, 712, 472); //h
  horizentalBorders[5] = new Borders (754, 493, 812, 493); //h
  horizentalBorders[6] = new Borders (70, 175, 440, 175); // h
  horizentalBorders[7] = new Borders (70, 375, 422, 375); //h
  horizentalBorders[8] = new Borders (310, 725, 970, 725); //h
  horizentalBorders[9] = new Borders (530, 527, 586, 527); //h 
  horizentalBorders[10] = new Borders (536, 595, 586, 595); // h
  horizentalBorders[11] = new Borders (350, 614, 396, 614); //h
  horizentalBorders[12] = new Borders (754, 602, 808, 602); // h
  horizentalBorders[0] = new Borders (756, 640, 808, 640); // h
  verticalBorders[12] = new Borders (440, 175, 440, 270); //h
  verticalBorders[1] = new Borders (540, 346, 540, 455); //v
  verticalBorders[2] = new Borders (582, 286, 582, 456);// v
  verticalBorders[3] = new Borders (70, 175, 70, 375); //v
  verticalBorders[4] = new Borders (310, 572, 310, 725); //v
  verticalBorders[5] = new Borders (970, 525, 970, 725);  // v
  verticalBorders[6] = new Borders (775, 675, 775, 720); // v
  verticalBorders[7] = new Borders (800, 675, 800, 720); //v 
  verticalBorders[8] = new Borders (586, 527, 586, 595); // v
  verticalBorders[9] = new Borders (536, 529, 536, 595); //v
  verticalBorders[10] = new Borders (396, 614, 396, 664); //v
  verticalBorders[11] = new Borders (808, 602, 808, 640); // v
  verticalBorders[0] = new Borders (756, 602, 756, 640); // v
  //if you die- page
  dead = loadImage("sorry.png");
  //if you win - page
  won = loadImage("finalpage.png");
  //instructional page
  instructions = loadImage("instructions.png");
  //loading the sounds of the song
  backgroundSound = new SoundFile(this, "OriginalBacktrack.mp3");
  baraha = new SoundFile(this, "CardSwipe.mp3");
  knock= new SoundFile(this, "Knock_RA.mp3");
  theatre = new SoundFile(this, "SneezeCough.mp3");
  library = new SoundFile(this, "Typing_clicking.mp3"); 
  palms = new SoundFile(this, "Birds.mp3");
  dining = new SoundFile(this, "Didyoudothereading.mp3");   
  alarm = new SoundFile(this, "iPhoneAlarm.mp3"); 
  meow = new SoundFile(this, "meow.mp3");
  ding = new SoundFile(this, "Ding.mp3");

//playing all the sounds in loop for easier restart (less bugs), all on mute except background, 
// this is so that whenever they are triggered they are still on tempo for the song
  backgroundSound.play();
  baraha.loop();
  baraha.amp(0.0);
  knock.loop();
  knock.amp(0.0);
  theatre.loop();
  theatre.amp(0.0);
  library.loop();
  library.amp(0.0);
  dining.loop();
  dining.amp(0.0);
  alarm.loop();
  alarm.amp(0.0);
  meow.amp(0.5);
  //restart();
}

void sound() {
  // locating each location for their respective sound, because an array of sounds did not work
  if ( locations[0].trigger == true) {
    library.amp(1.0);
    println("library");
  }
  if ( locations[1].trigger == true) {
    palms.amp(1.0);
  }
  if ( locations[2].trigger == true) {
    baraha.amp(1.0);
  }
  if ( locations[3].trigger == true) {
    theatre.amp(1.0);
  }
  if ( locations[4].trigger == true) {
    dining.amp(1.0);
  }
  if ( locations[5].trigger == true) {
    knock.amp(1.0);
  }
  if ( locations[6].trigger == true) {
    alarm.amp(1.0);
  }
}

void initScreen() {
  //initial screen, instruction page
  image(instructions, 0, 0);
  text("Click anywhere to begin ! ", 300, 700);
}

void restart() {
  // upon restart
  // this was a response to a cat bug
  trigger = false;
  creature = new Creature (50, 450, 10);
  game = new Game (1024, 786, 585);
  cats = new Cats[3];
  humans = new Humans[3];
  for ( int g = 0; g< 3; g++) {
    cats[g] = new Cats (int(random(200, 900)), int(random(200, 600)), 15, 70, 70, 200, 800);
    humans[g] = new Humans (int(random(200, 900)), int(random(200, 900)), 15, 70, 70, 200, 900);
  }
  locations = new Locations[7];
  locations[0] = new Locations (250, 270, 100); // library
  locations[1] = new Locations ( 250, 470, 100); // Palms
  locations[2] = new Locations (400, 310, 50); //baraha
  locations[3] = new Locations (790, 250, 100); //theatre
  locations[4] = new Locations (950, 400, 100); //dining
  locations[5] = new Locations (800, 620, 200); // knock
  locations[6] = new Locations (570, 570, 200); // alarm

  backgroundSound.loop();
  baraha.loop();
  baraha.amp(0.0);
  knock.loop();
  knock.amp(0.0);
  theatre.loop();
  theatre.amp(0.0);
  library.loop();
  library.amp(0.0);
  dining.loop();
  dining.amp(0.0);
  alarm.loop();
  alarm.amp(0.0);
  //meow.loop();
  meow.amp(0.5);
}

void draw () {
  // to make it easier which game scteen we are on
  if (gameScreen == 0) {
    // instructions page
    initScreen();
  } else if (gameScreen == 1) {
    // the game
    gameScreen();
  } else if (gameScreen == 2) {
    // if you lose
    gameOverScreen();
  } else if (gameScreen == 3) {
    // if you win
    gameWinScreen();
  }
}

void gameScreen() {
  game.display();
  creature.display();
  for ( int g = 0; g< 3; g++) {
    cats[g].display();
    humans[g].display();
  }
  for ( int n = 0; n < 12; n ++) {
    horizentalBorders[n].displayBorders();
  }
  for ( int m = 0; m < 13; m ++) {
    verticalBorders[m].displayBorders();
  }
  for ( int l = 0; l <7; l ++) {
    // checking if the locations are triggered
    locations[l].display();
    locations[l].trigger();
  }
  if (game.count <= 0) {
    // if the time runs our, you lose
    creature.alive = false; 
    creature.win = false;
  }
  if (creature. alive == false && creature.win == true) {
    // if you win
    gameScreen=3 ;
    gameWinScreen();
  }
  if (creature. alive == false && creature.win == false) {
    // if you lose
    gameScreen=2 ;
    gameOverScreen();
  }
}

void gameOverScreen() {
  // lost screen
  image (dead, 187, 59);
}
void gameWinScreen() {
  // won screen, dsplaying score as well
  image (won, 187, 59);
  text("Your Score: " + int(game.scorecount), 420, 420);
}

void mouseClicked() {
  if ( gameScreen == 0) {
    gameScreen= 1;
  }
  if (gameScreen == 2 || gameScreen == 3) {
    restart();
    gameScreen= 1;
    println("restart");
  }
}
// class borders for the map 
class Borders{
  float x, y, x2, y2;
  
  Borders (int _x, int _y, int _x2, int _y2 ){
    x = _x;
    y = _y;
    x2 = _x2;
    y2 = _y2;
    
  
  }
  
  void displayBorders(){
  //stroke(200);
  noStroke();
  line(x,y,x2,y2);
  }
}
// creature cats

class Cats {
  float x, y, r, w, h, x1, x2;
  float vy = 0;
  float vx = 0;
  boolean alive = true;
  PImage img; 

  Cats (float _x, float _y, int _r, int _w, int _h, int _x1, int  _x2) {
    x = _x;
    y = _y;
    r = _r;
    vy = 0;
    vx = random(1, 3);
    w = _w;
    h = _h;
    x1 = _x1;
    x2 = _x2;   
    // picking a random image of the 3 cat versions
    img = loadImage("cat" + str(int(random(1, 4))) + ".png");
  }

  void display() {
    update();
    noFill();
    noStroke();
    image( img, x-r, y-r, 50, 50);
    circle(x, y, r*2);
  }

  void update() {
  // movement of the cats 
    y += vy;
    x += vx; 
    
    // for the cats to move back and forth
    if (x < x1) {
      vx *= -1;
    } else if ( x > x2) {
      vx *= -1;
    }
  }

  float distance(int tarX, int tarY) {
    // to calculate the distance between the cat and another object
    return (pow( (pow(( x - tarX), 2) + pow((y - tarY), 2)), 0.5));
  }

  boolean trigger() {
    return false;
  }
}
//Clas for player 
class Creature {
  int x, y, r;
  float vy = 0;
  float vx = 0;
  int directionH = 0; 
  int numloc = 0;
  boolean alive = true;
  boolean win = false;
  boolean borderCheckY = true;
  boolean borderCheckX = true;
  PImage img, green;
  boolean leftKey = false;
  boolean rightKey = false;
  boolean upKey = false;
  boolean downKey = false;
  int previousTime = 0;
  float timePassed = 1000;


  Creature (int _x, int _y, int _r) {
    x = _x;
    y = _y;
    r = _r;
    vy = 0;
    vx = 0;
    img = loadImage("player.png");
  }

  void sound() {
    // function to equate each sound to its respective location, play the sound upon trigger
    if ( locations[0].trigger == true) {
      library.amp(1.0);
    }
    if ( locations[1].trigger == true) {
      palms.amp(1.0);
    }
    if ( locations[2].trigger == true) {
      baraha.amp(1.0);
    }
    if ( locations[3].trigger == true) {
      theatre.amp(1.0);
    }
    if ( locations[4].trigger == true) {
      dining.amp(1.0);
    }
    if ( locations[5].trigger == true) {
      knock.amp(1.0);
    }
    if ( locations[6].trigger == true) {
      alarm.amp(1.0);
    }
  }

  void display() {
    update();
    noFill();
    noStroke();
    image( img, x-r, y-r, 50, 50);
    circle(x, y, r*2);
  }

  void update() {
    keyPressed();

    y += vy;
    x += vx;

    // for movement of the player and keys + game borders
    if ( leftKey == true && x>=0) {
      directionH = 1;
      vx = -1;
    } else if ( rightKey == true && x + r <= 1024) {
      vx = 1;
      directionH = 0;
    } else if ( downKey == true && y +r <= 784) {
      vy = 1;
    } else if ( upKey == true && y >= 0  ) {
      vy = -1;
    } else { 
      vx = 0;
      vy = 0;
    }

    // checking for collision of map borders
    collisionY();
    collisionX();

    for ( int g = 0; g< 3; g++) {
      if (cats[g].distance(x, y) <= r +cats[g].r ) {
        // when player hits a cat
        if (trigger == false) {
          // mouse sound effect + you die, and the sounds stop
          meow.play();
          palms.amp(0.0);
          baraha.amp(0.0);
          library.amp(0.0);
          theatre.amp(0.0);
          dining.amp(0.0);
          knock.amp(0.0);
          alarm.amp(0.0);
          println("meow");
          trigger = true;
          alive = false;
          win = false;
        }
      }
      // if you hit a human, your score goes down, and you lose 10 seconds off your timer
      if (humans[g].distance(x, y) <= r +humans[g].r) {
        if (humans[g].trigger == false) {
          game.count -=10;
          game.scorecount-=20;
          previousTime = millis();
          humans[g].trigger = true;
          game.display();
        }
        if ( millis() > previousTime + timePassed) {
          previousTime= millis();
          humans[g].trigger = false;
        }
      }
    }

    // probably one of the most important parts. Code to see if you hit a location
    for ( int o = 0; o< 7; o++) {
      if (locations[o].trigger == false) {
        if (locations[o].distance(x, y) <= 20) {
          locations[o].trigger = true; 
          if (tally.size() >0) {
            // adds to your score
            numloc +=1;
            game.scorecount += ((120 - (millis()/1000))*creature.numloc);
            println(game.scorecount);
            // removes a string on top left to see how many you have left
            tally.remove(0);
            locations[o].display2();
            // ding as an indicator that you got one
            ding.play();
            sound();
            //image (green, height, width);
            println(tally);
          }
        }
        // check for a win. If the string length == 0, you won
      } else if (tally.size() == 0) {
        win = true;
        alive = false;
      }
    }
  }
  // checking to see if you hit the horizontal borders. makes you stop moving
  void collisionY() {
    for (int i = 0; i <13; i++ ) {
      if ( vy + y + r >= horizentalBorders[i].y && 
        vx + x + r >= horizentalBorders[i].x &&
        x + vx +r <= horizentalBorders[i].x2 && 
        vy + y + r <= horizentalBorders[i].y2) {
        vy = 0;
      }
    }
  }
  // collision with vertical borders.
  void collisionX() {
    for (int j = 0; j < 11; j++) {
      if ( vx + x + (r*2) <= verticalBorders[j].x && 
        vx + x + (r*2) >= verticalBorders[j].x2 &&
        y + vy + r >= verticalBorders[j].y && 
        vy + y + r <= verticalBorders[j].y2) {
        //println("stop3");
        vx = 0;
      }
    }
  }

  // equating key pressed with players movements
  void keyPressed() {
    if (keyPressed && keyCode == LEFT ) {
      leftKey = true;
    } else {
      leftKey = false;
    }
    if (keyPressed && keyCode == RIGHT) { //&& x+r <= game.w
      rightKey = true;
    } else {
      rightKey = false;
    }
    if (keyPressed && keyCode == UP) {
      upKey = true;
    } else {
      upKey = false;
    }
    if (keyPressed && keyCode == DOWN) { // && y+r <= game.h
      downKey = true;
    } else {
      downKey = false;
    }
  }



  float distanceCreature(int tarX, int tarY) {
    return (pow( (pow(( x - tarX), 2) + pow((y - tarY), 2)), 0.5));
  }
}
import processing.sound.*;
PImage img;
StringList tally;

class Game {
  float w, h, g;
  int count = 120;
  float scorecount = 0;

  Game(int _w, int _h, int _g) {
    w = _w;
    h = _h;
    g = _g;
    img = loadImage("map.jpg");
    tally = new StringList("I", "I", "I", "I", "I", "I", "I" );
  }

  void display () {
    // map image
    image (img, 0, 0);
    fill (0, 0, 0);
    textSize(25);
    // 3d effect on timer
    text("Time Remaining: " + count, 365, 50);
    fill(255, 255, 255);
    textSize(25);
    text("Time Remaining: " + count, 363, 48 );
    fill (255, 255, 255);
    // displaying the tally on the left
    for (int i = 0; i <tally.size(); i++) {
      text (tally.get(i), 50+(i*10), 50);
    }
    fill(150, 17, 17);
    for (int i = 0; i <tally.size(); i++) {
      text (tally.get(i), 50+(i*10), 50);
    }
    fill (255, 255, 255);
    text ("Score: " + int(scorecount), 850, 50);
    // displaying score, with 3d effect and also green if positive, red if negative
    if (scorecount <=0) {
      fill(150, 17, 17);
      text ("Score: " + int(scorecount), 849, 49);
    } else if (scorecount >=0) {
      fill(17, 150, 17);
      text ("Score: " + int(scorecount), 849, 49);
    }

    // for decreasing time
    if (creature.alive == true) {
      if (frameCount % 60 == 0) {
        //println (frameCount);
        count   -=1;
      }
      if (count <= 0) {
        creature.win=false;
        creature.alive = false;
      }
    }
  }
}
// creature cats

class Humans {
  float x, y, r, w, h, y1, y2;
  float vy = 0;
  float vx = 0;
  boolean alive = true;
  PImage img; 
  boolean trigger = false;

  Humans (float _x, float _y, int _r, int _w, int _h, int _y1, int  _y2) {
    x = _x;
    y = _y;
    r = _r;
    vx = 0;
    vy = random(1, 3);
    w = _w;
    h = _h;
    y1 = _y1;
    y2 = _y2;   
    img = loadImage("human" + str(int(random(1, 6))) + ".png");
  }

  void display() {
    update();
    noFill();
    noStroke();
    image( img, x-r, y-r, 50, 50);
    circle(x, y, r*2);
  }

  void update() {

    y += vy;
    x += vx; 

    if (y < y1) {
      vy *= -1;
    } else if ( y > y2) {
      vy *= -1;
    }
  }

  float distance(int tarX, int tarY) {
    return (pow( (pow(( x - tarX), 2) + pow((y - tarY), 2)), 0.5));
  }

  boolean trigger() {
    return false;
  }
}

 

Zip:IntroMidtermProject

Finally, here is the final game (That i am very bad at playing, sorry its so frustrating to watch):

Claustrophobia – Midterm

original game concept

My game is based on a nightmare I used to have when I was a child.
I’m very claustrophobic, so I used to have a recurring nightmare where I would be in a room with a cake. The cake would get progressively bigger until it left me with no space for myself. My only way out was to eat it.
This is what I wanted the gameplay to be.
I would be able to move around, jump, and crouch to avoid the cake’s hits (candles). If it gets me, it gets bigger. If I get close to it and press the spacebar, I can eat a small chunk out of it. The game will end either if the cake takes up the whole room, or if I eat it first.

Changes made/ Updated version

Due to time constraints, and many many details I was not expecting to cause issues within my program, I had to change the concept slightly, and give up one aspect of the game.
So now, this is the concept of my game:

        • It starts off similar. Me. Cake. In room.
        • The cake can still shoot at me and get bigger if it hits me.
        • I can still move around, jump and crouch to avoid hits.

HOWEVER, I gave up the part where I can eat the cake. So now, instead of eating it whole to win the game, another condition needs to be met:

        • I have to last 1 minute and 30 seconds without the cake taking up the whole space.
        • A timer is set up to take care of this.
        • The cake will take up the whole space when it hits me 14 times. So as long as I have lives remaining (initial lives = 14), and 1 minute and 30 seconds have passed, I win.

aesthetic of the game

The game is supposed to be a bit humorous, in the sense that it makes you think that this is going to be a horror game, dealing with claustrophobia and nightmares, while in reality, it is quite light and silly since it is a childhood nightmare of mine.

The vibe of the start screen is very creepy, accompanied by equally creepy music. But when you press play, the whole vibe changes. The images are a lot more colorful, and the music becomes fun and bouncy.



remaining issue

I ran into multiple bugs and issues while working on this assignment. Most were handled. However, there is one I can’t seem to fix.
When the cake size grows bigger, and the image in the code is updated to grow in size, the program begins to run slower the bigger the image gets.
I’ve tried many approaches to fixing this(img.resize(), scale(), …) but none seem to do the trick.
I kept the cake visualized by a rectangle for the game to be able to run smoothly, but I still intend to look for a way around this as I find myself very intrigued by what else could possibly be causing this and how to fix it.

classes and tabs

I have six tabs for this program. One for the main, 4 for classes (Game, Person, Hit, Cake), and one more tab containing the display functions for each screen (Start screen, Instructions, Winning screen, Losing screen).
I have illustrated and explained the main and basic ideas by commenting throughout my code.

This is the Main tab: 

import processing.sound.*;
SoundFile gamemusic,startmusic;
SoundFile pew, hit , scream,sleep;
PImage image1,image2,image3,bg,candle;
PImage creepy1, creepy2,title,title2,instruc,sleeping,screaming;
PImage[][] sprites;
Game g;
Person me;
Cake cake;
ArrayList<Hit> hits;
float ground ;
int timer, timeleft, timestart,seconds,minutes;
int temptime;

//variables to detect which screen we are on
int startMenu =1;
int howTo = 2;
int playGame = 3;
int gameState;

void setup() {
  size(1400, 800);
  gameState = startMenu; //start at start screen
  temptime = 90;
  timer = timeleft = 90000; // set timer for 1 minute 30 seconds
  timestart = 0;
  ground = height - 50;
  g = new Game(); //new instance of our whole game
  me = new Person(); //new person
  cake = new Cake(); //new cake
  hits = new ArrayList<Hit>(); //new hits array
  //load all images
  image1 = loadImage("me.png");
  sprites = new PImage[3][6]; 
  for (int y=0; y < 3; y++) {
    for (int x=0; x< 6; x++) {
      sprites[y][x] = image1.get(x*(image1.width/6), y*(image1.height/3), me.characterW, me.characterH);
    }
  }
  image2 = loadImage("me3.png");
  image3 = loadImage("cakee.png");
  candle = loadImage("candle.png");
  creepy1 = loadImage("creepy1.jpg");
  creepy2 = loadImage("creepy2.jpg");
  title = loadImage("title.png");
  title2 = loadImage("title2.png");
  instruc = loadImage("instruc.png");
  sleeping = loadImage("sleep2.jpg");
  screaming = loadImage("scream.jpg");
  bg = loadImage("bg1.jpg");
  //load all sounds, loop them and set their volumes to 0
  pew = new SoundFile (this,"pew.mp3");
  hit = new SoundFile (this,"gothit2.mp3");
  scream = new SoundFile (this,"scream.mp3");
  scream.loop(); scream.amp(0);
  gamemusic = new SoundFile (this,"gamemusic.mp3");
  gamemusic.loop(); gamemusic.amp(0);
  startmusic = new SoundFile (this,"startmusic.mp3");
  startmusic.loop(); startmusic.amp(0);
  sleep = new SoundFile (this,"sleep.mp3");
  sleep.loop(); sleep.amp(0);
}

void draw() {
  
  if (gameState == startMenu){
    startmusic.amp(1); //play start music
    image(creepy1,0,0);
    image(title, 200,140);
    image(title2, 400,300);
    displayStart(); //display start screen
    //make new instances of everthing to restart everything
    g = new Game(); 
    cake = new Cake();
    me = new Person();
    hits = new ArrayList<Hit>();
    gamemusic.amp(0); //mute other sounds
    scream.amp(0);
    sleep.amp(0);
  }
  else if (gameState == howTo){ 
    image(creepy2,0,0);
    image(instruc,500,100);
    displayHow(); //display instructions screen
    gamemusic.amp(0);
    scream.amp(0);
    sleep.amp(0);
    startmusic.amp(1); //play music for this screen
  }
  else if (gameState == playGame){
    if (g.won == false && g.lost == false) //if the game is still going
    {
      //display the game and play its music
      image(bg, 0, 0);
      gamemusic.amp(0.5);
      startmusic.amp(0);
      scream.amp(0);
      sleep.amp(0);
      temptime = minutes* 60 + seconds;
      g.coolingTime++;
      g.displayGame();
    }
    else if (g.lost == true) //if you lose, display lost screen
    {
      image(screaming,0,0);
      gamemusic.amp(0);
      startmusic.amp(0);
      sleep.amp(0);
      scream.amp(1);
      lostGame();
    } else if (g.won == true) //if you win display winning screen
    {
      image(sleeping,0,0);
      gamemusic.amp(0);
      startmusic.amp(0);
      scream.amp(0);
      sleep.amp(1);
      wonGame();
    }
  }

}
//mouse pressed functions to make the buttons that switch between screen
void mousePressed(){
  if (gameState == startMenu){
       if (475<mouseX && mouseX<475+450 && 620<mouseY && mouseY<620+100){
        gameState = howTo;
      }
  
      if (475<mouseX && mouseX<475+450 && 500<mouseY && mouseY <500+100){
        gameState = playGame;
        timestart = millis();
      }
  }
  else if (gameState == howTo){ 
      if (475<mouseX && mouseX<475+450 && 650<mouseY&& mouseY<650+100){
        gameState = startMenu;
      }
  }
  else if (gameState == playGame && (g.won || g.lost)){
    if (475<mouseX && mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      gameState= startMenu;
    }
  }
}

 

This is the Game Class:
This is where an instance of the actual gameplay is defined using all other instances from other classes. It only has a displayGame() method which combines everything that needs to be done when you press START.

class Game
{
  boolean won ;
  boolean lost;
  int coolingTime, hitsCount, biggerCount, lives;

  Game()
  {
    hitsCount = 0;
    biggerCount = 0;
    coolingTime = 50;
    won = false;
    lost = false;
  }

  void displayGame()
  {
    timeleft =  timer - (millis() - timestart);
    seconds = (timeleft/1000) % 60;
    minutes = (timeleft/1000) / 60;
    //game
    
    me.jump();
    me.move();
    me.drawPerson();
    //for each hit draw it, move it and check it it collides with character
    for (int i=0; i<hitsCount; i++)
    {
      hits.get(i).updateHit();
      hits.get(i).drawHit();
      if ((me.hitPerson(hits.get(i))== true)) //if there is a collision
      {
        hits.get(i).y=900; //make hit disappear from view
        cake.bigger(me); //make cake bigger
        biggerCount++; //add to this counter which gives us how many times you've been hit
        if (hit.isPlaying()) {
          hit.stop();
        }
        hit.play(); //play hit sound if not already playing
      }
      if (hits.get(i).y == 900 || hits.get(i).x<0) 
      {
        hits.remove(i); //to keep hits array small, remove any hit that is no longer needed
        // i.e if it disappeared from screen or if it already hit the character
        hitsCount--;  //and reduce this counter by 1
      }
    }


    if (coolingTime >= 100) { //add a new hit to hit array after some time
      hits.add( new Hit(random(550, ground-20)));
      hitsCount+=1; //increment hit counter
      if (pew.isPlaying()) { 
        pew.stop();
      }
      pew.play(); //play shooting sound "pew pew" if it's not already playing
      coolingTime = int(random(50));
    }
    //draw and move the cake
    cake.drawCake();
    cake.moveCake();
    if (biggerCount == 14) //detect loss
    {
      lost = true;
    }
    if (biggerCount <14 && seconds == 0 && minutes == 0) //detect win
    {
      won = true;
    }
    //display timer and number of remaining lives
    if(seconds <= 5 && minutes == 0) //make timer show up bigger in the center of the screen
    //when you have 5 minutes left
    {
      fill (255,220);
      rect(width/2-250,height/3-70,500,200);
      fill(0);
      textSize(70);
      textAlign(CENTER);
      lives = 14 - biggerCount;
      text("Timer: "+seconds, width/2, height/3+10);
      text("Lives: "+ str(lives), width/2, height/3+100);
    }
    else{
      fill (255,190);
      rect(10,10,220,100);
      fill(0);
      textSize(35);
      textAlign(LEFT);
      lives = 14 - biggerCount;
      if(seconds < 10)
       text("Timer: "+ minutes+":0"+seconds, 20, 50);
      else
        text("Timer: "+ minutes+":"+seconds, 20, 50);
      
      text("Lives: "+ str(lives), 42, 90);
    }
  }

}

 

This is the Cake class:
All methods related to cake are defined here : drawCake(), moveCake(), bigger() (the last one is to make the cake bigger).

class Cake
{
  float cakeW, cakeH;
  float cakeX, cakeY,speed;
  float s;
  Cake(){
    speed = 0.5;
    cakeW = 300;
    cakeH = 200;
    cakeX = width -cakeW - 50;
    cakeY = ground-cakeH;
    s=1;
  }
  
  void drawCake()
  {
    noStroke();
    fill(0,150);
    rect(cakeX,cakeY, cakeW, cakeH);
    //image(image3,cakeX,cakeY,cakeW, cakeH);
  }
  void moveCake() // to slightly move the cake to the left and right
  {
    cakeX -= speed;
    if (cakeX > width -cakeW - 25 || cakeX< width -cakeW - 75)
    {
      speed *= -1;
    } 
  }
  void bigger(Person thisMe) // to make the cake bigger everytime you are hit
  {
    //change size
    cakeH *= 1.1;
    cakeW *= 1.1;
    //push back x and y accordingly
    cakeX = width -cakeW - 50;
    cakeY = ground-cakeH;
    //upadte the x of the character to not let it be displayed afte the border of the cake
    float d =dist(thisMe.characterX,thisMe.characterY,cakeX,thisMe.characterY);
    if ( d< thisMe.characterW)
    {
      thisMe.characterX -= (thisMe.characterW -d) ;
    }

  }

}

 

This is the Hit class:
It has drawHit and updateHit methods.
An ArrayList of hits is defined in the Main Tab to contain all hits.

class Hit
{
  float x,y, speed;
  int w,h;
  
  Hit(float thisY)
  {
    w= 50;
    h = 6;
    //setting speed for every hit depending on timer
    if (90-temptime <= 20) {speed = 8;}
    else if (90-temptime > 20 && 90-temptime<= 30 ) {speed = 9;}
    else if (90-temptime > 30 && 90-temptime <= 40 ) {speed = 10;}
    else if (90-temptime > 40 && 90-temptime <= 50 ) {speed = 11;}
    else if (90-temptime > 50) {speed = 13;}
    x = cake.cakeX;
    y = thisY;
  }
  
  void drawHit()
  {
    fill(0);
    noStroke();
    //rect(x,y,w,h);
    image(candle,x,y,w,h);
  }
  
  void updateHit() //moving the hit
  {
    x-= speed; 
  }
}

 

This is the Person class:
It has 4 methods: jump , move, drawPerson, and hitPerson(this one is to check for collision with hits).

class Person
{
  int characterW;
  int characterH;
  int direction;
  int step;
  float xspeed;
  float yspeed;
  float characterX;
  float characterY ;
  boolean crouching; //boolean to check if player is crouching or not
  boolean jumping; //boolean to check if player is already jumping
  
  Person()
  {
    characterW=100;
    characterH=150;
    direction = 2;
    step = 0;
    xspeed= 5;
    yspeed = 0;
    characterX= 0;
    characterY= ground-characterH; 
    crouching = false;
    jumping = false;
  }
  
  void jump(){ //function to make character jump
    characterY += yspeed;
    
    if (characterY + characterH > ground) { //check if character is back on the ground
      //set variables to initial state
      characterY = ground - characterH; 
      yspeed = 0;
      jumping = false;
    }
    else { //if character is in the air
      yspeed ++; //bring him back to the ground (gravity)
    }
  }
  void move(){ //function to move the character with the arrow keys
    if (keyPressed && keyCode == RIGHT){
        direction = 0;
        if(characterX + characterW < cake.cakeX){
          characterX += xspeed;
        }
    }
    else if (keyPressed && keyCode == LEFT){
        direction=1;
        if (characterX > 0){
          characterX -= xspeed;
        }
    }
    else if (!keyPressed){
      direction = 2;
    }
    else if (keyPressed && keyCode == UP && !me.jumping) { // only jump if you're not already jumping
      me.yspeed = -15;
      me.jumping = true;
    }
     
    if (keyPressed && keyCode == DOWN){ //if crouching, update character height and y
      crouching = true;
      characterW=140;
      characterH = 93;
      characterY= ground-characterH; 
    }
    else {crouching = false; characterW = 100; characterH = 150;} //if not, reset them to original
    
    if (frameCount%xspeed==0) { //iterate through sprites
        step = (step+1) % 6;
    }
  }
  
  void drawPerson()
  {
    if (!crouching){
      //rect(characterX, characterY, characterW, characterH);
      image(sprites[direction][step],characterX,characterY);
    }
    else if(crouching) {
      //rect(characterX, characterY, characterW, characterH);
      image(image2,characterX,characterY);
    }
  }
  boolean hitPerson(Hit h) //checking collision with a hit
  {
    if((h.x>characterX +characterW) && dist(h.x+h.w/2,h.y+h.h/2,characterX+characterW/2,characterY+characterH/2) <= characterW/2+h.w/2)
    {
      return true; // means there is a collision from front or back
    }
    if((h.x<=characterX +characterW) && dist(h.x+h.w/2,h.y+h.h/2,characterX+characterW/2,characterY+characterH/2) <= characterH/2+h.h/2)
    {
      return true; // means there is a collision from top or bottom
    }
    else {return false;} // if not, there is no collision
  }
}

 

And finally! This is the tab for the screen display functions:

//function to display losing screen
void lostGame(){
  fill(255);
  textSize(30);
  textAlign(CENTER);
  text("OH NO! YOU LOST!", width/2,600);
  fill(255);
  stroke(0);
  strokeWeight(4);
  rect(475,650,450,100);
  fill(0);
  text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
  if (475<mouseX&& mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,650,450,100); 
      fill(255);
      text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
   }
}
//function to display winning screen
void wonGame(){
  fill(255);
  textSize(30);
  textAlign(CENTER);
  text("YAY! YOU WON!", width/2,600);
  fill(255);
  stroke(0);
  strokeWeight(4);
  rect(475,650,450,100);
  fill(0);
  text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
  if (475<mouseX&& mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,650,450,100); 
      fill(255);
      text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
   }
}
//function to display start screen
void displayStart(){
  textSize(30);
  textAlign(CENTER);
  fill(64);
  stroke(0);
  strokeWeight(4);
  rect(475,500,450,100);
  rect(475,620,450,100);
  fill(0);
  text("START GAME",475+450/2,500+100/2);
  text("HOW TO PLAY",475+450/2,620+100/2);
  
   if (475<mouseX&& mouseX<475+450 && 500<mouseY&& mouseY<500+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,500,450,100); 
      fill(255);
      text("START GAME",475+450/2,500+100/2);
   }
                
   if (475<mouseX && mouseX<475+450 && 620<mouseY && mouseY<620+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,620,450,100);
      fill(255);
      text("HOW TO PLAY",475+450/2,620+100/2);
   }
}
//function to display instructions screen
void displayHow(){
  fill(255,200);
  noStroke();
  rect(300,200,800,400);
  fill(0);
  textSize(20);
  textAlign(CENTER);
  text("Well hello sleepy head, welcome to your nightmare.", width/2,240);
  text("In this room, you will face a terrible monster.", width/2,270);
  text("Avoid its hits. OR ELSE...", width/2,300);
  text("It'll get bigger and have you face your claustrophobia!", width/2,330);
  text("If you manage to survive for 90seconds, ", width/2,360);
  text("(which, as we know,is hours in the dream world)", width/2,390);
  text("the nightmare will be over, and you'll sleep peacefully.", width/2,420);
  text("If the monster takes over the entire room before the time is over", width/2,450);
  text("(by hitting you 14 TIMES), your sleep will be interrupted.", width/2,480);
  text("Try to win to stay peacefully asleep.", width/2,510);
  text("Be careful!! The hits get progressively faster with time.", width/2,540);
  text("Move, jump and crouch with the arrow keys. Good luck.", width/2,570);
  fill(64);
  stroke(0);
  strokeWeight(4);
  rect(475,650,450,100);
  fill(0);
  text("GO BACK",475+450/2,650+100/2);
  if (475<mouseX&& mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,650,450,100); 
      fill(255);
      text("GO BACK",475+450/2,650+100/2);
   }
}

Final game
(has a rectangle instead of cake image)

If you want to try the game out, here is the .zip file!
midtermGame

UPDATE

I fixed the image resizing problem with an off-screen buffer using PGraphics.
This is the working version of the game.
fixedGame

Fight The Pandemic

From my last about the midterm until now, time for submission I had to change a few things. The biggest change was how the game would operate. Initially the plan was to have the shooter in the middle of the screen and have a pointer that would change location based on the location of the mouse in the window, however I changed this to have the shooter moving from side to side as a not to have the user/player switch between the mouse and the keyboard for now the player can play the entirely from the keyboard.

Objective of the game

Try to get as many points as possible while keeping the number of cases as low as possible within 2 minutes/

Game Operation

The keys used for the operation of the game are:

1,2,3,m, space bar and the left and right arrow keys.

The space bar will shoot the fighting tool that is currently selected, this can seen in the shooter icon. Keys 1,2 and 3 can be used to switch between the fighting tools when you’re in “fight” mode. Keys 1,2 and 3 can be used to buy more fighting tools when you are in buy mode. You can switch modes by pressing the m button.

The difference between the fighting tool is number of points you get when you shoot the virus down and the speed of the fighting tool.

Speed of Handwash < Speed of face mask < Speed of face shield.

Shooting the virus down with a handwash will get you 1 points, shooting it down with facemask will get you 3 points and the shield will get you 5 points.

You can use the points to buy more tools. Buying more handwash will cost 1 point, face masks cost 2 points/mask, and face shields cost 3 points/shield.

The number of cases will go up if the virus goes below the window.

 

import processing.sound.*;

//create new game
Game game;

// arrayList to store corona virus images
ArrayList <CoronaVirus> coronaList;
ArrayList <Fighter> activeFighters;

//sound files
SoundFile error;
SoundFile scaryBg;
SoundFile collision;
SoundFile newCase;

//image
PImage virus;

// misc vars
String errorMessage = "";

void setup() {
  size(1280, 720);
  game = new Game(); //init game
  coronaList = new ArrayList<CoronaVirus>(0);
  activeFighters = new ArrayList<Fighter>(0);
  for (int i=0; i<4; i++) {
    coronaList.add(new CoronaVirus());
  }
  virus = loadImage("virus.png");
  error = new SoundFile(this, "error.mp3");
  scaryBg = new SoundFile(this, "scaryBg.mp3");
  collision = new SoundFile(this, "collide.mp3");
  newCase = new SoundFile(this, "dead.mp3");
}

void draw() {
  if (game.state==-2) {
    startScreen();
  }
  else if (game.state==0 || game.state==1) {
    background(211, 211, 211); // clear background to avoid trails
    game.drawGameStats(); // show the game statistics
    game.shooter(); // draw the shooter
    displayError(); //display error messages
    displayCost(); // display cost of buying
    playBGMusic(); // play background music
    updateCorona(); // update the position and display the virus
    moveFighters(); //move the fighting tools on the screen
    checkCollisions(); //check for collision
    checkCoronaPosition(); // check for off screen viruses
    checkFighterPosition(); // check for off screen fighting tool
    newViruses(); // generate new viruses
    game.moveShooter(); // move the shooter
    game.updateTime(); // countdown time
  }
  else if (game.state==-1) {
    gameOver();
  }
}
import processing.sound.*;

//create new game
Game game;

// arrayList to store corona virus images
ArrayList <CoronaVirus> coronaList;
ArrayList <Fighter> activeFighters;

//sound files
SoundFile error;
SoundFile scaryBg;
SoundFile collision;
SoundFile newCase;

//image
PImage virus;

// misc vars
String errorMessage = "";

void setup() {
  size(1280, 720);
  game = new Game(); //init game
  coronaList = new ArrayList<CoronaVirus>(0);
  activeFighters = new ArrayList<Fighter>(0);
  for (int i=0; i<4; i++) {
    coronaList.add(new CoronaVirus());
  }
  virus = loadImage("virus.png");
  error = new SoundFile(this, "error.mp3");
  scaryBg = new SoundFile(this, "scaryBg.mp3");
  collision = new SoundFile(this, "collide.mp3");
  newCase = new SoundFile(this, "dead.mp3");
}

void draw() {
  if (game.state==-2) {
    startScreen();
  }
  else if (game.state==0 || game.state==1) {
    background(211, 211, 211); // clear background to avoid trails
    game.drawGameStats(); // show the game statistics
    game.shooter(); // draw the shooter
    displayError(); //display error messages
    displayCost(); // display cost of buying
    playBGMusic(); // play background music
    updateCorona(); // update the position and display the virus
    moveFighters(); //move the fighting tools on the screen
    checkCollisions(); //check for collision
    checkCoronaPosition(); // check for off screen viruses
    checkFighterPosition(); // check for off screen fighting tool
    newViruses(); // generate new viruses
    game.moveShooter(); // move the shooter
    game.updateTime(); // countdown time
  }
  else if (game.state==-1) {
    gameOver();
  }
}
class Fighter {
  int type = -1; // 0 is handwash, 1 is mask, 2 is shield
  int speed = -1;
  int positionX = -1;
  int positionY = -1;
  PImage img;

  //methods
  //update position
  void updatePosition() {
    positionY -= speed;
    showFighter();
  }
  void fighterImage() {
    //println(type);
    switch(type) {
    case 0: 
      { // handwash
        img = loadImage("handwash.png");
        speed = 3;
        break;
      }
    case 1:
      { // facemask
        img = loadImage("mask.png");
        speed = 5;
        break;
      }
    case 2:
      { // faceshield
        img = loadImage("shield.png");
        speed = 7;
        break;
      }
    }
  }

  void showFighter() {
    image(img, positionX, positionY, 30, 30);
  }
}
class Game {
  // attributes
  int points = 0;
  int cases = 0;
  int state = -2; // 0->shooting | 1-> buying | -1 -> gameOver | -2-> game not started
  int fightingTool = 0; // 0-handwash, 1-mask, 2-shield
  int time = 120;
  PImage maskImg = loadImage("mask.png");
  PImage handwashImg = loadImage("handwash.png");
  PImage shieldImg = loadImage("shield.png");
  int timeDisplay = 0;
  int shooterPosition = width/2;
  int shooterDirection = 0;
  float shooterSpeed = 2;
  
  // fighting tools
  // handwash attr
  int handwash = 20;
  
  // facemask attr
  int masks = 14;
  
  // faceshield attr
  int shields = 6;
  
  

  //methods
  void incrementPoints(int type) {
    if (type == 0) {
      points += 1;
    } else if (type == 1) {
      points += 3;
    } else if (type == 2) {
      points += 5;
    }
  }
  
  void incrementCases() {
    cases+=1;
  }

  void incrementMasks() {
    masks+=1;
  }

  void decrementMasks() {
    masks-=1;
  }

  void incrementShields() {
    shields+=1;
  }

  void decrementShields() {
    shields-=1;
  }

  void incrementHandwash() {
    handwash+=1;
  }

  void decrementHandwash() {
    handwash-=1;
  }
  
  void decrementFighters(){
    switch(fightingTool){
      case 0:{
        decrementHandwash();
        break;
      }
      case 1:{
        decrementMasks();
        break;
      }
      case 2:{
        decrementShields();
        break;
      }
    }
  }

  void changeState() {
    //println("changing state");
    if (state==0) {
      state=1;
    } else if (state==1) {
      state=0;
    }
  }
  void updateTime() {
    if (time==0) {
      state=-1;
    }
    if (frameCount%60==0 && state!=-1) {
      time-=1;
      if (timeDisplay==0) {
        timeDisplay = 1;
      } else if (timeDisplay==1) {
        timeDisplay=0;
      }
    }
    if (points==0 && handwash==0 && masks==0 && shields==0){
      state=-1;
    }
  }

  void drawGameStats() {
    textSize(12);
    color(0, 0, 0);
    imageMode(CENTER);
    
    pushStyle();
    //draw handwash
    image(handwashImg, 70, 30, 30, 30); //150,30,30,30
    text(handwash+" x", 25, 30);//110,30

    // draw mask
    image(maskImg, 150, 30, 30, 30);
    text(masks+" x", 110, 30);

    // draw faceshield
    image(shieldImg, 230, 30, 30, 30);
    text(shields+" x", 190, 30);
    popStyle();
    
    //display time
    pushStyle();
    textSize(30);
    if (timeDisplay==1) {
      fill(0, 0, 0);
      text(time, width-100, height-30);
    } else if (timeDisplay==0) {
      fill(255, 255, 0);
      text(time, width-100, height-30);
    }
    popStyle();

    // game mode
    pushStyle();
    int rectWidth = 100;
    int rectHeight = 30;
    int cornerRadius = 10;
    noFill();
    rect(width/2-rectWidth/2, 20, rectWidth, rectHeight, cornerRadius, cornerRadius, cornerRadius, cornerRadius);
    if (state==0) {
      pushStyle();
      textAlign(CENTER);
      textSize(25);
      fill(255, 255, 0);
      text("FIGHT", width/2, 45);
      popStyle();
    } else if (state==1) { //display buy
      pushStyle();
      textAlign(CENTER);
      textSize(25);
      fill(0, 255, 0);
      text("BUY", width/2, 45);
      popStyle();
    } else if (state==-1) { //display gameover
      pushStyle();
      textAlign(CENTER);
      textSize(18);
      fill(255, 0, 0);
      text("Game Over", width/2, 43);
      popStyle();
    }

    popStyle();

    //display points
    pushStyle();
    fill(0, 0, 0);
    textSize(25);
    text(points+" points", width-300, 45);
    text(cases+" cases", width-150, 45);
    popStyle();
  }
  void drawFightingTool(int x, int y, int imgWidth, int imgHeight) {
    pushStyle();
    imageMode(CENTER);
    if (fightingTool==0) { // draw handwash
      image(handwashImg, x, y, imgWidth, imgHeight);
    } else if (fightingTool == 1) { //draw mask
      image(maskImg, x, y, imgWidth, imgHeight);
    } else if (fightingTool == 2) { //draw shield
      image(shieldImg, x, y, imgWidth, imgHeight);
    }
    popStyle();
  }

  void changeFightingTool(int tool) {
    fightingTool = tool;
    //println(fightingTool);
  }

  void shooter() {
    pushStyle();
    fill(255, 255, 255);
    circle(shooterPosition, height, 100);
    game.drawFightingTool(shooterPosition, height-20, 40, 40);
    popStyle();
  }

  void moveShooter() {
    if (shooterSpeed<=2) {
      shooterDirection=0;
      shooterSpeed=2.1;
    }
    if (shooterSpeed>2) {
      shooterPosition += shooterDirection*shooterSpeed;
    }
  }
}
// to interact with game
void keyPressed() {
  //println(keyCode);
  switch(keyCode) {
  case 32: 
    { // space bar pressed need to create a new fighter add to list and reduce the number from game class
      if (game.fightingTool==0 && game.handwash>0) {//check number of handwash
        Fighter tempFighter = new Fighter();
        tempFighter.type = game.fightingTool;
        tempFighter.positionX = game.shooterPosition;
        tempFighter.positionY = height-30;
        tempFighter.fighterImage();
        activeFighters.add(tempFighter);
        game.decrementFighters();
      } else if (game.fightingTool==1 && game.masks>0) {//check number of masks
        Fighter tempFighter = new Fighter();
        tempFighter.type = game.fightingTool;
        tempFighter.positionX = game.shooterPosition;
        tempFighter.positionY = height-30;
        tempFighter.fighterImage();
        activeFighters.add(tempFighter);
        game.decrementFighters();
      } else if (game.fightingTool==2 && game.shields>0) {//check number of shields
        Fighter tempFighter = new Fighter();
        tempFighter.type = game.fightingTool;
        tempFighter.positionX = game.shooterPosition;
        tempFighter.positionY = height-30;
        tempFighter.fighterImage();
        activeFighters.add(tempFighter);
        game.decrementFighters();
      }
      break;
    }
  case 37:
    { //move left
      game.shooterDirection = -1;
      if (game.shooterSpeed<10) {
        game.shooterSpeed*=1.1;
      }
      break;
    }
  case 39:
    { //move right
      game.shooterDirection = 1;
      if (game.shooterSpeed<10) {
        game.shooterSpeed*=1.1;
      }
      break;
    }
  case 49:
    { //handwash
      if (game.state==0) {
        game.changeFightingTool(0);
      } else if (game.state==1) { //buy more handwash
        if (game.points>=1) { //check if you have enough points
          game.points-=1;
          game.handwash+=1;
        } else {
          triggerErrorSound();
          errorMessage = "Not Enough Points";
        }
      }
      break;
    }
  case 50:
    { //facemask
      if (game.state==0) {
        game.changeFightingTool(1);
      } else if (game.state==1) { //buy more masks
        if (game.points>=2) { //check if you have enough points
          game.points-=2;
          game.masks+=1;
        } else {
          triggerErrorSound();
          errorMessage = "Not Enough Points";
        }
      }
      break;
    }
  case 51: 
    { //faceshield
      if (game.state==0) {
        game.changeFightingTool(2);
      } else if (game.state==1) { //buy more handwash
        if (game.points>=3) { //check if you have enough points
          game.points-=3;
          game.shields+=1;
        } else {
          triggerErrorSound();
          errorMessage = "Not Enough Points";
        }
      }
      break;
    }
  case 77:
    {
      game.changeState();
      break;
    }
  default:
    {
      triggerErrorSound();
      errorMessage = "Invalid Control";
      break;
    }
  }
}

void keyReleased() {
  if (keyCode==37 || keyCode==39) {
    game.shooterSpeed = 0.8;
  }
}

void flashMessage(String msg, color c, int size, int hOffset) {
  pushStyle();
  fill(c);
  textSize(size);
  text(msg, 10, height-hOffset);
  popStyle();
}

void triggerErrorSound() {
  if (!error.isPlaying()) {
    error.play();
  }
}

void updateCorona() {
  for (int i=0; i<coronaList.size(); i++) {
    coronaList.get(i).updatePosition(height);
    coronaList.get(i).dispVirus();
  }
}

void displayError() {
  if (error.isPlaying()) // // display error message
  {
    color errorColor = color(255, 0, 0); // color of error message
    flashMessage(errorMessage, errorColor, 15, 25); // display error message
  }
}

void displayCost() {
  if (game.state==1) { // display cost of buying more items
    String msg = "Handwash 1  Facemask 2  Faceshield 3"; // message string
    color c = color(53, 50, 56); // color of message
    flashMessage(msg, c, 18, 10); // display message
  }
}

void playBGMusic() {
  if (!scaryBg.isPlaying()) { // play bgMusic
    scaryBg.play();
  }
}

void moveFighters() {
  //activeFighters
  for (int i=0; i<activeFighters.size(); i++) {
    activeFighters.get(i).updatePosition();
  }
}

void checkCollisions() {
  //println("checking collision");                        
  // two for loops. Check as virus against each fighter
  // if collision rem both fighter and virus from arraylist
  // increment point accordingly
  // if fighter above screen remove from array list
  // if virus below screen increment cases and rem from arraylist
  for (int i=0; i<coronaList.size(); i++) {
    println(coronaList.size());
    CoronaVirus tC = coronaList.get(i);
    int size = tC.size;
    //println(size);
    for (int j=0; j<activeFighters.size(); j++) {
      Fighter tF = activeFighters.get(j);
      if (tF.positionX>tC.positionX && tF.positionX<tC.positionX+size) { // this mean the fighter is within the image
        if (tF.positionY>tC.positionY && tF.positionY<tC.positionY+size) { // this means the fighter is within the image
          coronaList.remove(i);
          activeFighters.remove(j);
          game.incrementPoints(tF.type);
          if (!collision.isPlaying()){
            collision.play();
          }
          game.points+=1;
        }
      }
    }
  }
}

void checkCoronaPosition(){
  for(int i=0;i<coronaList.size();i++){
    CoronaVirus tC = coronaList.get(i);
    if (tC.positionY>height){
      game.cases+=1;
      if(!newCase.isPlaying()){
        newCase.play();
      }
      coronaList.remove(i);
    }
  }
}

void checkFighterPosition(){
  for(int i=0;i<activeFighters.size();i++){
    Fighter tF = activeFighters.get(i);
    if (tF.positionY<0){
      activeFighters.remove(i);
    }
  }
}

void newViruses(){
  if (frameCount%7==0 && frameCount%9==0 && frameCount%4==0){
    coronaList.add(new CoronaVirus());
  }
}

void gameOver(){
  // this should display the previous game stats
  background(110,125,171);
  int size = 50;
  pushStyle();
  textSize(24);
  int moveItemsHorizontal = 100;
  image(game.handwashImg,width/2-120+moveItemsHorizontal,height/2-240,size,size);
  text(game.handwash,width/2-80+moveItemsHorizontal,height/2-230);
  
  image(game.maskImg,width/2-120+moveItemsHorizontal,height/2-160,size,size);
  text(game.masks,width/2-80+moveItemsHorizontal,height/2-150);
  
  image(game.shieldImg,width/2-120+moveItemsHorizontal,height/2-80,size,size);
  text(game.masks,width/2-80+moveItemsHorizontal,height/2-70);
  
  textSize(32);
  textAlign(CENTER);
  text("Points "+game.points,width/2,height/2+30);
  text("Cases "+game.cases,width/2,height/2+80);
  popStyle();
  restartButton();
  
}

void restartButton(){
  int buttonYPosition = 200;
  int rectWidth = 250;
  int rectHeight = 70;
  pushStyle();
  rectMode(CENTER);
  fill(9,129,74);
  if (mouseX>width/2-rectWidth/2 && mouseX<width/2+rectWidth/2 && mouseY>height/2+buttonYPosition-rectHeight/2 && mouseY<height/2+buttonYPosition+rectHeight/2){
    fill(9,160,74);
    if (mousePressed){
      resetGame();
      fill(9,100,74);
    }
  }
  rect(width/2,height/2+buttonYPosition,rectWidth,rectHeight,30);
  textAlign(CENTER);
  fill(0,0,0);
  textSize(40);
  text("RESTART",width/2,height/2+buttonYPosition+15);
  popStyle();
}

void resetGame(){
  game = new Game();
  coronaList = new ArrayList<CoronaVirus>(0);
  activeFighters = new ArrayList<Fighter>(0);
  game.state = 0;
  for (int i=0; i<4; i++) {
    coronaList.add(new CoronaVirus());
  }
}

void startScreen(){
  background(110,125,171);
  image(virus,20,20,500,500);
  instructions();
  startButton();
}

void startButton(){
  int moveButton = 85;
  int buttonYPosition = 200;
  int rectWidth = 250;
  int rectHeight = 70;
  pushStyle();
  rectMode(CENTER);
  fill(9,129,74);
  if (mouseX>width/2-rectWidth/2 && mouseX<width/2+rectWidth/2 && mouseY>height/2+buttonYPosition-rectHeight/2+moveButton && mouseY<height/2+buttonYPosition+rectHeight/2+moveButton){
    fill(9,160,74);
    if (mousePressed){
      game.state=0;
      fill(9,100,74);
    }
  }
  rect(width/2,height/2+buttonYPosition+moveButton,rectWidth,rectHeight,30);
  textAlign(CENTER);
  fill(0,0,0);
  textSize(40);
  text("START",width/2,height/2+buttonYPosition+15+moveButton);
  popStyle();
}

void instructions(){
  // Title
  pushStyle();
  String title = "Pandemic";
  fill(178,103,0);
  stroke(178,103,0);
  strokeWeight(3);
  textAlign(CENTER);
  textSize(48);
  text(title,width/2,60);
  popStyle();
  
  //objective heading
  pushStyle();
  String objective = "Objective";
  fill(0,0,0);
  textAlign(LEFT);
  textSize(32);
  text(objective,width/2,150);
  popStyle();
  
  //objective text
  pushStyle();
  String objectiveText = "Try to get your points as high as possible while keeping the number of the cases as low as possible";
  fill(0,0,0);
  textAlign(LEFT);
  textSize(18);
  text(objectiveText,width/2+50,180, width/2-70,250);
  popStyle();
  
  //instruction heading
  pushStyle();
  String instruction = "Instructions";
  fill(0,0,0);
  textAlign(LEFT);
  textSize(32);
  text(instruction,width/2,280);
  popStyle();
  
  //objective text
  String line1 = "1. Move the shooter across the screen by using the left and right arrow keys.\n";
  String line2 = "2. The icon shown in the shooter will be what you will be shooting down the virus with.\n";
  String line3 = "3. Shooting the virus down with different tools will yield different points.\n";
  String line4 = "4. The tools which yield greater points are also more expensive to acquire.\n";
  String line5 = "5. You can choose the tool that you’re fighting with by pressing 1,2 or 3. 1 will select the handwash, 2 will select facemask and 3 will select a face shield.\n";
  String line6 = "6. You can acquire more tools by pressing m to go into buying mode and then pressing 1,2 or 3 to acquire more tools. A hand wash will cost 1 point, a face mask will cost 2 points and a face shield will cost 3 points.\n";
  String line7 = "7. Number of cases will increase is the virus goes below the screen.";
  pushStyle();
  String instructionText = line1+line2+line3+line4+line5+line6+line7;
  fill(0,0,0);
  textAlign(LEFT);
  textSize(15);
  text(instructionText,width/2+50,300, width/2-70,700);
  popStyle();
}
Planning Page 1
Planning Page 2
Planning Page 3
Planning Page 4
Planning Page 5
Planning Page 6
Planning Page 7
Planning Page 8
Planning Page 9

 

Aayush Deo Midterm Project

 

David – Midterm

zip file : Midterm_IM

This surely was the project in which I tried my best to incorporate everything we have learned so far. Initially, I wanted to make a game that resembles an already existing game called, “Flappy Bird”, but I diverted my plan to render something different and original to a certain extent. The game’s goal is to fly out of the mysterious cave by avoiding blocks of flying lava. Prior to the instructions of the game, which is fairly simple, I gave a short dystopian background narrative that the player somehow ended up in the cave because things fell apart ever since 2020.

I made the instructions fairly easy. The player will be floating along the cave and the only way to maneuver is to use UP, DOWN, RIGHT (to speed up) keys to avoid the flying lava bombs. If you successfully get out of the cave, you will have to go back to the society, and that requires MONEY. Other people have also attempted to get out of the cave, only to be buried there. So you will pick up the coins that the previous adventurers have left behind and follow their legacy… If you fail, you will become one of them.

I used a few images of caves and rocks to design the game as follow.

Cave1Cave2Rock1 Rock2

To talk more about the functionalities of the game, a player’s health decreases everytime he hits a block of lava. It will keep decreasing until your lives (initally out of 3) have fully depleted. You will then have to restart from the beginning. The placement of coins and lava bombs will be randomly assigned for every level. A very short song from PacMan will be played once you start the game and during the game a player will be able to hear sound effects when he hits a coin or a lava. You will see that there is a flashlight effect on the instructions page that was employed to create a more eerie and dystopian atmosphere.

Here is what it looks like:

The code is as follows:

import ddf.minim.*;

PFont font, font2;

Player player;
ArrayList<Coin> coins;
ArrayList<Bomb> bombs;
boolean [] keys = new boolean[128];
boolean instruction = true;
boolean gameover = true;
PImage photo, cave, rock, rock2;

color player_c = color(100, 200, 200);

int myPlayer = 3;
int myCoins = 0;
float myHealth = 100;

//sound effect
Minim minim;
AudioPlayer sound, sound2, sound3;

void setup(){
  size(800, 400);
  background(0);
  smooth();
  rectMode(CENTER);
  font = createFont("Trattatello",60);
  font2 = createFont("Arial",16);
  
  player = new Player();

  coins = new ArrayList<Coin>();
  for(int i=0; i < 10; i++){
    coins.add(new Coin());
  }

  bombs = new ArrayList<Bomb>();
  for(int i=0; i < 15; i++){
    bombs.add(new Bomb());
  }
  minim = new Minim(this);
  sound = minim.loadFile("1.aif");
  sound2 = minim.loadFile("2.mp3");
  sound3 = minim.loadFile("3.mp3");
  sound.setGain(-5);
  sound2.setGain(-5);
  sound3.setGain(-3);
  sound3.play();
  sound3.rewind();
  
}

void draw(){
  if (instruction){
    photo = loadImage("cave.jpg");
    //Using the width and height of the photo for the screen size
    photo.resize(width,height); 
    image(photo,0,0);
    loadPixels();
    photo.loadPixels();
    for(int x= 0; x<width; x++){
      for(int y=0; y<height; y++){
        //location in the pixel array
        int loc = x+y*width;
        float r = red(photo.pixels[loc]);
        float g = green(photo.pixels[loc]);
        float b = blue(photo.pixels[loc]);
        //get distance between the cursor and the pixel
        float d = dist(mouseX,mouseY,x,y);
        float mult = map(d,0,300,1.8,0);
        //the closer the distance, the brighter.
        pixels[loc] = color(r*mult,g*mult,b*mult);
      }
    }
    updatePixels();
    textFont(font);
    fill(#FFFF00);
    text("Cave Escape",width/2-200,height/2-70);
    textFont(font2);
    fill(255);
    text("It's year 2020... Things went south...\nIn fact, so south that you got stuck in a mysterious cave...\nSurvive the cave of HELL with full of GOLD COINS.\nMake your way out through flying lava bombs.\n\nTo maneuver, press → ↑ ↓\nPress ANY KEY to start",width/2-200,height/2-20);
    return;
  }
  
  background(0);
  cave = loadImage("cave2.jpg");
  //Using the width and height of the photo for the screen size
  cave.resize(width,235); 
  image(cave,0,67);
  caveStone();
  
  for (int i = coins.size() -1; i >= 0; i--){
    Coin c = coins.get(i);
    c.display();
    
    if(player.checkCollisionCoins(c)){
      sound2.rewind();
      sound2.play();
      
      myCoins += 10;

      coins.remove(i);
    }
  }
  
  for (int i = bombs.size() -1; i >= 0; i--){
    Bomb b = bombs.get(i);
    b.display();
   
    if(player.checkCollisionBombs(b)){
      sound.rewind();
      sound.play();
      player_c = color(255, 0, 0);
      myHealth -= 1.3;
      if(myHealth <= 0){
        myPlayer -= 1;
        myHealth = 100;
      }
    } 
  }

  player.display();
  player.move();
 
  //score
  textSize(15);
  fill(255);
  text("Player life = " + myPlayer, 60, 30, 255); // show life
  textAlign(LEFT);
  text("Coins = " + myCoins, 10, 55, 255); // show coins gathered 
  textAlign(LEFT);
  text("Health = " + myHealth, 110, 55, 255); 
  
  if(myPlayer == 0){
    setup();
    myCoins = 0;
    myHealth = 100;
    myPlayer = 3;
  } else {
    player_c = color(100, 200, 200);
  }

}


//Player
class Player{
  PVector pos;
  PVector acc;
  PVector vel;
  PVector speed;
  
  Player(){
    pos = new PVector(-5, height/2);
    acc = new PVector(0.8, 0.8);
    vel = new PVector(0,50);
    speed = new PVector(0.9,0);
  }
  
  void display() {
    fill(player_c);
    rect(pos.x, pos.y,30,30, 7);
  }
  
  void move(){
    vel.mult(5);
    vel.limit(10);
    vel = vel.add(acc);
    pos.add(speed);
    
    if(pos.x < -50){
      pos.x = width; 
    }
    if(pos.x > width){
      pos.x = -50;  
      bombs = new ArrayList<Bomb>();
      coins = new ArrayList<Coin>();
      for(int i=0; i < 10; i++){
        coins.add(new Coin());
      }
      for(int i=0; i < 15; i++){
        bombs.add(new Bomb());
      }
    }
    if(pos.y > height - 120 ){
      pos.y = height - 120; 
    }
    if(pos.y < 120){
      pos.y = 120; 
    }
    
    if(keys[RIGHT])
      pos.x += vel.x;
    if(keys[UP])
      pos.y -= vel.y;
    if(keys[DOWN])
      pos.y += vel.y;
  }
  
  boolean checkCollisionBombs(Bomb b){
     if(dist(player.pos.x, player.pos.y, b.pos.x, b.pos.y) <= b.b_size){
        return true; 
      } else {
        return false;
      }
  }

  boolean checkCollisionCoins(Coin c){
    if(dist(player.pos.x, player.pos.y, c.pos.x, c.pos.y) < c.radius){
        return true;
      } else {
        return false;
      }
  }
}

class Coin{
  
  PVector pos;
  PVector acc;
  PVector vel;
  color c;
  float radius;
  
  Coin(){
    pos = new PVector(random(50, width), random(110, height-110));
    acc = new PVector(0,0);
    vel = new PVector(0,0);
    radius = 20;
    c = color(255, 204, 0);
  }
  
  void display(){
    pushMatrix();
    noStroke();
    translate(pos.x, pos.y);
    fill(80);
    ellipse(2,2, radius, radius);
    fill(c);
    ellipse(0,0,radius, radius);
    fill(0);
    textSize(15);
    textAlign(CENTER);
    text("$",0,4);
    popMatrix();
  }
}


class Bomb {
  PVector pos;
  PVector acc;
  PVector vel;
  float b_size = 25;
  color c;
  float direction = random(2,8);
  
  Bomb (){
    pos = new PVector(random(100, width), random(110, height- 110));
    acc = new PVector(0,0);
    vel = new PVector(0,0);
    c = color(255, 0, 0);
  }
  
  void display(){
    pushMatrix();
    translate(pos.x, pos.y);
    fill(c);
    rect(0, 0, b_size, b_size);
    pos.y += direction;
    if (pos.y < 110 || pos.y > height-110){
      direction *= -1;
    }
    popMatrix();
  }
  
}

void keyPressed(){
  if (instruction) {
    instruction = false;
  }
  else if (gameover){
    gameover = false;
  }
  else {
    keys[keyCode] = true;
  }
}

void keyReleased(){
  keys[keyCode] = false;
}



void caveStone(){
  rock = loadImage("base.jpg");
  rock2 = loadImage("base2.jpg");
  rock.resize(30,30);
  rock2.resize(30,30);
  
  for(int i = 0; i <= width; i += 30) {
    image(rock,i,67);
    image(rock,i,302);
  }
  for(int j = 0; j <= width; j += 90) {
    image(rock2,j,67);
    image(rock2,j,302);
  }
}

 

Week 7 – Midterm

Here we are, after Fall-ish break also known as Midterms Week:

Back to the Spelling Game

After a lot of planning, and not much sleep, I managed to achieve the general idea of my game, with a few bugs and missing elements. Although I would have loved to have it as I imagined, I think with the amount of time + midterms that I had, this is a cool result!

Additionally, if you still didn’t notice, I always try my best to avoid making games for our assignments and often opt for the artwork option. The thought of coding a game often scares me, especially when I think of moving between screens and checking for correct moves. So, this was a great challenge for me! And while I feel like I’m a bit more familiar with game states and other elements, I still think I can work on my game-making skills, and will probably end up looking at your codes for help 🙂

what planning looks like for me

List of Classes: 

  • Animals: this is where I load and display the visual for my animal
  • Buttons: where I create the start and replay buttons
  • Letter: Where I create letter blocks for a keypad grid and check for player clicks.

 

 

Steps

  • Make a start button, start screen, and then switch to the gameplay screen that is linked with an animal.

 

  • Make a keypad for each possible animal. This keypad must contain the letters of the animal name in random order, and it must not repeat letters within the alphabet list:

For this bit, I created two functions

  1. WordCharsinKeypad(): fills the characters from the animal name into the keypad randomly (the letters won’t appear next to each other).
  2. fillRemainingKeys(): checks for remaining empty spots in the keypad and fills them with a random letter from the alphabet without repeating letters from that alphabet array.
  • Display the game playing screen, make the keypad interactive, and give feedback to the user by printing on the screen the letters they get correctly:

I initially aimed to provide two types of feedback. One, which is evident in my program, is that the animal is gradually spelled out on the screen and a letter is added when the player picks the correct letter.

The other, however, I struggled with, which is showing a “try again” message when the player presses the wrong letter. For now, the program just doesn’t respond to a wrong letter.

When I tried to apply the second feedback this is what I got:

  • Finally, I wanted to switch between screens, as you can see above, move from the start screen to the gameplay screen, to the replay screen:

For the start and replay, I relied on buttons from my button class, however, for moving from gameplay to replay screen, it only requires the player to guess the complete word. A small issue I have here is that the last letter does not display before moving to the replay screen. I tried to resolve it with delay()  and altering the if condition in the code, but I still haven’t figured it out.

Some Design Choices

  • I was truly excited to design my own animals, I thought it would be a great chance to work on my Adobe Illustrator skills. However, I placed that as my last priority and unfortunately did not have time for that. So I used free PNGs from open source websites.
  • My game is missing sound, this is something that I will work on and practice as I still feel a bit unfamiliar with it
  • I tried to use soft colors with great contrast to appeal to the child’s eye
  • Keeping accessibility in mind, I made sure to use a font that is legible for most kids. This typeface is OpenDyslexic and it is specifically designed to make it easier for anyone with dyslexia to read a text.

Example of a Game Run

Future Developments

For a more developed version of this game, it would probably incorporate the sounds I mentioned in my progress post, my own designs, and levels rather than one run.

Here’s my code!

Main Function

// to load OpenDyslexic Font
PFont f; 
//for final screen
PImage confetti; 
boolean wrongLetter = false;
//startScreen
int screenNumber = 0;
//a string array to pick a random animal each time start is pressed:
String[] animalNames = {"CAT", "OWL", "FROG", "FISH", "PANDA", "MONKEY", "TURTLE", "CHAMELEON"}; 
// string where the random animal picked from array is stored:
String word; 
//character array of all letters in the alphabet:
char alphabet[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; 
//initializing a letter object array for a keypad:
Letter[] keypad = new Letter[12]; 
//initializing the animal object for the animal displayed in the run:
Animals animal4Run; 
//columns for keypad grid display
int cols = 3;
//rows for keypad grid display
int rows =4; 
// a variable that checks the players progress of selecting the correct letters -- e.g. if they have "CA" out of "CAT" it will equal 1 
int playerPosition = 0; 
//initializing a button object for start button
Buttons startButton;
// initializing a button object for replay button
Buttons replayButton; 
//an empty string variable. It will be used to reflect the player's progress in choosing the correct letter
String wordInProg; 

void setup() {
  size(960, 540);
  confetti = loadImage("CONFETTI.png");
  confetti.resize(960, 540);
  //using OpenDyslexic so the font could be readable for most
  f = createFont("OpenDyslexic-Bold.otf", 20);  
  textFont(f);
  //creating the start button object
  startButton = new Buttons(width/2, height/2+100, 100, 70, "Start"); //creating the start button object
  //creating the replay button object
  replayButton = new Buttons(width/2, height/2+200, 100, 70, "Replay"); 
  //setup the word in progress as an empty string, it will fill gradually as the player picks the correct letters.
  wordInProg = "";
  //generates a random index to pick a random animal each time run is clicked
  int indexForRun = (int)random(0, animalNames.length); 
  //sets the word variable using the generated random index
  word = animalNames[indexForRun];
  //creates an animal object using the word
  animal4Run = new Animals(word); 
  //index variable to go through all objects in the keypad array
  int index = 0; 
  for (int c = 0; c < cols; c++) {
    for (int r = 0; r < rows; r++) {
      //using columns and rows for a grid display
      keypad[index] = new Letter('x', c, r); 
      index += 1;
    }
  }

  //function that fills the characters from the animal name into the keypad in random positions
  wordCharsInKeypad(); 
  //function that fills the remaining empty keypad spaces
  fillRemainingKeys();
}

void draw() {
  background(227, 181, 164);
  //switch function to move between game screens
  switch(screenNumber) { 
  // case 0 is the start screen
  case 0:
  //calls setup to randomize a new animal everytime start is pressed
    setup(); 
    fill(232, 95, 92);
    textSize(60);
    text("Spell the Animal Name Game!", width/2, height/2 - 100);
    //display function for start button
    startButton.buttonDisplay();
    //checks if player clicks the start button and moves on to the next game screen
    if (mousePressed && mouseX >= startButton.xLoc - startButton.buttonW/2 && mouseX <= startButton.xLoc + startButton.buttonW/2 && mouseY >= startButton.yLoc - startButton.buttonH/2 && mouseY <= startButton.yLoc + startButton.buttonH/2) {
      screenNumber = 1; 
    }
    break;
  // playing screen
  case 1: 
    fill(156, 255, 250);
    textSize(50);
    text("What is this animal called?", width/2, height/2 - 200);
     //displays the player's progress in choosing the correct letters on the screen
    text(wordInProg, width/2-50, height/2 + 200);
    for (int i = 0; i < keypad.length; i ++) {
      //displays all letter objects in the keypad
      keypad[i].display(); 
      //displays the image of the animal in question
      animal4Run.display(); 
      //loops through all letters to check if 1. they are clicked , 2. if they are the correct letter 
      for (int j = 0; j < keypad.length; j++ ) { 
        textSize(20);
        //calls a function that checks whether the letter is clicked 
        keypad[j].letterIsClicked(); 
        if (keypad[j].letterClicked == true) {
          keypad[j].letterClicked = false;
          //println(">>>", keypad[j].letter, cat.animalName.charAt(playerPosition));
          if (keypad[j].letter != animal4Run.animalName.charAt(playerPosition)) {
            text("try again", width/2 - 300, height/2);} //?????
          //checks if the character clicked in the keypad matches the character that is at the player's position in the animal name
          if (keypad[j].letter == animal4Run.animalName.charAt(playerPosition)) {
            //this condition ensures that it adds the correct letters only for as long as the word in progress is the same length as the animal name, otherwise you would get "CATTTTTTTTTT" 
            if (wordInProg.length() < animal4Run.animalName.length()) {
              //keeps adding to reflect the player's progress
              wordInProg = wordInProg + animal4Run.animalName.charAt(playerPosition);
              //println(wordInProg);
              //println(keypad[j].letter + " " + cat.animalName.charAt(playerPosition));
              //condition: as long as the player position is still less than the length of the animal name, keep adding as the name is not complete yet
              if (playerPosition < animal4Run.animalName.length() -1) {
                playerPosition+= 1;
               //checks if the word is complete to switch to end screen that reflects success and gives replay button
              } else if (playerPosition == animal4Run.animalName.length()-1) {
                screenNumber = 2;
                break;
              }
            }
          }
        }
      }
    }
    break;
   //end screen
  case 2:
  //resets player position for next iteration of the game
    playerPosition = 0;
    for (int k = 0; k < keypad.length; k++ ) {
      //resets any letters clicked
      keypad[k].letterClicked = false; 
    }
    background(227, 181, 164);
    imageMode(CORNER);
    //tint(255);
    //loads confetti image for celebration :)
    image(confetti, 0, 0);
    fill(232, 95, 92);
    textSize(60);
    text("What a Spelling Champ!", width/2, height/2 - 100);
    fill(156, 255, 250);
    text("Press Replay to Spell Again!", width/2, height/2);
    //displaying replay button
    replayButton.buttonDisplay();
    //checks if pressed to go back to start screen
    if (mousePressed && mouseX >= replayButton.xLoc - replayButton.buttonW/2 && mouseX <= replayButton.xLoc + replayButton.buttonW/2 && mouseY >= replayButton.yLoc - replayButton.buttonH/2 && mouseY <= replayButton.yLoc + replayButton.buttonH/2) {
      screenNumber = 0;
    }
  }
}


//function that inputs the characters of the animal name in the keypad
void wordCharsInKeypad() {
  //a boolean to check if all the characters have been input
  boolean complete = false;
  //i variable to loop through all letters in the animal name
  int i = 0;
  while (complete == false) { 
    //generating a random index so letters don't appear in correct order in the keypad
    int randomIndex = (int) random(keypad.length - 1);  
    // condition: if the key is empty, signified by 'x', fill it with this letter, and then move to the next character (to avoid overwriting keys)
    if (keypad[randomIndex].letter == 'x') { 
    
      keypad[randomIndex].letter = animal4Run.animalName.charAt(i); 
      i += 1;
    }
    //if all the letters from the animal name are in the keypad, exit the while loop
    if (i == animal4Run.animalName.length()) { 
      complete = true;
    }
  }
}

//function to fill the rest of the keypad
void fillRemainingKeys() {
 //same boolean and variable concept as before
  boolean complete = false; 
  int j = 0;
  while (complete == false) {
    //this boolean is to check if the chosen letter already exists in the keypad
    boolean duplicated = false;
    //generates a random index to input a random letter from the alphabet
    int randomIndex = (int) random(alphabet.length -1);
    // println(j, keypad[j].letter, alphabet[randomIndex], word.indexOf(alphabet[randomIndex]));
    //if this position in the keypad is empty AND the chosen character is not in the animal name (as that wouldve been input in the prev function, fill it in the keypad)
    if (keypad[j].letter == 'x' && animal4Run.animalName.indexOf(alphabet[randomIndex]) == -1) {
      //for loop that checks if the letter that we are about to input already exists in the keypad to avoid repetition
      for (int k = 0; k < keypad.length; k++) {
        if (keypad[k].letter == alphabet[randomIndex] ) {
          duplicated = true;
          //if it is a duplicate it breaks out of this for loop and restarts to generate a new letter
          break;
        }
      }
      //to continue trying to generate
      if (duplicated) {
        continue;
      }
      // if it isn't a duplicate it inputs it into the keypad
      keypad[j].letter = alphabet[randomIndex];
    }
    //if the letter is in the word, meaning it is already in the keypad, also continue to generate something else
    if (word.indexOf(alphabet[randomIndex]) != -1) {
      continue;
    }
    j+= 1;
    //condition to exit the while loop when the keypad is fully formed
    if (j == keypad.length) {
      complete = true;
    }
  }

  //for (int k = 0; k < keypad.length; k++) {
  //println(k, keypad[k].letter);
  // }
}

Animals Class

class Animals {
  //two vars for animal name and to load the suitable image
  String animalName;
  PImage animalImage;
  
  
  Animals(String tempAnimalName){
    animalName = tempAnimalName;
    //using animal name to load the image
    animalImage = loadImage(animalName+".png");
    animalImage.resize(300,300);
  }
  
  
  void display(){
   fill(0,255,0);
   imageMode(CENTER);
   image(animalImage,width/2-50,height/2);
   //rect(width/2,height/2,100,100);
   //fill(0);
   //textAlign(CENTER,CENTER);
   //textSize(15);
   //text("I am a" + " " + animalName, width/2,height/2);
  }
}

Buttons Class:

  class Buttons {
    int xLoc, yLoc, buttonW, buttonH; 
    String buttonTitle;
    //
    
    
  
    Buttons(int xLocTemp, int yLocTemp, int buttonWTemp, int buttonHTemp, String buttonTitleTemp) {
      xLoc = xLocTemp;
      yLoc = yLocTemp;
      buttonW = buttonWTemp;
      buttonH = buttonHTemp; 
      buttonTitle = buttonTitleTemp;
      
      
    }
  
  
  

  
  
    void buttonDisplay() {
      rectMode(CENTER);
       noStroke();
       fill(200);
       rect(xLoc, yLoc, buttonW, buttonH);
       fill(0);
       textSize(25);
       textAlign(CENTER,CENTER);
       text(buttonTitle, xLoc, yLoc);
      }
  }

Letter Class:

class Letter {
  char letter; 
  int xLoc, yLoc; 
  int blockW = 70;
  int blockH = 70;
  int colNo;
  int rowNo;
  //booleans to check during the game
  boolean letterClicked;
  boolean letterCorrect;


  Letter(char letterTemp, int tempColNo, int tempRowNo) {
    letter = letterTemp;
    colNo = tempColNo;
    rowNo = tempRowNo;
  }


  void display() {
    //as it is a grid object, col and row number are used for x and y coordinates
    rectMode(CORNER);
    fill(0);
    stroke(255);
    rect(700+colNo*70, 150+rowNo*70, blockW, blockH);
    fill(255);
    textSize(20);
    textAlign(CENTER, CENTER);
    text(letter, 735+colNo*70, 185+rowNo*70);
  }
  
  
//the function that checks if the letter is clicked
 void letterIsClicked() {
    if (mousePressed && mouseX >=  700+(colNo)*70  && mouseX <= 700+(colNo)*70 + blockW && mouseY >= 150+(rowNo)*70 && mouseY <= 150+(rowNo)*70+blockH) {
     // println("mouseY: ", mouseY, " - 200+(rowNo-1)*70: ", 200+(rowNo-1)*70, " - 200+(rowNo-1)*70+blockH: ", 200+(rowNo-1)*70+blockH);

      letterClicked = true;
      //println(letter, " Clicked");
    }
  }
  
}

Zip: midtermm_Game

Nathan – Midterm Project

For this project, I continued to want to make a two-player game. The game is competitive in nature, where the two players will rush to grab valuable resources before it’s all taken by the other.

Inspiration

There isn’t any single source of inspirations for this game. It’s 2D interface kind of resembles the Nintendo pokemon games, where the world is a flat surface and players take a birds-eye view of the world. The mode of the game actually reminds me of the lego star wars game, in some ways. Players have to rush to collect resources while dodging dangerous objects.

Game-Specific Information

There are two heroes, a ninja and a warrior that are on a quest together in an underground mine. While resting, candies start to float out of the caves, and the two heroes decide to grab candies to eat. However, there are edible candies and poisonous candies, and only the candy indicator tells the players which candies are edible. In the game, this candy indicator is located at the bottom middle of the screen, as shown:

If a player “eats” an edible candy, one point will go to their score, which shows at their spawn point, at the bottom of the screen. If a player “eats” a poisonous candy, they die and respawn at their spawn point. Once all the edible candies are collected, the player with the most candy wins. If both have the same amount, it is a tie.

When the game initializes, there is a starting screen with the name of the game, “Candy Rush”, as well as the instructions and a “play” button. When hovering the mouse over the play button, the button will be highlighted:

Regarding the sounds, the game has three in total. One is the background music that loops endlessly. The other two are sounds for when the hero eats a poisonous candy and an edible candy.

There are two classes, one for the heroes and one for the candies. The hero tab simply includes its display and move functions. The candy tab includes its display, collision with the heroes, collision with a wall, and movement functions. The main code page, apart from draw and setup, includes four main functions. The reset function is in charge of initializing all the game values. Most of these functions could have gone into setup, but I needed to create a restart function for when the game ends. Thus, I moved most of the value initialization into the reset function, and then called the reset function in setup. This way, I could decide when to “re-setup” my game.

The next function is menu, which is in charge of the starting menu. I run this function first in draw. While running the menu function, when the player clicks “play”, the universal boolean “go” will turn to true, triggering the begin function.

The begin function is the main gameplay, where all the object functions are called. At the end of the begin function, the last function is called, which is checkwin.

Checkwin simply runs through the win conditions constantly and displays the winning screen when the win condition is met.

Images used

Background:

Hero Sprites:

Sounds used

Background Music:

Catching edible candy:

Catching poisonous candy:

 

Full Code

Main Code Page:

PFont font;                              

import processing.sound.*;            //all the sounds in this game
SoundFile eat;
SoundFile die;
SoundFile music;

Ball b1;                              //initializing the two players
Ball b2;
PImage ninja, warrior, bg;
PImage[][] ninjawalk;
PImage[][] warriorwalk;
int ndirection = 0;
int wdirection = 0;
int nstep = 0;
int wstep = 0;
int speed = 5;

Candy[] candy;                        //initializing all the candy
int count;
color chosen;
int livecount = 0;                    //how many edible candy there are

boolean go = false;                   //start game value

void setup() {
  size(1200, 800);
  bg = loadImage("bg.jpg");
  
  //sounds
  eat = new SoundFile(this, "eat.mp3");
  die = new SoundFile(this, "die.mp3");
  music = new SoundFile(this, "music.mp3");
  music.loop();
  
  //slicing the two sprite sheets for player 1 and 2
  ninja = loadImage("ninja.png");
  ninjawalk = new PImage[4][3];
  int w = ninja.width/3;
  int h = ninja.height/4;
  for(int iy = 0; iy < 4; iy++){
    for(int ix = 0; ix<3; ix++){
      ninjawalk[iy][ix] = ninja.get(ix*w, iy *h, w, h);
    }
  }
  warrior = loadImage("warrior.png");
  warriorwalk = new PImage[4][3];
  int ww = warrior.width/3;
  int wh = warrior.height/4;
  for(int wy = 0; wy < 4; wy++){
    for(int wx = 0; wx<3; wx++){
      warriorwalk[wy][wx] = warrior.get(wx*ww, wy *wh, ww, wh);
    }
  }
  //use this function to restart the game
  reset();
}

void draw() {
  menu();
  if (go == true){
    begin();
  }
}

//this function restarts the game from the gaming interface directly
void reset(){
  float unit = 200;
  b1 = new Ball(width/4, height - 20, 1);          //player 1 character
  b2 = new Ball(width*3/4, height - 20, 2);        //player 2 character
  
  int wideCount = width / int(unit);               //calling the candies, initializing different candy colors
  int highCount = (height*3/4) / int(unit);
  count = wideCount * highCount;
  candy = new Candy[count];
  int index = 0;
  color[] colors = new color[6];
  colors[0] = #FF0011;
  colors[1] = #BA00FF;
  colors[2] = #2500FF;
  colors[3] = #00FFE8;
  colors[4] = #00FF01;
  colors[5] = #FFAF00;
  chosen = colors[int(random(0,5))];
  for (int y = 0; y < highCount; y++) {
    for (int x = 0; x < wideCount; x++) {
      candy[index++] = new Candy(x * unit + unit/2, y * unit + unit/2, 20, 20, colors[int(random(0,5))]);
    }
  }
  
  for (Candy candy : candy) {                      //counting how many edible candies there are to know the win condition
    if (chosen == candy.c){
      livecount ++;
    }
  }
  
  for (int i =0; i<count;i++){                     //candy speed initializing
     candy[i].xSpeed = random(-2,2);
     candy[i].ySpeed = random(-2,2);
  }
}

//this is the starting menu
void menu(){
  background(bg);
  fill(255);
  font = createFont("BreatheFire-65pg.ttf", 60);
  textFont(font);
  textAlign(CENTER);
  text("CANDY RUSH \n even heros like candy", width/2, 185);
  font = createFont("Georgia", 40);
  textFont(font, 30);
  text("This is a two player game. Ninja and Warrior wants to take a break \n on their quest, so they are going to catch candy. Some candies are \n poisonous, and only the candy indicator can tell you which candies are edible. \n The first player to catch all the edible candy wins. \n NINJA: WASD, WARRIOR: direction keys", width /2, 370);
  stroke(0);
  fill(0);
  rectMode(CENTER);
  rect(width/2, 680, 200, 72);
  fill(255);
  textFont(font, 60);
  font = createFont("BreatheFire-65pg.ttf", 42);
  textFont(font);
  textAlign(CENTER);
  text("PLAY", width/2, 700);
  if (mouseX>=(width/2 - 100)  & mouseX<=(width/2 + 100) & mouseY>=650 & mouseY<=720){
    stroke(255);
    noFill();
    rect(width/2, 680, 200, 72);
    if (mousePressed){
        go = true;
    }
  }
}

//this is the game starting function. the actual game is ran here
void begin(){
  background(bg);
  textSize(24);
  text(b1.score, width/4, height - 50);
  text(b2.score, width*3/4, height - 50);
  
  //calling candy functions
  for (Candy candy : candy) {
    candy.display();
    candy.collision();
    candy.update();
    candy.checkEdges();
  }
  
  //this is the candy indicator
  stroke(0);
  fill(chosen);
  ellipse(width/2, height - b1.diameter, b1.diameter, b1.diameter);
  
  //calling character functions
  b1.display();
  b1.move();
  b2.display();
  b2.move();
  checkwin();
}

void checkwin(){
  if (livecount == 0){                      //win condition
    for (Candy candy : candy) {
      candy.xSpeed = 0;
      candy.ySpeed = 0;
    }
    rectMode(CENTER);
    if (b1.score > b2.score){
      fill(0);
      rect(width/2, height/2, width, height);
      fill(255, 255, 255);
      textSize(32);
      text("Ninja Wins! \n (Press r to restart)", width/2, height/2); 
    }
    else if (b2.score > b1.score){
      fill(0);
      rect(width/2, height/2, width, height);
      fill(255, 255, 255);
      textSize(32);
      text("Warrior Wins! \n (Press r to restart)", width/2, height/2);
    }
    else if (b2.score == b1.score){
      fill(0);
      rect(width/2, height/2, width, height);
      fill(255, 255, 255);
      textSize(32);
      text("Tie! \n (Press r to restart)", width/2, height/2);
    }
    
    //restart button
    if (keyPressed) {                            
       if (key == 'r' || key == 'R') {
          reset();
       }
    }
  }
}

Player Class Page:

class Ball {

  float x;
  float y;
  float diameter;
  int score;
  int player;
  int w = ninja.width/3;
  int h = ninja.height/4;
  int ww = warrior.width/3;
  int wh = warrior.height/4;

  Ball(float tempX, float tempY, int tempPlayer) {
    x = tempX;
    y = tempY;
    diameter = 45;
    score = 0;
    player = tempPlayer;
  }

  void display() {
    imageMode(CENTER);                                    //displaying player 1
    if (player == 1){
      image(ninjawalk[ndirection][nstep],x,y,w,h);
    }
    if (player == 2){                                     //displaying player 2
      image(warriorwalk[wdirection][wstep],x,y,ww,wh);
    }
  }
  
  void move() {  
    if (player == 1){
      if (keyPressed) {
        if (key == 'w' || key == 'W') {
          y = y - 5;
          ndirection = 0;
        }
        if (key == 'a' || key == 'A') {
          x = x - 5;
          ndirection = 3;
        }
        if (key == 's' || key == 'S') {
          y = y + 5;
          ndirection = 2;
        }
        if (key == 'd' || key == 'D') {
          x = x + 5;
          ndirection = 1;
        }
        if (frameCount%speed == 0) {
          nstep = (nstep + 1) %3;
        }
      }
    }
    
    if (player == 2){
      if (keyPressed) {
        if (keyCode == UP) {
          y = y - 5;
          wdirection = 0;
        }
        if (keyCode == LEFT) {
          x = x - 5;
          wdirection = 3;
        }
        if (keyCode == DOWN) {
          y = y + 5;
          wdirection = 2;
        }
        if (keyCode == RIGHT) {
          x = x + 5;
          wdirection = 1;
        }
        if (frameCount%speed == 0) {
          wstep = (wstep + 1) %3;
        }
      }
    }
  }
}

Candy Class Page:

class Candy {
  float x;
  float y;
  float w;
  float h;
  float xSpeed, ySpeed;
  boolean caught = false;
  color c;

  Candy(float tempX, float tempY, float tempW, float tempH, color tempC) {
    x = tempX;
    y = tempY;
    w = tempW;
    h = tempH;
    c = tempC;
    xSpeed = ySpeed = 0;
  }  

  void display() {

    ellipseMode(CENTER);
    if (caught == false){
      fill(c);
      ellipse(x, y, w, h);
    }
  }
  
  //collision function for when players catch the right and wrong candy
  void collision() {
    if ((b1.x - b1.w *3/4 < x && x < b1.x + b1.w*3/4) & (b1.y - b1.h*3/4 < y && y < b1.y + b1.h*3/4)){
      if (c == chosen){
        x = -50;
        y = -50;
        caught = true;
        b1.score ++;
        livecount --;
        eat.play();
      }
      else if (c != chosen){
        b1.x = width/4;
        b1.y = height - b1.diameter;
        die.play();
      }
    }
    if ((b2.x - b2.w*3/4 < x && x < b2.x + b2.w*3/4) & (b2.y - b2.h*3/4 < y && y < b2.y + b2.h*3/4)){
      if (c == chosen){
        x = -50;
        y = -50;
        caught = true;
        b2.score ++;
        livecount --;
        eat.play();
      }
      else if (c != chosen){
        b2.x = width*3/4;
        b2.y = height - 20;
        die.play();
      }
    }
  }
  
  void update(){
    x += xSpeed;
    y += ySpeed;
  }
  
  void checkEdges() {
    if (y>(height*3)/4) {
      ySpeed = -ySpeed;
    }
    if (y<0) {
      ySpeed = -ySpeed;
    }
    if (x>width) {
      xSpeed = -xSpeed;
    }
    if (x<0) {
      xSpeed = -xSpeed;
    }
  }
}

 

Entire Compressed Code

Midterm

Game Demonstration