Unusual switch

Concept

I decided to create a ‘black-box’ type switch.  The switch consists of two jumper cables which are connected to the insides of a small box. The box is filled with a few metal sheets and balls of copper tape — essentially a few separate pieces of conductive material.

The switch is initially open. It can be closed by shaking or moving around the box. This causes the contents of the box to move around chaotically. With enough movement, they will connect in such a way that the current from one jumper cable can move to the other one, closing the switch and completing the circuit.

However, by deliberate design, the switch is difficult to control. It isn’t easy to close it immediately, or open it afterwards. Also, often, while moving around the box, it closes for only a brief moment, sending a short signal but then stopping. This creates a kind of controlled chaos and I can imagine using it to create interesting effects using Arduino in the future.

Finally, although the switch can be used most easily with hands, it works with any parts of your limbs, as long as you can move the box.

Pictures and video demo

I created a simple circuit to test that the switch works. It looks like this: Arduino 5 Volts -> first jumper cable -> box -> second jumper cable -> LED light -> 300 Ohm resistor -> Arduino ground. The LED light turns on when the switch is closed.

How the switch looks like from the outside.
The contents of the box.

 

Assignment 5: Unusual Switch

Idea:

For my unusual switch, I decided to link something I love doing into my assignment which is skateboarding. There’s this skating move called “manual” where you balance the board on your back wheels and continue riding, kinda like this picture shown below:

If the board touches the ground, it’s not a manual, however if you manage to keep the board up and balanced, you’re doing a manual. So for my unusual switch, essentially, you would do a manual, and if the back end of the board would touch the ground, then it would connect the circuit, hence acting like a switch.

So I taped some aluminum foil to my board and some to the ground. If I manage to do a manual (aka the board not touching the ground), the LED light will be off because the circuit isn’t connected. Else, if the board does touch the ground, the two foils will touch and thus connecting the circuit and the LED light would switch on.

Here is a simple circuit diagram I have constructed to show my assignment:

 

Video:

Midterm Game: Star Shooter

Inspiration:
The inspiration for Star Shooter comes from a variety of sources, including classic arcade games like Space Invaders and Galaga, as well as more modern games like Geometry Wars and Asteroids. These games typically involve shooting at large moving targets, often with a spaceship or other vehicle as the player’s avatar. The use of space-themed graphics, such as stars and comets, and the inclusion of a timer and score system are also one of the common features of shooting games. The game draws inspiration from science fiction movies and TV shows, which often depict space battles and laser gun fights.

Features and challenges:

One of the main features of the game is that it uses object-oriented programming to create dots and bullets that move around the screen. I had to use arrays to hold all of the dots and bullets, and I spent a lot of time making sure that the movement and collision detection was smooth and accurate, using efficient algorithms for collision detection. I used the testIntersection() function that allows the measurement of the distance between the radius of the bullet and the comet. If the distance is less than or equal to the sum of their radii, the bullet has intersected with the dot.

testIntersection(dot) {
  var d = dist(this.x, this.y, dot.x, dot.y);
  if (d <= this.r + dot.r) {
    return true;
  }
}

preload(): This function is used to load all the necessary assets (images) for the game before it starts. It is called once when the program starts. The arguments are the file paths to the image files that need to be loaded. The function works by using the loadImage() function provided by p5.js to load the images and store them in global variables for later use.

mousePressed(): This function is called once every time the left mouse button is pressed. It is used to create a new bullet object and add it to the bullets array. The function works by creating a new instance of the Bullet class and passing the current x-coordinate of the mouse as an argument. The new bullet is then added to the bullets array.

keyPressed(): This function is called once every time a key is pressed. It is used to create a new bullet object and add it to the bullets array when the spacebar key is pressed. The function works by checking if the key that was pressed is the spacebar key, and if it is, creating a new instance of the Bullet class and adding it to the bullets array.

Bullet.display(): This method is used to display the bullet on the canvas. It takes no arguments. The function works by drawing a rectangle on the canvas with a white fill color and a size of 10×20 pixels, using the bullet’s x- and y-coordinates.

Bullet.move() : This method is used to move the bullet up the screen. It takes no arguments. The function works by subtracting a fixed value from the bullet’s y-coordinate every frame, causing it to move up the screen.

Bullet.testIntersection(): This method is used to test if the bullet has intersected with a dot (enemy). It takes a single argument, which is a dot object to test against. The function works by calculating the distance between the center of the bullet and the center of the dot, and comparing it to the sum of their radii. If the distance is less than or equal to the sum of their radii, the bullet has intersected with the dot.

Dot.display(): This method is used to display the dot on the canvas. It takes no arguments. The function works by drawing an ellipse on the canvas with a red fill color and a size of 60×60 pixels, using the dot’s x- and y-coordinates.

Dot.move(): This method is used to move the dot around the canvas. It takes no arguments. The function works by adding a random value to the dot’s x- and y-coordinates every frame, causing it to move in a random direction. If the dot reaches the edge of the canvas, it is wrapped around to the opposite edge.

Mid-Term Project: Traffic Racer

Midterm Project – Duck Hunt

Duck Hunt sketch: 

For this assignment, I researched retro arcade games and decided I want to make a replica of the classic NES game Duck Hunt. Initially, I started with the background on which I built the rest of the game. Then, I used a sprite sheet that changes the ducks to dead ducks when shot. That was done using the following code, which is also the code that decides the gravity of the falling duck:

if (shot == true) {
  speed = -7;
  speed2 = 0
  push();
  noStroke();
  fill(220);
  duck(deadDuck);
  pop();
  
  if (y > 440) {
    shot = false;
    x = random(5, 580);
    speed = random(4,12);
    speed2 = random(-7,7);
    score += 1;
  }
}

I had several versions of the game until I decided to go with the final one in which you have unlimited ammo but a limited time to shoot as many ducks as possible. This way, the game can be played by anyone, however the score gives it a competitive edge in a sense that players can compete with their own previous score.

Regarding the code and the technical aspect of my assignment, I would say I am most proud of setting the timer to countdown from whatever value we assign to it. This was done with the following code:

fill(0, 0, 0);
  textSize(30);
  text("Time Left:" + timer, 25, 380)
  if (frameCount % 60 == 0 && timer > 0) {
    timer --;
  }
  if (timer == 0) {
    //text("GAME OVER", 200, 200);
    window.location.reload();

The timer additionally ends the game and displays the home screen. This particular switch from the home screen to the game was a problem that I was luckily able to overcome. Additionally, I ran into a problem while using OOP in one of the earlier versions. As I could not fix this problem I decided just to switch up and not use OOP. This is one of the few key changes that should be done to improve the game in the future. Other than this, I should improve the spawning points of the ducks as well as implement another paradigm to run the game including limited ammo, power ups and difficulty levels.

Finally, this game has a lot of space to improve, but it is a good starting point. I personally chose this game in order to recreate the feelings and experience of hunting for ducks on the NES using the gun controllers. I plan on doing this with the help of Arduino.

 

Midterm – 3 Rooms

You can visit my project titled “3 rooms” by clicking this link: https://editor.p5js.org/iremnaz/full/aFq48HNIB

or

 

Project Background:

This project is a visual, auditory, and interactive experience that hopes to include the user through interactivity allowed only in limited ways. The level of involvement and “the representation” of the user slowly recedes, only to be established somewhat at the end. The experience hopes to be enjoyable, surprising, disorienting, and thought-provoking.

Each room is chosen and designed after many trials and errors, as there is no set way of looking and behaving for each room, except for the feelings it should be awakening.

The Workings Of It:

Prior to each room, the instructions of what you can do is explicitly given.

For room 1 you can use the arrow keys.

For room 2 you can use the mouse to Click only on the decided sections of the screen.

For room 3 you can only press the ENTER key.

The user is able to proceed using the W key between information pages, but can never go back or exit the experience. These were conscious choices.

Things That Work and Do Not Work:

Room 1 definitely turned out well, as I especially love the colors, movements, and songs inside it.

Room 2 feels like there is room for improvements, purely by using self-created and themed artwork instead of outsourcing from royalty-free pieces. I have the feeling that experience in room 2 has more room for improvement.

Room 3 was especially meaningful for me. I feel like I did all of this project to actually be able to make room 3. It also, as a theme, holds a lot of meaning for me, and I totally enjoyed making it.

Code Snippets:

The code for the sketch runner is as follows:

Codes for rooms are not given here as there are nearly 800+ code lines. Please check them out in the p5.js sketch.

let roomChoice = 0;
let tintB=255;

//preloading the necessary images and sounds
function preload() {
  sound1 = loadSound("myMusic.mp3");
  imgT = loadImage("myTrees.png");
  ph2 = loadImage("mount.png");
  ph3 = loadImage("cliff.png");
  img1 = loadImage("1.jpg");
  img2 = loadImage("2.jpg");
  img3 = loadImage("3.jpg");
  img4 = loadImage("4.jpg");
  img5 = loadImage("5.jpg");
  img6 = loadImage("6.jpg");
  sound = createAudio("chatter.mp3");
}

//setting up with the canvas size
function setup() {
  createCanvas(400, 400);
  setRoom2();
  img = img1;
}

//the room selection menu is follows in draw
function draw() {
  switch (roomChoice) {
    case 0:
      main();
      break;
    case 1:
      preRoom1();
      break;
    case 2:
      ready1();
      break;
    case 3:
      situation1 = runRoom1();
      if (situation1 == true) {
        roomChoice = 4;
      }
      break;
    case 4:
      preRoom2();
      break;
    case 5:
      ready2();
      break;
    case 6:
      situation3 = runRoom2();
      if (situation3 == true) {
        roomChoice = 7;
      }
      break;
    case 7:
      preRoom3();
      break;
    case 8:
      ready3();
      break;
    case 9:
      runRoom3();
      break;
  }
}

//this is the main room (intro room)
//setsup the initial aura 
function main() {
  stroke(255);
  strokeWeight(1);
  background(0);
  fill(255);
  textFont("Georgia", 15);
  text("Welcome to the experience!", 5, height / 10);
  text("This is you:", 5, (2 * height) / 10);
  hello = new user1(width / 2, height / 2);
  hello.draw();
  text("You will be experiencing 3 rooms.", 5, (8 * height) / 10);
  text("Press W to proceed.", 5, (9 * height) / 10);
  boundary1();
  if(tintB>0){
  background(0,tintB);
  tintB-=1;}
}

//just before the room 1 opens this is the last time 
//to take breather, press 2 to proceed
function ready1() {
  stroke(255);
  strokeWeight(1);
  background(0);
  fill(255);
  textFont("Georgia", 15);
  text("Are you ready?", width / 2 - 50, (1.5 * height) / 10);
  text("Press W to proceed.", width / 2 - 65, (9 * height) / 10);
  hello = new user1(width / 2, height / 2);
  hello.draw();
  boundary1();
}

//are you ready page before room 2 resumes
function ready2() {
  stroke(255);
  strokeWeight(1);
  background(0);
  fill(255);
  textFont("Georgia", 15);
  text("Are you ready?", width / 2 - 50, (1.5 * height) / 10);
  text("Press W to proceed.", width / 2 - 65, (9 * height) / 10);
  hello = new user1(width / 2, height / 2);
  hello.draw();
}

//are you ready page before room 3 resumes
function ready3() {
  stroke(255);
  strokeWeight(1);
  background(0);
  fill(255);
  textFont("Georgia", 15);
  text("Are you ready?", width / 2 - 50, (1.5 * height) / 10);
  text("Press W to proceed.", width / 2 - 65, (9 * height) / 10); 
}

//this is the page where instructions for room 1 is given
function preRoom1() {
  stroke(255);
  strokeWeight(1);
  background(0);
  fill(255);
  textFont("Georgia", 50);
  text("Room 1", 5, height / 10);
  textFont("Georgia", 15);
  text("Yellow line is your boundary.", 150, (3 * height) / 10);
  text("You exist, within it.", 7, (4.5 * height) / 10);
  text(
    "Experience room 1 by using the 4 arrow keys to move.",
    10,
    (6 * height) / 10
  );
  text(
    "Don't forget to dance, even if you don't want to.",
    30,
    (7.5 * height) / 10
  );
  text("even if you are lonely.", (2.5 * width) / 4, (8 * height) / 10);
  textFont("Georgia", 20);
  text("We are all happy here.", (1.2 * width) / 4, (8.8 * height) / 10);
  textFont("Georgia", 15);
  text("Press W to proceed to room 1.", 60, (9.5 * height) / 10);
}

//this is where instructions for room 2 is given
function preRoom2() {
  stroke(255);
  strokeWeight(1);
  background(0);
  fill(255);
  textFont("Georgia", 50);
  text("Room 2", 5, height / 10);
  textFont("Georgia", 15);
  text("You ONLY exist inside the yellow boundary.", 100, (3 * height) / 10);
  text(
    "When you click while you exist, you give life to the plant.",
    5,
    (4.5 * height) / 10
  );
  text("Experience room 2 by giving life to the plant.", 80, (6 * height) / 10);
  text(
    "When you click, you give love. You show effort.",
    5,
    (6.8 * height) / 10
  );
   text(
    "You have to show effort until you can move on.",
    70,
    (7.5 * height) / 10
  );
  textFont("Georgia", 20);
  text(
    "Is your love enough to grow the plant?",
    (0.4 * width) / 4,
    (8.5 * height) / 10
  );
  textFont("Georgia", 15);
  text("Press W to proceed to room 2.", (2 * width) / 4, (9.5 * height) / 10);
}

//this is where instructions for room 3 are given
function preRoom3() {
  stroke(255);
  strokeWeight(1);
  background(0);
  fill(255);
  textFont("Georgia", 50);
  text("Room 3", 5, height / 10);
  textFont("Georgia", 15);
  text("You DON'T exist.", 150, (2.5 * height) / 10);
  text("Until you are welcome here.", 7, (4 * height) / 10);
  textFont("Georgia", 20);
  text("Until you feel welcome here.", (1.5 * width) / 4, (5 * height) / 10);
  textFont("Georgia", 15);
  text("When you don't, everything looks foreign.", 30, (6 * height) / 10);
  text("Gather your courage and talk.", (2 * width) / 4, (7 * height) / 10);
  text("You talk when you PRESS on it. When you are ready.", 30, (7.8 * height) / 10);
  text(
    "Experience room 3 by pressing the ENTER key.",
    (0.2 * width) / 4,
    (8.8 * height) / 10
  );
  text("Press W to proceed to room 3.", (2 * width) / 4, (9.5 * height) / 10);
}

//this takes the keyboardinformation to change between 
//loading pages as well as the in-room movements
function keyPressed() {
  if (keyCode == ENTER) {
    counter = 0;
  }
  if (keyCode == 87 && roomChoice!=3 && roomChoice!=6) {
    roomChoice++;
    if (roomChoice == 3) setRoom1();
    if (roomChoice == 9) setRoom3();
    if (roomChoice == 10){tintB=255; roomChoice=0;}
  }
}

Reflections:

This midterm project really pushed me out of my comfort zone as I was confronted by artistic and built issues at the same time.

Per artistic issues, I feel that the way I have designed the introductory pages, the theme of the user icon with a ring around, as well as the themes in each room were kind of struggling to come through the other users. This was something I have realized both in my friends as well as classmates. In order to solve this, I did try to reorganize my introductory pages, and have the “Are you ready” pages stay until the user wishes to continue. These pages were where the difference in the user icon was explicitly shown different, and these pages were previously timed so that there were not enough time to grasp differences. However, I can see that even after making changes, the same issues persist to some degree. I think that by changing the design and flow of these intermediary stages between rooms, the experience can be improved. This is something that I definitely should focus on.

Per coding issues, I feel that each room was a struggle to make it work. Especially room 1, it was hard to figure out the pattern changes, their succession, and the user icon interaction with its environment. As there were many loops that are ongoing at the first part, we can see the icon motions lagging. Moreover, it was a lot of trial and error to have a feel for when to have the patterns change according to the song. Similarly, for room 2 sprites were quite the issue as they were not made in equal gaps, so I had to manually try and crop them. There was also a lot of trial and error in their placement in the sketch and the way they are cropped. In room 2, choosing any of the images was quite hard due to their processing but more importantly how they looked together. Room 3 was quite simple and straight forward in terms of the coding part, but figuring out the lines to be printed on the scene was quite hard.

I believe when I look at my journey overall, figuring out the storyline in each room, and the overall cohesion has been quite hard. Even after finishing the project, I can see that there is room for growth. I think an approach to figuring out this issue would be writing down the storyline on paper and seeing how it feels, instead of running the project over and over again.

Overall, I enjoyed making this project very much. However, it was definitely time-consuming as it pushed my creative and coding boundaries during the each stage. I feel excited to come back to this project and let it grow in the future.

Mid-Term Project – Snakes and Ladders

Snakes and Ladders

Concept

My project is a digital adaptation of the classic board game, Snakes and Ladders, which holds a special place in my heart due to fond childhood memories of playing it with my sister. I created this game as a way to relive those memories with her whenever we are apart. I made this to play the game with her whenever I am back home.

Upon opening the game, players are presented with a visually appealing cover page that offers the option to access instructions or dive right into the game play. The game features a soothing soundtrack that helps to enhance concentration and enjoyment. Two players can participate, each with a randomly assigned token represented by an ellipse in a unique color. The game board is comprised of four snakes and ladders each, with the placement randomized for each playthrough. These snakes and ladders can either hinder or assist the players, respectively, in reaching the goal of 100. As a luck-based game, there are no guarantees, but players who land on ladders can quickly ascend to the finish line, while those who land on snakes must descend to their tails. Upon completion, players can easily restart the game by simply pressing any key, returning them to the cover page for another round of fun.

Code

new p5();// Initialize the p5.js library as it random function was not working without this

// Define some constants for the game
const rows = 10;
const cols = 10;
const cellSize = 60;

// Define some global variables for game elements
let boardImage;
let startTime = 0;
let startTimeSnake = millis()*2;
let startTimeLadder = millis()*2;
let diceResult;
let dice1;
let dice2;
let dice3;
let dice4;
let dice5;
let dice6;
let cover;
let back;
let restart;
let instructions;
let bg_music;
let dice_sound;

// Define ladders
const ladders = [
  { start: 2, end: 22},
  { start: 16, end: 37},
  { start: 36, end: 74},
  { start: 58, end: 85}
];

// Define snakes
const snakes = [
  { start: 99, end: 1 },
  { start: 90, end: 61 },
  { start: 50, end: 23 },
  { start: 39, end: 17 }
];
// Preload all the game assets
function preload()
{
  boardImage = loadImage("boardImage.png");
  dice1 = loadImage("1.png");
  dice2 = loadImage("2.png");
  dice3 = loadImage("3.png");
  dice4 = loadImage("4.png");
  dice5 = loadImage("5.png");
  dice6 = loadImage("6.png");
  cover = loadImage("cover.png");
  back = loadImage("back.png");
  instructions = loadImage("instructions.png");
  restart = loadImage("restart.png");
  bg_music = loadSound("bgmusic.mp3");
}
// Set up the canvas
function setup()
{
  createCanvas(cellSize * cols, cellSize * rows);
}

function draw()
{ // Start playing background music if it's not already playing
  if (!bg_music.isPlaying()) 
  {
    bg_music.play();
  }
  board.display();
  
  //handling mouse click events
  handledMouseEvent = false;
  if ((millis() - startTime < 2000) && board.gameState == "start")
  {
    diceDisplay();
  }
  
  if ((millis() - startTimeSnake < 2000) && board.gameState == "start" && frameCount > 180)
  {
    board.snakeText();
  }
  
  if ((millis() - startTimeLadder < 2000) && board.gameState == "start" && frameCount > 180)
  {
    board.ladderText();
  }
  
  
}
// Roll the dice and return the result
function getDiceRoll() 
{
  startTime = millis();
  diceResult = floor(random(1, 7));
  return diceResult;
}
// Display the dice image based on the result out of the 6 images 
function diceDisplay()
{
  let diceWidth = 100;
  let diceHeight = 100;
  let diceX = 250;
  let diceY = 250;
  if (diceResult == 1)
  {
    dice1.resize(diceWidth, diceHeight);
    image(dice1, diceX, diceY);
  }
  else if (diceResult == 2)
  {
    dice2.resize(diceWidth, diceHeight);
    image(dice2, diceX, diceY);
  }
  else if (diceResult == 3)
  {
    dice3.resize(diceWidth, diceHeight);
    image(dice3, diceX, diceY);
  }
  else if (diceResult == 4)
  {
    dice4.resize(diceWidth, diceHeight);
    image(dice4, diceX, diceY);
  }
  else if (diceResult == 5)
  {
    dice5.resize(diceWidth, diceHeight);
    image(dice5, diceX, diceY);
  }
  else if (diceResult == 6)
  {
    dice6.resize(diceWidth, diceHeight);
    image(dice6, diceX, diceY);
  }
}

class Board
{
  constructor() //initializes various game-related variables, including the player array and the game state
  {
    startTime  = 0;
    this.playerArray = [];
    this.playerNum = 2;
    this.counter = 0;
    this.playerIndex = 0;
    this.encounter = false;
    this.gameState = "cover";
    for (let i = 0; i < this.playerNum; i++)
    {
      this.playerArray[i] = new Player();
    }
  }
  
  display() //displays different images depending on the game state, including the game board, cover page, instructions, and restart page.
  {
    if (this.gameState == "cover")
    {
      image(cover, 0, 0);
      if (mouseX >= 170 && mouseX <= 405 && mouseY >= 385 && mouseY <= 460)
      {
        noStroke();
        fill(255, 0, 0, 100);
        rect(170, 385, 235, 80);
      }
      
      if (mouseX >= 20 && mouseX <= 580 && mouseY >= 490 && mouseY <= 570)
      {
        noStroke();
        fill(255, 0, 0, 100);
        rect(20, 488, 560, 83);
      }
    }
    else if (this.gameState == "start")
    {
      image(boardImage, 0, 0);
      for (let i = 0; i < this.playerNum; i++)
      {
        this.playerArray[i].display();
      }
    }
    else if (this.gameState == "instructions")
    {
      image(instructions, 0, 0);
      image(back, 10, 10);
    }
    else if (this.gameState == "restart")
    {
      image(restart, 0, 0);
    }
  }
  //called whenever it is the player's turn to move, and it moves the player based on the number they rolled on the dice
  playing()
  {
    this.playerIndex = this.counter % this.playerNum;
    this.playerArray[this.playerIndex].movement(getDiceRoll());
    let newPos = this.checkForSnakeAndLadder(this.playerArray[this.playerIndex].player_position);
    let oldPos = this.playerArray[this.playerIndex].player_position;
    
    let movingPos = newPos - oldPos;
    
    this.playerArray[this.playerIndex].movement(movingPos);
      
    this.counter = this.counter + 1;
    this.checkForWin();
  }
  //method checks whether any player has reached the end of the board and sets the game state to restart if so.
  checkForWin()
  {
    for (let i = 0; i < this.playerNum; i++)
    {
      if (this.playerArray[i].player_position > 99)
      {
        this.gameState = "restart";
      }
    }
  }
  //checks whether the player has landed on a snake or ladder and returns the new position accordingly
  checkForSnakeAndLadder(position)
  {
    for (let ladder of ladders)
    {
      if (ladder.start == position)
      {
        startTimeLadder = millis();
        return ladder.end;
      }
    }
    
    for (let snake of snakes)
    {
      if (snake.start == position)
      {
        startTimeSnake = millis();
        return snake.end;
      }
    }
    
    return position;
  }
  
  snakeText()
  {
    textSize(30);
    fill(0, 0, 0);
    rect(200, 400, 200, 50);
    fill(255, 255, 255);
    text("SNAKE!", 240, 435);
  }
  
  ladderText()
  {
    textSize(30);
    fill(0, 0, 0);
    rect(200, 400, 200, 50);
    fill(255, 255, 255);
    text("LADDER", 240, 435);
  }
}

class Player
{
  constructor(player_color) //initializes the player's position and color, and the movement method moves the player on the board
  {
    this.player_position = 0;
    this.player_color = player_color;
    this.player_x = cellSize/2;
    this.player_y = cellSize/2;
    this.red = random(255);
    this.green = random(255);
    this.blue = random(255);
    this.opacity = 225;
  }
  
  movement(dice_roll)  // movement dependent on dice roll as an arguement
  {
    this.player_position = this.player_position + dice_roll;
    this.player_x = (this.player_position % cols) * cellSize + (cellSize/2);
    this.player_y = floor(this.player_position / cols) * cellSize + (cellSize/2);
  }
  
  display() //displays the player's avatar on the board.
  {
    fill(this.red, this.green, this.blue, this.opacity);
    ellipse(this.player_x, this.player_y, cellSize/2, cellSize/2);
  }
}

let board = new Board();

// Use a flag to indicate if the mouse click event has been handled
let handledMouseEvent = false;

function mouseClicked() // handles mouse clicks during the game, allowing the player to roll the dice and move accordingly
{
  if (board.gameState == "start")
  {
    // Check if the event has already been handled
    if (!handledMouseEvent) {
      board.playing();
      handledMouseEvent = true;
    }
  }
  else if (board.gameState == "cover")
  {
    handledMouseEvent = true;
    if (mouseX >= 170 && mouseX <= 405 && mouseY >= 385 && mouseY <= 460)
    {
      board.gameState = "start";
    }
    if (mouseX >= 20 && mouseX <= 580 && mouseY >= 490 && mouseY <= 570)
    {
      handledMouseEvent = true;
      board.gameState = "instructions";
    }
    
  }
  else if (board.gameState == "instructions")
  {
    if (mouseX >= 30 && mouseX <= 90 && mouseY >= 30 && mouseY <= 90)
    {
      board.gameState = "cover";
    }
  }
 
}

function keyPressed() //allows the player to restart the game if they win.
{
  if (board.gameState == "restart")
  {
    board = new Board();
  }
}
remove();

 

Overview

The game begins with a cover page that offers the player two options: to start playing or to read the instructions. Once the player begins the game, they roll a dice to determine the number of moves they can make on the board. The board is a 10×10 grid that features both snakes and ladders in specific cells. If the player lands on a ladder’s bottom cell, they climb up to the ladder’s top cell. If they land on a snake’s head cell, they slide down to the snake’s tail cell. Before the game begins, the program initializes various constants, global variables, ladders, and snakes. It also preloads all the game assets, sets up the canvas, and defines several functions to handle mouse clicks, retrieve the dice roll value, display the corresponding dice image, and show different images based on the game state. The game employs a Board class, which initializes various game-related variables, such as the player array and the game state. The class offers methods to display different images based on the game state, update the player’s position on the board, and display text when the player lands on a snake or ladder cell.

The cover page is the first thing that the player sees, and it sets the tone for the entire game. The font and background theme of the snake design create a sense of excitement and anticipation, drawing the player into the game before it even begins. Creating the board game that you played as a child was a significant accomplishment that speaks to your creativity and resourcefulness. It likely required a lot of time and effort to design and produce, but the end result is something that you can be truly proud of.

Future Improvements

To add more interactive features to the game, there are a few different directions that could be explored. One option is to introduce different levels of difficulty, which could be achieved by changing the layout of the board or adding new challenges for the player to overcome. For example, some cells on the board could be marked as “obstacle” cells, where the player has to complete a mini-game or answer a trivia question to progress.

Another way to make the game more engaging is to introduce power-ups or special abilities that the player can use to gain an advantage. For instance, the player could collect “snake repellent” items that allow them to skip over snake cells or “ladder boosters” that let them climb higher on ladder cells. These power-ups could be randomly placed on the board or awarded to the player for completing certain challenges.

Moreover, bonus points or rewards could be given to the player for landing on specific cells on the board. For example, landing on a cell with a picture of a golden apple could give the player bonus points, while landing on a cell with a picture of a bomb could subtract points from their score. This would add an element of strategy to the game, as players would have to decide whether to take risks and aim for high-reward cells or play it safe and avoid the high-risk ones. In terms of graphics and user interface, there are many ways to enhance the visual appeal and immersion of the game. For example, the background could be animated to show a moving snake or ladders climbing up and down the sides of the board. The game could also have different themes or environments, such as a jungle or a castle, with corresponding graphics and sound effects. This for me was very hard to try implementing but I still tried and could not.

Midterm – Crack The Egg

Inspiration & Concept:

For my final project in “Introduction to Computer Science,” I spent nearly two weeks brainstorming ideas before deciding between two games: “Crack the Egg” (a new game) and “BrickBreaker,” which I used to play on my father’s BlackBerry phone. I ultimately chose to make “BrickBreaker,” but promised myself that I would someday create “Crack the Egg” as well. When I learned about the opportunity to make a game for “Introduction to Interactive Media,” I knew immediately that I wanted to create “Crack the Egg.” The inspiration for this game came from another childhood game I used to play called Piano Tiles. Instead of having randomly generated tiles one after the other, my game would continuously generate eggs of various health in a way that they are all equidistant to each other, vertically and horizontally. Piano Tiles looks like the following:

On the other hand, Crack the Egg looks like this:

In “Crack the Egg,” the player takes on the role of a worker in a factory that heavily relies on eggs for production. The worker’s job is to stand next to three conveyor belts and manually inspect the eggs to ensure quality. There are three types of eggs in the game: “normal,” “bad,” and “very bad.” The player must avoid cracking the “normal” eggs while single tapping to crack the “bad” eggs and double tapping to crack the “very bad” eggs. As the game progresses, the conveyor belts will increase in speed, making it increasingly difficult for the player to inspect the eggs and the probability of encountering a non-normal egg will increase as well. Every “normal” egg that passes through un-cracked adds to the player’s score along with every non-normal egg that is perfectly cracked. However, if the player cracks a “normal” egg or fails to crack a “bad” or “very bad” egg, they will lose one of their six lives. The player will tap the eggs using their mouse.

Working of Game:

Two classes have been created: the “Egg” class and the “Game” class. The “Egg” class contains information such as the egg’s dimensions, coordinates, randomly generated health, and “normal status.” It manages the eggs’ movement from the bottom to the top of the canvas using a global speed variable. Additionally, the class includes a function that verifies whether the egg was appropriately handled based on its health status. Furthermore, the egg class ensures that the egg image displayed corresponds to its health and normalcy status. Eggs with a health value greater than or equal to three are blue in color and have a positive normalcy status. Eggs with a health value of two (requiring two clicks) are yellow-red with flames, and those with a health value of one (requiring one click) are yellow with chicken legs. Both of these types of eggs have a negative normalcy status. Health zero corresponds to a normal egg yolk or a chick for non-normal eggs. The egg class also manages what occurs when a particular egg is clicked during the game, which involves reducing the egg’s health. The Egg class looks like this:

//Egg class includes all the functions for each individual egg
class Egg
{
  constructor(x, y, e_width, e_height, e_index, e_up)
  {
    //coordinates for the egg
    this.egg_x = x;
    this.egg_y = y;
    //quality of egg and a number greater than 3 would mean a normal egg
    this.egg_health = int(random(1, globalRandomness)); //1 because 0 is yolk or chicken
    //dimensions for the egg
    this.egg_height = e_height;
    this.egg_width = e_width;
    
    //index in array to see place the eggs behind the last element beneath the canvas
    this.index_in_array = e_index;
    
    this.up_distance = e_up; //vertical distance between two adjacent eggs
    
    this.normal = true; //contains boolean value to see if the egg is normal or not
    if (this.egg_health < 3) //any egg with less than 3 health means that it is not normal
    {
      this.normal = false;
    }
  }
  
  movement()
  {
    
      this.egg_y = this.egg_y - globalGameSpeed; //uses the globalGameSpeed to move the eggs. we use global because it keeps changing over time
  }
  
  error_detection() //detects if the egg has been treated right. this is called when the egg is above the canvas
  {
    if (this.egg_health < 3 && this.egg_health > 0 && this.normal == false) //if the egg that is not normal has not been fully cracked (i.e health < 0), then an error has been made
    {
      return true;
    }
    else if (this.egg_health == 0 && this.normal == true) //if the normal egg has been cracked accidentally
    {
      return true;
    }
    else
    {
      return false;
    }
  }
  
  display()
  {
    //normal eggs (blue coloured)
    if (this.egg_health >= 3)
    {
      normalEggPicture.resize(this.egg_width, this.egg_height);
      image(normalEggPicture, this.egg_x, this.egg_y);
    }
    //eggs that need to be tapped twice (firey egg)
    else if (this.egg_health == 2)
    {
      smallEggPicture.resize(this.egg_width, this.egg_height);
      image(smallEggPicture, this.egg_x, this.egg_y);
    }
    //eggs that need to be clicked once and has chicken legs out
    else if (this.egg_health == 1)
    {
      bigEggPicture.resize(this.egg_width, this.egg_height);
      image(bigEggPicture, this.egg_x, this.egg_y);
      
    }
    //if the egg is cracked and wasnt supposed to be cracked then the yolk
    else if (this.egg_health <= 0 && this.normal == true)
    {
      eggYolkPicture.resize(this.egg_width, this.egg_height);
      image(eggYolkPicture, this.egg_x, this.egg_y);
    } 
    else if (this.egg_health <= 0 && this.normal == false) //if the non normal eggs are fully cracked then the chicken
    {
      chickPicture.resize(this.egg_width, this.egg_height);
      image(chickPicture, this.egg_x, this.egg_y);
    }
    
    fill(0,0,0);
    rect(0, this.egg_y - this.up_distance/2, width, 4); //horizontal lines between eggs to create like a grid
  }
  
  egg_clicked() //if the egg is clicked
  {
    if (this.egg_health >= 3) //the egg is normal
    {
      this.egg_health = 0; //then the egg becomes yolk
    }
    else
    {
      this.egg_health = this.egg_health - 1; //otherwise for non-normal egg decrease health by 1
    }
  }
}

In contrast, the Game class is relatively more complex, containing the dimensions and coordinates of various buttons, such as the start, instruction, and back buttons, along with the score and player lives. The constructor incorporates three arrays, one for each conveyor belt, which append 15 eggs to each array while passing various arguments, including coordinates. Initially, 50 eggs were in each array, but this was reduced to 15 to speed up the game loop time. Four different game states are maintained in the game class, representing the menu screen, instruction screen, game start screen, and restart screen, which alternate between the screens. I am particularly proud of this implementation as it allowed me to navigate through the coding process smoothly and transition between screens seamlessly. The eggs rise above the canvas, are checked for proper handling, and their coordinates are changed to be beneath the last egg in the line, instead of popping them every time and adding new objects, which proved to be a much faster and efficient method. The game keeps track of the score, compares it to the high score for the session, and ends when all the lives are lost. The player can restart the game by pressing the Enter key after the game ends. The Game class looks like this:

//game class contains everything the game needs
class Game
{
  constructor()
  {
    //reinitialize these variables here just in case a user decides to restart game without pressing play
    globalGameSpeed = 2;
    globalRandomness = 10;
    
    //canvas dimensions 
    this.canvas_width = 300;
    this.canvas_height = 600;
    
    //start button on menu - coordinates
    this.start_button_x = this.canvas_width/6;
    this.start_button_y = 4.5 * (this.canvas_height/6);
    
    //instruction button on menu - coordinates
    this.instruction_button_x = this.canvas_width/6;
    this.instruction_button_y = 5* (this.canvas_height/6);
    
    //dimensions for the start and instruction buttons to be used for mouseClicked interactivity
    this.button_width = 2*300/3;
    this.button_height = 30;
    this.button_arc = 20;
    
    
    this.score = 0; //will keep the score of all the good eggs that have successfully passed, and bad eggs that have been successfully cracked
    this.player_lives = 6; //will keep track of the lives for the player and will be displayed as well
    
    //dimensions of the eggs
    this.egg_width = 75;
    this.egg_height = 100;
    this.side_difference = 12.5; //difference of eggs from each other on the side
    this.up_difference = 25; //difference of eggs from each other from the top
    
    this.num_eggs = 15; //total number of eggs in each array kept 15 to allow for quicker loops
    //the three arrays will contain objects of egg class and will each represent a different conveyer belt
    this.egg_array3 = [];
    this.egg_array2 = [];
    this.egg_array = [];
    
    //this loop creates objects of Egg() class and assigns them their position and values
    for (let i = 0; i < this.num_eggs; i++)
    {
      this.egg_array[i] = new Egg(this.canvas_width - this.side_difference - this.egg_width, this.canvas_height + i*(this.egg_height + this.up_difference), this.egg_width, this.egg_height, i, this.up_difference);
      
      
      this.egg_array2[i] = new Egg(this.canvas_width - (3 * this.side_difference) - (2 * this.egg_width), this.canvas_height + i*(this.egg_height + this.up_difference), this.egg_width, this.egg_height, i, this.up_difference);
      
      this.egg_array3[i] = new Egg(this.canvas_width - (5 * this.side_difference) - (3 * this.egg_width), this.canvas_height + i*(this.egg_height + this.up_difference), this.egg_width, this.egg_height, i, this.up_difference);
      
    }
    
    //controls what is shown to the user at a given time
    this.menu_screen = true;
    this.start_screen = false;
    this.instruction_screen = false;
    this.restart_screen = false;
    
    //dimensions and details for the back button that will be present in the instruction screen
    this.back_button_x = 20;
    this.back_button_y = 20;
    this.back_button_width = 75;
    this.back_button_height = 35; 
  }
  
  //this will control the movement of three belts/arrays simultaneously
  belt_movement()
  {
    
    for (let i = 0; i < this.num_eggs; i++)
    {
      //each indidivual movement of an egg
      this.egg_array[i].movement();
      this.egg_array2[i].movement();
      this.egg_array3[i].movement();
      
      if (this.egg_array[i].egg_y + this.egg_array[i].egg_height <= 0) //if the egg goes above the canvas
      {
        
        if (this.egg_array[i].error_detection() == true) //we see if the egg has been rightfully treated or not
        {
          this.player_lives = this.player_lives - 1; //if there is an error detection, this will decrease the life of the player
        }
        else //if it has been rightfully treated
        {
          this.score = this.score + 1; //it will increase the score of the player
          score_count = score_count + 1; //score_count will also be increased to check if the game needs to be made harder. this is reinitialized in the draw function
        }
        this.egg_array[i].egg_y = this.egg_array[this.num_eggs - 1].egg_y + ((i+1) * (this.egg_height + this.up_difference)); //the egg that has gone above the canvas comes and attaches itself under the last element in the array using the index but its own index will remain the same
        

        this.egg_array[i].egg_health = int(random(1,globalRandomness)); //change the health and the quality of the egg once again to another random value
        
      if (this.egg_array[i].egg_health < 3) //also change the normal or not normal status for the egg. any random number above 3 is for eggs that are normal
        {
          this.egg_array[i].normal = false;
        }
        else
        {
          this.egg_array[i].normal = true;
        }
        
      }
      
      //do the same for egg_array2 and egg_array3
      if (this.egg_array2[i].egg_y + this.egg_array2[i].egg_height <= 0)
      {
        if (this.egg_array2[i].error_detection() == true)
        {
          this.player_lives = this.player_lives - 1;
        }
        else
        {
          this.score = this.score + 1;
          score_count = score_count + 1;
        }
        
        this.egg_array2[i].egg_y = this.egg_array2[this.num_eggs - 1].egg_y + ((i+1) * (this.egg_height + this.up_difference));
        this.egg_array2[i].egg_health = int(random(1,globalRandomness));
        
        if (this.egg_array2[i].egg_health < 3)
        {
          this.egg_array2[i].normal = false;
        }
        else
        {
          this.egg_array2[i].normal = true;
        }
      }
      
      if (this.egg_array3[i].egg_y + this.egg_array3[i].egg_height <= 0)
      {
        if (this.egg_array3[i].error_detection() == true)
        {
          this.player_lives = this.player_lives - 1;
        }
        else
        {
          this.score = this.score + 1;
          score_count = score_count + 1;
        }
        
        this.egg_array3[i].egg_y = this.egg_array3[this.num_eggs - 1].egg_y + ((i+1) * (this.egg_height + this.up_difference));
        this.egg_array3[i].egg_health = int(random(1,globalRandomness));
        if (this.egg_array3[i].egg_health < 3)
        {
          this.egg_array3[i].normal = false;
        }
        else
        {
          this.egg_array3[i].normal = true;
        }
      }
      
      //displays all the eggs with appropraite images
      this.egg_array[i].display();
      this.egg_array2[i].display();
      this.egg_array3[i].display();
    }
  }
  
  //displays different types of screens to the user
  screenDisplay()
  {
    if (this.menu_screen == true) //if the starting menu is displayed
    {
      image(startmenu_display_picture, 0,0); //display the picture with the logo
      fill("#541675"); //box color
      noStroke();
      //boxes for the start and instruction buttons
      rect(this.start_button_x, this.start_button_y, this.button_width, this.button_height, this.button_arc); //boxes to act as buttons
      rect(this.instruction_button_x, this.instruction_button_y, this.button_width, this.button_height, this.button_arc);
      //if the mouse hovers over the start button
      if (mouseX >= this.start_button_x && mouseX <= this.start_button_x + this.button_width && mouseY >= this.start_button_y && mouseY <= this.start_button_y + this.button_height) //if the mouse hovers over the button
      {
        fill("green"); //turn the text green
      }
      else
      {
        fill("gold"); //otherwise the text will be gold
      }
      //write START in the button
      textSize(20);
      text("START", this.start_button_x + ((this.button_width)/3.2), this.start_button_y + (this.button_height/1.3));
      //mouse hovering feature for the instruction button
    if (mouseX >= this.instruction_button_x && mouseX <= this.instruction_button_x + this.button_width && mouseY >= this.instruction_button_y && mouseY <= this.instruction_button_y + this.button_height)
    {
      fill("green");
    }
    else
    {
      fill("gold");
    }
      text("INSTRUCTIONS", this.instruction_button_x + ((this.button_width)/11), this.instruction_button_y + (this.button_height/1.3));
   }
    //if the start button is clicked
    else if (this.start_screen == true)
    {
      if (this.player_lives > 0) //the game hasnt ended
      {
        this.belt_movement();
      }
      else //the game has ended
      {
        this.restart_screen = true; //restart screen would be displayed in the next iteration
        this.start_screen = false; //current game state would be changed
      }
    }
    //if the instruction button is clicked then this if statement would run to show the instruction screen
    else if (this.instruction_screen == true)
    { 
      image(instruction_display_picture, 0 ,0); //image will all the instructions
      
      //Image for backbutton is resized to fit the desired dimensions and then added at the appropriate location in the instruction screen
      back_button.resize(this.back_button_width, this.back_button_height);
      image(back_button, this.back_button_x, this.back_button_y);
    }
    else if (this.restart_screen == true) //the game has ended
      {
        if (this.score > highscore)
        {
          highscore = this.score; //update highscore if needed
        }
        image(simple_game_over, 0, 0); //display the already edited restart screen
        fill(0, 0, 0);
        textSize(17);
        text("YOUR SCORE: ", 72, 347); //display score
        text(this.score, 192, 347);
        text("HIGH SCORE: ", 72, 425); //display high score
        text(highscore, 192, 424);
      }
  }
}

After all of this was done, the final product looks like this:
Full Screen: https://editor.p5js.org/mt4610/full/XgShKTIyL

Problems – Reflections:

While developing this game, I encountered several issues. Initially, I faced problems with using the “random” function in classes despite having used it before. It took me about 1.5 days, with the assistance of a friend, to come up with a solution. I added “new P5()” and “remove” at the beginning and end of the code, respectively, which enabled the program to run again. I also struggled with organizing the eggs in a loop and encountered issues such as overlapping and unequal distances. I decided to take a break, rewrite the code from scratch, and found a new solution. Rather than moving the eggs to their initial position, I looped them back to the end. Another issue I faced was with the restart game feature, which I spent hours trying to fix. I realized the next day that I had a typing error, which prevented the game from restarting. Although most issues were resolved, I was unable to find a way to save the highscore indefinitely. I managed to read from the file, but overwriting the high score was challenging. Every time I tried to save the score, a new file was created and downloaded to the user’s PC. As a result, I decided to create a high score feature for the current session and leave the indefinite high score for the future. I also had to balance the game’s speed increase and the probability of finding bad eggs to ensure that the game was neither too easy nor too difficult. In terms of creativity, I chose not to include sounds for tapping or cracking eggs, as they became repetitive/annoying over time. In the future, I plan to add extra features such as extra lives and slowing down the speed for a set period. Additionally, I aim to enhance the game’s aesthetics.

Midterm Project – Memory Card Game

Concept:

Introducing my latest creation – a memory card game with a unique twist! As one of the most popular games enjoyed by people of all ages in various forms, I wanted to put my own spin on it by incorporating a theme that I feel passionately about – food. The result is a game that is both fun and engaging and adds a personal touch that sets it apart from other versions.

I aim to showcase Pakistani Cuisine through this game, which shares similarities with cuisines from neighboring countries such as Nepal, India, and Sri Lanka. The increasing globalization has led to a rise in the popularity and recognition of desi food. Through this memory game, I hope to introduce players to lesser-known dishes that they may not have come across before. As a result, the unique names of these dishes will stay with them for a long time.

Final Outcome:

Game Code:

Following is the code for the game:

let numMatches = 0; //to see if all matches found and game ends
let numTries = 0; //counter to keep track of counts
let delayStartFC;
//10 card image variables
let card1;
let card2 ;
let card3;
let card4;
let card5;
let card6;
let card7;
let card8;
let card9;
let card10;
//array variables for image of cards and tiles etc
let faces = [];
let flippedTiles = []; 
let tiles = [];
let selected = [];
let possibleFaces;
let startgame;
//variables for images and sounds
let backgroundImage;
let instructionsImage;
let endImage;
let mymusic;
let flipsound;
let clicksound;
let flipSide;
let gameState; //to enter different stages: instructions, game, end
let NUM_COLS = 4;
let NUM_ROWS = 5;
function preload() 
{
  //loading all images and sounds
  flipSide = loadImage("back.jpg");
  card1 = loadImage("card1.jpg");
  card2 = loadImage("card2.jpg");
  card3 = loadImage("card3.jpg");
  card4 = loadImage("card4.jpg");
  card5 = loadImage("card5.jpg");
  card6 = loadImage("card6.jpg");
  card7 =  loadImage("card7.jpg");
  card8 = loadImage("card8.jpg");
  card9 =  loadImage("card9.jpg");
  card10 = loadImage("card10.jpg");
  startgame = loadImage ("startgame.png");
  backgroundImage = loadImage ("game.png");
  instructionsImage = loadImage("instructions.png")
  endImage = loadImage("end.jpg");
  mymusic = loadSound('Baisara-Beera-Kalpana-Patowary-Papon (mp3cut.net).mp3');
  flipsound = loadSound ('Card-flip-sound-effect-1.mp3');
  clicksound = loadSound('mixkit-game-click-1114.wav');
}

function setup() 
{
  // Set up canvas and other initialization tasks
  createCanvas(600, 700);
  gameState = 'start';
  mymusic.loop(); //stays on throughout
  frameRate(120);
}

class Tile 
{
     constructor(x, y, face) 
    {
      this.x = x;
      this.y = y;
      this.size = 100;
      this.face = face;
      this.isFaceUp = false;
      this.isMatch = false;
    }
    draw ()
    {
      //draws back if not face up otherwise the image of food
      if (this.isFaceUp)  
          image(this.face, this.x, this.y, this.size, this.size);
      else 
          image(flipSide, this.x, this.y, this.size, this.size);
      
      //black outline of the tiles
      stroke(0);
      strokeWeight(3);
      noFill();
      rect(this.x, this.y, this.size, this.size,5);
    }
  
    isUnderMouse(x,y)
    {
      //checks if when clicked the tile is under the mouse
        return x >= this.x && x <= this.x + this.size  && y >= this.y && y <= this.y + this.size;
    }
}
//function to randomly shuffle the array of tiles with 2 copies each
function shuffleArray(array) 
{
    let counter = array.length;
    // while there are elements in the array
    while (counter > 0) 
    {
        //selecting a random index
        let index = Math.floor(Math.random() * counter);
        // decrementing counter
        counter--;
        //swap last element with it
        var temp = array[counter];
        array[counter] = array[index];
        array[index] = temp;
    }
}
function draw() 
{
  if (gameState == 'start')
  {  
        //displaying image for background of game
        image(startgame, 0,0,600,700); 
        //start button
        let c = color('#FF69B4'); 
        fill(c);
        noStroke();
        circle(302.5, 350, 112);
        fill('black');
        textSize(20);
        textStyle(BOLD);
        text('Start', 280, 355);
  } 
  else if (gameState == 'instructions')
  {
    //instruction page is an image I made from canva
    image(instructionsImage,0,0,600,700);
  }
  else if (gameState == 'beingSet')
  {
    //this is just done once when the start button is clicked 
    faces = [card1, card2, card3, 
             card4, card5, card6, 
             card7, card8, card9, 
             card10]; //storing all images into array
    // making an array of 2 cards and then randomising
    possibleFaces = faces.slice(0);
    for (let i = 0; i < (NUM_COLS * NUM_ROWS) / 2; i++) 
    {
        // Randomly pick one from the array of remaining faces
        var randomInd = floor(random(possibleFaces.length));
        var face = possibleFaces[randomInd];
        // Push twice onto array
        selected.push(face);
        selected.push(face);
        // Remove from array
        possibleFaces.splice(randomInd, 1);
    }
    shuffleArray(selected); //shuffling the array
    
    // Creating a Tile object for each card
    for (var i = 0; i < NUM_COLS; i++) 
    {
        for (var j = 0; j < NUM_ROWS; j++) 
        {
            var tileX = (i * 100) + 55+ (i*30);
            var tileY = (j * 100) + 70 +(j*17);
            var tileFace = selected.pop();
            tiles.push(new Tile(tileX, tileY, tileFace));
        }
    }
        // Changing the game state to 'playing'
    gameState = 'playing';
  }
  else if (gameState == 'playing') 
  {
    // Displaying the background image
    image(backgroundImage, 0,0,600,700);
    // Checking if enough time has passed since the last card flip
    if (delayStartFC && (frameCount - delayStartFC) > 25) 
    {
        // Hiding the flipped cards that are not matches
        for (let i = 0; i < tiles.length; i++) 
        {
          if (!tiles[i].isMatch) 
              tiles[i].isFaceUp = false;
        }
// Resetting the flippedTiles array and delayStartFC variable
        flippedTiles = [];
        delayStartFC = null;
     }
    
    // Drawing each tile on the canvas
    for (let j = 0; j < tiles.length; j++) 
        tiles[j].draw();
   
    // Checking if the game is over
    if (numMatches == tiles.length/2)
        gameState = 'end';
} 
  else if (gameState == 'end')
  {
    image(endImage, 0,0,600,700);
    let c = color('#d90166');
    fill(c);
    noStroke();
    //play again button
    circle(302.5, 350, 112);
    fill('black');
    textSize(15);
    textStyle(BOLD);
    text('Play Again', 266, 355);
    textSize(20);
    //diplaying the tries as a score for game
    text("You found them all in " + numTries + " tries!", 146,488);
    } 
  
mouseClicked = function() 
{ 
  if(gameState == 'start')
    {
      //checking ditance between click and start button 
      let distance = dist(mouseX, mouseY, 302.5, 350);
      if (distance < 112/2) 
        {
          //play click sound
          clicksound.play();
           gameState = 'beingSet';
        }
    // checking distance between click and instructions button
      if (mouseX>220 && mouseX<380 && mouseY>500 && mouseY<540)
        {
          clicksound.play();
          gameState = 'instructions';
        }
    }
  else if (gameState == 'playing')
  {    
    //loop through all the tiles
    for (let i = 0; i < tiles.length; i++) 
      {
          let tile = tiles[i];
          if (tile.isUnderMouse(mouseX, mouseY)) 
          {
  // Check if the tile can be flipped (only if less than 2 tiles are flipped and the tile is not already face-up)
              if (flippedTiles.length < 2 && !tile.isFaceUp) 
              {
                  tile.isFaceUp = true;
                  flipsound.play();
                  flippedTiles.push(tile); //adds to flippedtiles array
                  if (flippedTiles.length === 2) 
                  {
                      numTries++; //increments the tries counter
                      if (flippedTiles[0].face === flippedTiles[1].face) //if they match
                      {
                          flippedTiles[0].isMatch = true;
                          flippedTiles[1].isMatch = true;
                          flippedTiles.length = 0; //empties flipped array because match found
                          numMatches++;
                      }
                      delayStartFC = frameCount;
                  }
              } 
          }
      }
  }
  else if (gameState == 'instructions')
    {
      //clicking the startgame button
      if (mouseX > 149 && mouseX < 449 &&
      mouseY > 600 && mouseY < 615)
        {
          clicksound.play();
          gameState = 'beingSet';
        }
    }
  else if (gameState == 'end')
    {
      //clicking restart button 
      let distance = dist(mouseX, mouseY, 302.5, 350);
      if (distance < 112/2) 
        {
          clicksound.play();
         gameState = 'start';
        }
    }
};
}

Process:

Initially, I developed a tile class and successfully displayed the tiles on the screen using the draw function in my program. Although the game was functional, I encountered challenges when attempting to change the game state from the starting page to the playing and end pages. To overcome this hurdle, I referred to the coding examples provided by Professor Mang for changing the game state. However, I had to modify my code to make it compatible with the “if-else” loops used to change the game state. I eventually decided to categorize my game states further and added a new category called “beingSet”, which is only called once before the game begins.

During my coding process, I encountered another obstacle while creating cards for my game using Canva. Initially, the cards included an image, the name of the dish, and a brief description. However, due to the limited dimensions of my game’s canvas, the description on the cards was difficult to read, and it also compromised the visibility of the dish’s name. As a result, I had to recreate all the cards without the description. Despite my efforts, I was disappointed that I could not include the descriptions on the cards.

previously made cards:

final card: 

I just solved another issue in my game a few minutes ago by adding a single line of code:

frameRate(120);

This line of code significantly improved the performance of my game. Previously, as the game progressed towards the end, it would slow down. However, with the addition of this line of code, my game’s speed increased, making it more challenging and an actual test of memory for the players. Nevertheless, I need to investigate why the cards take longer to flip over towards the end, despite the game starting at a normal speed.

Future Improvements:

While I am immensely proud of my game, there is always room for improvement. One area that could be enhanced is by adding a menu with the description of every dish. This could be introduced as an introductory section, where players click on a button and are taken to a menu with forward and back buttons to navigate through an array of images. This would give players the opportunity to familiarize themselves with the dishes, the details of the dish, and the corresponding images before they start playing. This would not only enhance the players’ overall gaming experience but also provide a fun and interactive way to learn about Pakistani cuisine.

Midterm Project – Space Shooter

Below is the game I have created, a retro styled classic: Space Shooter!

and below is the code for this game:

//Declaring all variables to be used
let bullets = [];
let enemies = [];
let stars = [];
let powerups = [];
let gamestate;
let posterImage;
let fighter;
let player;
let laser;
let enemy;
let score = 0;
let title_screen_audio;
let playing_screen_audio;
let end_screen_audio;
let laser_sound_effect;
let enemySpawnRate =1;
let timeElapsed = 0;


function preload() {
  posterImage = loadImage('SpaceShooterBackground.png'); //Loading main menu
  fighter = loadImage('NicePng_spaceship-png_138961.png'); //Loading spaceship
  laser = loadImage('LaserBullet.png'); //Loading laser bullet
  enemy = loadImage('invader.png'); //Loading enemies
  font = loadFont('GameOverFont.ttf'); //Loading Game Over Screen Font
  title_screen_audio = loadSound("SkyFire (Title Screen).mp3");
  playing_screen_audio = loadSound("Battle in the Stars.mp3"); //Loading menu music
  end_screen_audio = loadSound("gameEndMusic.mp3");  //Load Game Over Screen Music
  laser_sound_effect = loadSound("lasershot.wav"); //Load laser sound effect
}

function setup() {
  createCanvas(400, 600);
  gamestate = 'menu'; //Setting gamestate menu
  player = new Spaceship(67, 61); //Initializing Sapceship class as the player

  for (let i = 0; i < 100; i++) {
    stars[i] = new Star(random(width), random(height), random(1, 5)); //Randomly generating stars
  }
}

function draw() {
  if (gamestate == 'menu') {
    image(posterImage, 0, 0, width, height); //Displaying menu screen if gamestate is menu
    if (!title_screen_audio.isPlaying())
      {
        title_screen_audio.play(); //Plays menu music
      }
  }
  if (gamestate == 'playing') {
    title_screen_audio.stop(); //Stops menu music when gamestate is playing
    if (!playing_screen_audio.isPlaying())
      {
        playing_screen_audio.play(); //Plays battle music
      }
    background(1, 22, 64);
    player.show();

    for (let star of stars) {
      star.show(); //Displaying stars
    }

    // Add enemies randomly
    if (frameCount % (60 / enemySpawnRate) == 0) {
      let enemy = new Enemy(random(width-50), -50, random(1, 4));
      enemies.push(enemy);
    }

    for (let i = 0; i < bullets.length; i++) {
      bullets[i].update(); //Adding velocity to bullets
      bullets[i].show(); //Displaying bullets
      for (let j = 0; j < enemies.length; j++) {
        let enemyHitbox = enemies[j].getHitbox(); //Initializing enemies with hitbox
        if (bullets[i].hits(enemyHitbox)) {
          bullets.splice(i, 1); //Remove bullets when it hits an enemy
          enemies[j].hits(); //Registers hit to enemy
          score += 1; //Incremements score on hit
          enemies.splice(j, 1); // remove the enemy object from the array
          break;
        }
      }
    }

    for (let i = 0; i < enemies.length; i++) {
      enemies[i].update(); //Makes enemies fall
      enemies[i].show(); //Displays enemies
    }

    let anyEnemyReachedBottom = false; // flag to indicate if any enemy has     reached the bottom
    for (let i = 0; i < enemies.length; i++) {
      if (enemies[i].reachedBottomFlag) {
        anyEnemyReachedBottom = true; //Turns true when enemy reaches bottom
        break;
      }
    }

    if (anyEnemyReachedBottom) {
      gamestate = 'gameover'; //Sets gamestate to gameover once enemy reaches the bottom
    }

    textSize(20);
    strokeWeight(1);
    textFont(font);
    fill(255);
    text("Score: " + score, 10, 30); //Displays score at top left
  }

  if (gamestate == 'gameover') {
    playing_screen_audio.stop(); //Stops battle music
    if (!end_screen_audio.isPlaying())
      {
        end_screen_audio.play(); //Plays defeat music
      }
    background(1, 22, 64);
    for (let star of stars) {
      star.show(); //Displays stars
    }
    textSize(30);
    strokeWeight(1);
    fill(255);
    textFont(font);
    text("Game Over", width / 2 - 80, height / 2 - 20);
    text("Score: " + score, width / 2 - 65, height / 2 + 20);
    text("Press Space to retry!", width / 2 - 150, height / 2 + 60);
  }
  
  timeElapsed += deltaTime / 1000;
  if (timeElapsed >= 40) { // increase spawn rate every 40 seconds
    enemySpawnRate++;
    timeElapsed = 0;
  }
  
}
function mouseClicked() {
  if (gamestate == 'menu') {
    gamestate = 'playing'; //Changes gameststate on mouseclick
  }
  if (gamestate == 'playing') {
    let bullet = new Bullet(mouseX + 3, height - 20, 10);
    bullets.push(bullet); //Fires bullet on every click
    laser_sound_effect.play(); //Plays laser sound on every click
  }
}

function keyPressed(){
  if (key == ' ' && gamestate == 'gameover'){
    score = 0;
    bullets = [];
    enemies = [];
    restartGame();
    end_screen_audio.stop(); //Restarts game by pressing space on game over screen
  }
}

function restartGame() {
  gamestate = 'playing';
  enemySpawnRate = 1;
}

class Bullet {
  //Setting variables
  constructor(x, y, velocity) {
    this.x = x;
    this.y = y;
    this.velocity = velocity;
    this.size = 20; 
  }

  update() {
    this.y -= this.velocity; //Fires bullet upward
  }

  show() {
    image(laser, this.x - this.size/2, this.y - this.size/2, this.size, this.size); //Laser image
  }

  hits(hitbox) {
    let d = dist(this.x, this.y, hitbox.x + hitbox.width/2, hitbox.y + hitbox.height/2);
    if (d < (this.size + hitbox.width)/2) {
      return true; //Hitbox registration
    } else {
      return false;
    }
  }
}

class Spaceship {
  constructor(x,y) {
    this.x = x;
    this.y = y; // Setting variables
  }
  show(){
    //Prevents fighter from going out of bounds
    if (mouseX - 30 < 0)
      {
        image(fighter,0,height-50,this.x,this.y);
      }
    else if (mouseX - 30 > width)
      {
        image(fighter,width - this.x,height-50,this.x,this.y);
      }
    else
      {
        image(fighter,mouseX-30,height-50,this.x,this.y);
      }
    
    
  }
}
//Creates stars in background
class Star {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size;
  }

  show() {
    stroke(255);
    strokeWeight(this.size);
    point(this.x, this.y);
  }
}

class Enemy {
  constructor(x, y, velocity) {
    this.x = x;
    this.y = y;
    this.velocity = velocity;
    this.size = 30;
    this.reachedBottomFlag = false; // flag to indicate if this enemy has reached the bottom
  }

  update() {
    this.y += this.velocity;
    if (this.y >= height) {
      this.reachedBottomFlag = true; //Detects if enemy has reached bottom
    }
  }

  show() {
    image(enemy, this.x, this.y, this.size, this.size); //Creating enemies images
  }

  hits() {
    this.velocity = 0; //Sets velocity to 0 on hit
  }

  getHitbox() {
    return {
      x: this.x,
      y: this.y,
      width: this.size,
      height: this.size //Creates hitbox of enemies
    }
  }
} 

I was inspired by the many retro classic games. These games bring a long a sense of nostalgia that no other games bring. Since I am an avid space enjoyer and all things sci-fi, I decided to combine these 2 concepts into the game Space Shooter. The story is you are a space fighter defending the human race from these purple invaders! Your mission is to prevent these invaders to breaking through into your back line.

Behind the scenes, I used Canva in order to create the opening menu of this game. I then created separate classes for the player’s spaceship, enemies, bullets, and stars. I tied the spaceship position and laser firing position to the mouseX coordinate while invaders fall between a random set of x values. I essentially created a hitbox for the enemies so the laser can register whether it hits an enemy or not. I also made it so that it tracks how long the game goes on and after every 30 seconds, the spawn rate of the enemies increasing making the game get progressively difficult. Just for some extra personalization, I made it so the stars in the background are randomly generated so there is always something that is a tiny bit new in every playthrough.

I am especially proud of making the hitbox method in order to deal with collision. I initially struggled with collision but after researching and messing around, I managed to finally solve it and in quite the efficient way I like to think. I’m also proud of the laser bullet and invader sprites. I managed to find a good vector sprite for the spaceship but importing different sized bullets and invaders could be messy. So, I made them on my own using online software allowing me to personalize them into whatever size or shape I need. This further simplified my work and going forward and gave it a bit more of my own touch and creativity into this project.

I still feel as there are many more improvements that can be made into this. For starters, I could add other enemy types where they require multiple hits in order to be destroyed. I can also add powerups where the game could be slowed for example. I was also told about adding a pause screen which I initially thought wouldn’t fit as it is a retro game where pause menus didn’t exist but still could be added as a quality-of-life improvement.