Midterm Project

Concept

As part of my Mid term project, the idea was to create a game that is similar to the traditional ‘space-ship’ games, combined with the ‘infinite-runner’ game ideology. This would essentially be a canvas, where a rocket starts from the bottom, has freedom to move around, with the background moving, depicting a running environment. The objective would be for the rocket to dodge some meteors falling from the sky, and also to fuel up with cans that become available in intervals.

The graphical objects that I used are:

How it functions?

The outcome, from the above mentioned ideology, is a single-player ‘Rocket Rage’ game, which starts off with the a screen to allow a user to choose the difficulty, and also lay out instructions. On top of this, it also keeps track of the Top Score so far (on the local machine), in both difficulty modes!

The following snippet shows how it will look on the canvas:

Once the user chooses their difficulty, they are progressed to the next phase where they can control the Rocket. The objective is to dodge everything that comes, except the Fuel. There is a running score, and decreasing fuel with only one chance to go!

How that would look like can be seen as follows:

Embedded Sketch:

The p5js final sketch is as follows:

Link to Sketch: https://editor.p5js.org/mzv205/full/ZaD5RILaw

Challenges:

Something I really am proud of are the usage of the rotational ability of moving the rocket at angles, it was particularly challenging to figure out the logic for this. Moreover, I also learnt the usage of local storage, so that was another challenge initially.

Moreover, the appropriate usage and updating of boolean state variables was very challenging to keep track of. Pausing with the mouse click, and making sure the game has started for it to be paused. There were state variables used even for the first Menu Screen as well. This was something that I found to be a hassle to deal with!

Code Snippets:

For Rotating the Rocket:

// ROTATING THE ROCKET
// store the current drawing matrix
push()
// move the origin to where the rocket is
translate(rocketX, rocketY);
// rotate using our angle variable
rotate(radians(angle) )
imageMode(CENTER)
image(rocket,0, 0, 80, 75.6)
// restore the previous drawing matrix
pop()

Code for the Asteroids class:

class Asteroid {

  constructor(){
    // Assigning random position and speed to the asteroid
    this.x = random(60,590);
    this.speed = random(2,5)
    this.size = random(30,90);
    this.y = 5+ this.size/2;
    // Assigning random image to the asteroid
    this.pic = random([asteroid_1, asteroid_2, fireball_1])
    fill(255);
  }

  display(){
    // Displaying the asteroid
    imageMode(CENTER);
    image(this.pic, this.x, this.y, this.size, this.size);
    imageMode(CORNER);

    // Setting the boundaries of each Asteroid

    this.Top = this.y-this.size/2
    this.Bottom = this.y+this.size/2
    this.Right = this.x+this.size/2
    this.Left = this.x-this.size/2+5

    if(this.pic == asteroid_2){
      this.Top = this.y-this.size/3+15
      this.Bottom = this.y+this.size/3
      this.Right = this.x+this.size/3
      this.Left = this.x-this.size/2
    }


    if(this.pic == fireball_1){
      this.Top = this.y-this.size/3+15
      this.Bottom = this.y+this.size/3
      this.Right = this.x+this.size/3-5
      this.Left = this.x-this.size/3+5
    }
  }

  move(){
    // Moving thr asteroid
    this.y += this.speed;
  }

  collision(){
    // Detecting if no collision between rocket and asteroid
    if ((rocketTop > this.Bottom || rocketLeft > this.Right || rocketRight < this.Left || rocketBottom < this.Top)){
      collision = false;
    }
    else{
      // Otherwise ending the game
      resetVariables();
    }
  }

}

 

Areas of Improvement:

I think something that can be improved is having the ability to unlock more levels, or even choose different rockets. Having done the minimum viable product for the game, I tried to build on the functionality of choosing the rocket and it seemed to be breaking the other state variables. Therefore, I think planning out the different states of the games and then coding accordingly is something I can take away from this!

Lovers Meet

Concept
For this project, I was inspired by a game called “Barbie bike styling ride,” so much so that I wanted to replicate it. So along with creating a game for my midterm project, I wanted to live those cute childhood memories and enjoy making this game. The game is themed behind the famous love story we have all heard about, Romeo and Juliet. So the task of the game is to help Romeo reach Juliet with the highest number of scores, crossing all the obstacles in his way to reach his love.

Code and project explanation
Coding and making this project required first to lay down the code’s structure and how I would like to manage the different screens of the game. At the start of the game’s making, I was trying to understand the most efficient way to structure code and organize the different images and the media being imported. I use the variable gameState to keep track of the different screens (the start screen, the game screen, the difficulty screen, the instructions screen, and the end screen) in the display. Then to structure everything more easily, I divided the code into different files, which include: “screens.js,” “player.js,” “obstacles.js.”, and “functions.js.” I am really proud of the button function, the arrangement of my code, the jumps of the character, and the scorekeeping.

the screen setup

function startScreen() {
  image(start_pg, 0, 0, width, height);
  button("Start", "#B667E6", 3*width/4-150, 3*height/4-50, 120, 30, 5 )
  button("Instructions", "#B667E6", 3*width/4-150, 3*height/4-10, 120, 30, 5 )
  button("Difficulty", "#B667E6", 3*width/4-150, 3*height/4+30, 120, 30, 5 )
}

function instructionScreen(){
  image(instruction_pg, 0,0, width,height);
  button("Return", "#B667E6", width-150, height-50, 120, 30, 5 );
}

function difficultyScreen(){
  image(difficulty_pg, 0,0, width,height);
  
  button("Return", "#B667E6", width-150, height-50, 120, 30, 5 );
  
  button("EASY", "#B667E6", 50, 150, 155, 100, 25 );
  button("MEDIUM", "#B667E6", 225, 150, 155, 100, 25 );
  button("HARD", "#B667E6", 400, 150, 155, 100, 15 );
}

function gameScreen() {
  // Draw game background image
  background(0);
  
  image(game_pg, v1, 0, width, height);
  image(game_pg, v2, 0, width, height);
  
  v1 -= game_speed;
  v2 -= game_speed;
  
  if(v1 < -width) { v1 = width;} 
  if(v2 < -width) { v2 = width;}
  
  if (random(1) < 0.005) {
    obs.push(new Obstacles(width, height - 50, 50, 50));
  }

  for (let ob of obs) {
    ob.move();
    ob.show();

    if (p.hits(ob)) {
      gameState = "endScreen"
    }
    else{
       score += 1;
    }
    
    if(ob.x +ob.w<0){
      obs.splice(ob)
    }
  }
  p.show();
  p.move();
  if (keyIsDown(32)) {
    p.jump();
  }
}

function endScreen(){
  image(end_pg, 0,0, width,height);
  button("Restart", "#B667E6", width-150, height-50, 120, 30, 5 );
  textSize(50); // set text size
  fill(255); // set text color
  text("SCORE:", 450, 220); 
  text(score, 450, 270); 
  
}

The player class

class Player {
    constructor() {
      this.r = 200; //size of the image
      this.x = 50;
      this.y = height - this.r;
      this.vy = 0; //velocity with which it goes up
      this.gravity = 1; //gravity with which it comes down
    }
  
    jump() {
      //jump only if its in the ground
      // if (this.y == height - this.r){
        this.vy = -25; //velocity with which it jumps
       // }
    }
  
    hits(obs) {
      let x1 = this.x + this.r * 0.5;
      let y1 = this.y + this.r * 0.5;
      let x2 = obs.x + obs.w * 0.5;
      let y2 = obs.y + obs.h * 0.5;
      return collideCircleCircle(x1, y1, this.r, x2, y2, obs.w);
    }
  
    move() {
      this.y += this.vy; //reduces the height inturn makes the player move upwards
      this.vy += this.gravity; //adding gravity so that it comes down
      this.y = constrain(this.y, 0, height - this.r); 
    }
  
    show() {
      image(player, this.x, this.y + 50, this.r, this.r);
    }
  }

 

Problems faced and Areas of improvement
One of the most crucial problems that I ran into was making to make the player jump. At first, I was using the keyPressed function, but then it wasn’t working because it was not getting referenced in the draw directly, so I used the keyIsDown to perform the same; this indeed is a minor error, but it took a lot of time to debug this down. Mostly I faced difficulties in the starting regarding the logistics of the game and how to build it, but once we had a structure built up in mind, then it was easy to build.

This project can be extended to make it more realistic with the player and the obstacle in a more 3D version. We could also add more features to the set up of the game with functions and buttons to mute the volume or have different levels.

Midterm Project Documentation(Hit the Island Game)

IDEA

For my midterm project, I decided to create a game out of the options from which we could choose any to create a project. A game that would use all the concepts learned in class so far. I got my inspiration from a game on the apple app store called “Hit the Island”. I always wondered how the game was created and how the developers got it to be interactive with the dynamic island on the iPhone 14 pro series. After taking this class, I have gained enough knowledge to know that the game doesn’t actually interact with the dynamic island but has a bar-shaped object behind the island with which the balls in the game interact.

ORIGINAL GAME

The original game below was developed by Funn Media, LLLC.

Code Structure:
The code is structured into several functions that are responsible for different parts of the game. The main functions are as follows:

createPaddles() – This function creates two paddles, one at the top of the canvas and the other at the bottom.

resetGame() – This function resets the game by clearing the balls array and calling createPaddles() to create new paddles.

setup() – This function initializes the game by creating a canvas, calling resetGame(), and loading images and sounds.

preload() – This function loads images and sounds before the game starts.

introScreen() – This function displays the intro screen with the game title.

endScreen() – This function displays the end screen with the player’s score.

gameplay() – This function handles the main gameplay loop by updating the ball’s position, checking for collisions with walls and paddles, and removing balls that fall off the canvas.

checkKeyPressed() – This function checks for keyboard input and moves the paddle left or right, and launches the ball.

Ball class – This class represents the ball in the game and handles its movement, collision detection, and rendering.

Paddle class – This class represents a paddle in the game and handles its movement, collision detection, and rendering.

How the game is played(functionality)
The game starts with the intro screen and waits for the player to press the “s” key to start the game. Once the game starts, the player can move the paddle left or right using the left and right arrow keys.  The ball bounces off the paddles and the walls of the canvas, and the player scores points by hitting the ball with the top paddle. The game ends when the player loses all their lives, and the end screen displays the player’s score. The player can restart the game by pressing the “p” key or return to the intro screen by pressing the “q” key.

GAME

 

CODE SNIPPETS

Main gameplay code

CHALLENGES AND FUTURE IMPROVEMENTS

As at now, the game has a lot of bugs and is still in the “beta testing” phase. There are numerous errors to be fixed including the update of score when the ball hits the top paddle, playing of ball sound when the ball hits the roof of the canvas, and the general collision between the ball and the paddles. In a next update, i intend to fix these errors and also add more levels, varying ball speed and increasing the number of balls when the user gets to a certain score.

 

 

Midterm Project – ‘Save the Ocean’ Game

Concept

‘Save the Ocean’ is an adaptation of the famous snakes game but with a new concept. It aims to raise awareness of the plastic pollution in our oceans around the world and with this intent in mind, I developed this game where the player controls a diver (instead of a snake) and uses arrow keys on the keyboard to move around the ocean – the canvas. The diver’s mission is to collect as much plastic waste as possible while avoiding obstacles – the monsters.

The Game

Fullscreen view here: https://editor.p5js.org/ramshabilal/full/uDXF9FUDA

Key elements of the game

Start Page

The game has a start page that shows the cover of the game along with a start button, which, when pressed moves onto the instructions page. Game music also begins playing on this page. An image I designed for the game is used as a background while the button is added using shapes and text.

Instructions Page

With an online image as a background, this page highlights the key information and instructions needed to understand how the game is to be played.  A button ‘Go’ when pressed takes the player to the main game page where the game is to be played.

Main Game Page

The main game page has:

  • The diver: using a segment class I made, the diver object can be moved around using keyboard navigations.
  • Monsters: Monsters are generated at random positions (coordinates saved in arrays) on the canvas and if the diver collides with them, 10 points are deducted from the player’s “Life” – game stops when Life gets to zero.
  • Plastic Waste: These are the green plastic bottles that the user has to collect to save the ocean. Whenever plastic is collected, 10 points are added to the user’s score. At the end of the game, the high score is updated – if the user exceeds the previous high score, they break their own record. This layer is added to compel users to play again.
  • Informational text: Score, high score, and life are displayed for the user to see.

Game over page

This is the page displayed when the game ends i.e. life = zero. Here, the score and high score with the corresponding message are displayed. If the user breaks their high score record, they are informed here as well. Music also changes on this page. The user can also press a button to go back to the start – the game is reset using a reset game function and the user can start again (without rerunning the code).

Difficulty

The difficulty of the game increases with time – as time passes, the game speeds up and the number of monsters in the ocean also increases.

Some of my design considerations

A color scheme was designed for the game – blue and purple were the dominant colors in this theme to create a coherent and complete look and experience for the user and to fit the “ocean” theme of the game.

Initially, this was my cover for the game:

However, to reveal more about what the game will be and to match the theme and colors, I changed it to this design I made:

Buttons changed color when the mouse hovered over them to indicate they need to be pressed.

The main music played in the game was chosen to match the ocean theme. Moreover, pop and coin sound effects were added which corresponded to the collisions with monster and plastic waste respectively.

A simple yet fun font was used uniformly throughout the game to create a coherent and fun look while keeping everything minimal and easy to follow.

Code I am proud of

As  I developed the game, unique challenges popped up that required creative solutions. Developing these solutions was the part I was most proud of as it required problem-solving.

One example is when I wanted to create more than one monster at a time as opposed to the one monster-at-a-time thing I had initially. For this, I created arrays to store random coordinates and loops to create the monsters.

function createMonsters(){
//for every monster, update its speed and if it collides with the walls, reverse their direction of motion
  for (let i = 0; i < numOfMonsters; i++){
    image(monsterImg, monsterXs[i], monsterYs[i], monsterWidth, monsterHeight);
    
    monsterXs[i] += monsterSpeeds[i];
  if (monsterXs[i] < 0 || monsterXs[i]+monsterWidth > width)
    {
      monsterSpeeds[i] = -monsterSpeeds[i];
    }
  }
}

Another challenge I faced was that when the user would play again, the conditions would not reset. For this, firstly i introduced game states to smoothly switch between different stages of the game and also created a reset page function to refresh the game to start conditions.

function stopPage(){
  background('rgb(100,229,255)');
  image(gameOverImg, 0,0,width,height);
  fill('white');
  textAlign(CENTER);
  textFont(scoreFont);
  textSize(24);
  if (score > highscore)
  {
    print('score is ' + score + ' highscore is ' + highscore);
    
    text('Congratulations. You helped free the ocean of harmful plastic and you just beat your previous highscore!', 0, height/2, width);
    fill('gold')
    text('New Highscore =  ' + score, 10, height*2/3, width);
  }
    else 
    { 
      fill('white');
       text('Congratulations. You helped free the ocean of harmful plastic!',10, height/2, width);
      
       text('Score =  ' + score + '\n\nHighscore = '+ highscore, 10, height*2/3, width);
    }
  fill('red')
  ellipse(width/2, 530,200,80);
  fill('white')
  text('Play Again', 295, 538);
  if (mouseX > width/2-100  && mouseX < width/2+100 && mouseY > 530-40 && mouseY < 530+40){
    fill('gold');
    ellipse(width/2, 530,200,80);
    fill('white')
    text('Play Again', 295, 538);
    if (mouseIsPressed){
      reset = true;
      gameOverSong.stop();
      gameState='start';
    }
  }
}

function resetGame(){
  gameOverSong.stop();
  song.loop();
  life =100;
  if (score > highscore){
    highscore = score;
  }
  score = 0;
  startPlaying=false;
  x1=0;
  x2=width;
  direction='start';
  newSeg.setX(width/2);
  newSeg.setY(height/2);
  
  for (i = numOfMonsters; i > 2;i--)
  {
    monsterSpeeds.splice(i, 1);
    monsterXs.splice(i, 1);
    monsterYs.splice(i, 1);
  }
  numOfMonsters = 3;
  step =3;
  
  for (i =0; i<numOfMonsters; i++)
    {
      monsterSpeeds[i] = random(-2,2);
      monsterXs[i] = random(0,width);
      monsterYs[i] = random(0,height);
    }
}

Also, while resetting, to go back to the original number of monsters, I splice their arrays keeping only some monsters and reset their speeds to the initial range (while keeping it random).

I also used other boolean flags to control the flow of the game and as a whole, I am very proud of the entire code – functions, class, etc – that I wrote.

//seg is class for the diver and step is the speed of the diver
let newSeg, segWidth = 60, step = 3;
let direction = 'start', gameState = 'start';

//booleans to store various game states/reset etc. startPlaying checks if game level has started, reset checks if the game is started again to reset the variables, playgameover stores if the game over song should be played or not
let startPlaying = false, reset = true, playgameover = true;

//for images and songs 
let img, diverImg, instrImg, bgImg, plasticImg, monsterImg, gameOverImg;
let song, gameOverSong, coinSong,powSong;
let cnv; 

//for scrolling background 
let x1 = 0, x2, scrollSpeed = 1;

//for the green plastic bottles 
let plasticX = 100, plasticY = 200, plasticWidth =70, plasticHeight = 70;

//to save randomly chosen x and y coordinates for the monsters
let monsterXs = [], monsterYs = [];
let monsterWidth =60, monsterHeight = 60; 

//initially num of monsters and array to store their randomly chosen speeds (within a range)
let numOfMonsters = 3, monsterSpeeds =[];
let highscore = 0, score = 0, life = 100;

//preload required images, songs, and fonts
function preload() {
  img = loadImage('images/1.png');
  song = loadSound('music.mp3');
  gameOverSong = loadSound('music/gameover.mp3');
  coinSong = loadSound('music/coin.mp3');
  powSong = loadSound('music/pow.mp3');
  bgImg = loadImage("images/game_bg.png");
  plasticImg = loadImage("images/plastic.png");
  monsterImg = loadImage("images/monster.png");
  gameOverImg = loadImage("images/gameOverImg.png");
  instrImg = loadImage("images/instructions.jpg");
  diverImg = loadImage("images/diver-1.png");
  regularFont ='Georgia';
  scoreFont = loadFont("fonts/Sunrise.ttf");
}

//class to represent the segment/object for the diver 
class Segment {
  constructor(x, y){
    this.x = x;
    this.y = y;
    noStroke();
    fill(55);
    ellipse(this.x, this.y, segWidth);
    image(diverImg, this.x-55, this.y-segWidth, 100,80);
  }
  //methods to change direction of diver
   moveRight(){
    if (this.x > width){
       this.x = 0;
     }else {
    this.x += step;
     }
    noFill();
    ellipse(this.x, this.y,segWidth);
    image(diverImg, this.x-55, this.y-35,100,80);
     
  }
   moveLeft(){
     if (this.x < 0){
       this.x = width;
     }
     else
    {
      this.x -= step;
    }
     //flip the image when diver moves left
     noFill();
    ellipse(this.x, this.y,segWidth);
     push();
     //flipping along x-axis to create turning effect
     scale(-1,1);
    image(diverImg, -(this.x+55), this.y-35,100,80);
     pop();
  }
   moveUp(){
     if (this.y < 0){
       this.y = height;
     }
     else{
       this.y -= step;
     }
     //fill(55);
     noFill();
    ellipse(this.x, this.y,segWidth);
     image(diverImg, this.x-55, this.y-35,100,80);
  }
   moveDown(){
     if (this.y > height){
       this.y = 0;
     }
     else {
       this.y += step;
     }
     noFill();     
    ellipse(this.x, this.y,segWidth);
    image(diverImg, this.x-segWidth, this.y-35,100,80);
  }
  updatePosition()
  {
    if (direction === 'right')
    {
     this.moveRight();
    }
  if (direction === 'left')
    {
      this.moveLeft();    
    }
  if (direction === 'up')
    {
      this.moveUp();
    }
  if (direction === 'down')
    {
      this.moveDown(); 
    }
  }
  //get and set methods
  getX(){
    return this.x;
  }
  getY(){
    return this.y;
  }
  blink(){
    fill(random(200,255));
    ellipse(this.x,this.y, segWidth);
  }
  setX(x){
    this.x=x;
  }
  setY(y){
    this.y=y;
  }
}

//during set up, play song, initialize the x and y coordinates for monsters, and create diver segment 
function setup() {
  cnv = createCanvas(600, 600);
 // frameRate(20);
  song.loop();
  
  //for scrolling background
  x2=width;
  newSeg = new Segment(width/2, height/2);
  for (i =0; i<numOfMonsters; i++)
    {
      append(monsterXs,random(0,width));
      append(monsterYs,random(0,height));
      append(monsterSpeeds, random(-2,2));
    }
  
  //increase speed of monsters and diver and increment number of monsters every 20 seconds when game is playing
  setInterval(increaseSpeed, 20000); 
}

function draw() {
//draw according to the state of the game 
  
  //reset game whenever game is started again
  if (gameState === 'start'){
    if (reset === true){
      resetGame();
      reset = false;
    }
    playgameover=true; //flag to check if game over song should be played or not
    startPage(); //if game started, show start page
  }
  else if (gameState === 'play'){
    playGame(); 
  }
  //switch song when game/level is over and show the stop page
  else if (gameState === 'stop'){
    song.stop();
    if(playgameover === true){
      gameOverSong.loop();
      playgameover = false;
    }
    stopPage();
  } 
}

function startPage(){
   textAlign(LEFT, BASELINE);
  image(img, 0, 0,600,600);
  noFill();
  //when mouse enters the canvas - display the start button
  if (mouseX< 580 && mouseX > 20 && mouseY < 580 && mouseY > 20) 
     {
      fill('rgba(10,108,180,0.85)');
      ellipse(width*4/5, height*5/6, 150,100);
      fill('white');
      textFont(scoreFont);
      textSize(32);
      text('Start',425, 513);
     } 
  //when mouse hovers over start button, change its color
    if (mouseX < width*4/5+75 && mouseX > width*4/5-75 && mouseY < height*5/6 + 50 && mouseY > height*5/6-50)
    {
      fill('#6338B1');
      ellipse(width*4/5, height*5/6, 170,120);
      textFont(scoreFont);
      textSize(38);
      fill('white')
      text('Start',415, 513);
      //if start button is pressed, change game state
      if (mouseIsPressed)
          {
            gameState = 'play'
          }
    
    }
  fill(255, 60, 100);
}

function playGame(){
  smooth();
  background('rgb(100,229,255)');
  showInstructions();
if (startPlaying === true){
    startPlayingGame();
  }
}

function showInstructions(){
  background('rgb(100,117,255)')
  fill(255)
  square(25,25,550);
  image(instrImg, 30,30,540,540);
  textSize(20);
  textFont(scoreFont)
  fill('white');
   textAlign(CENTER);
  text('Instructions', width/2, 100);
  textSize(18);
  
   textAlign(LEFT,BASELINE);
  text ('Welcome!\n\nToday, you are a diver. Your mission is to \ncollect as much plastic waste from the \nocean as possible to protect marine life.\n\nMove around using the left, right, up, and \ndown arrow keys on your keyboard to \ncollect trash.\n\nEvery piece of trash collected = +10 points\n\nBeware! The ocean has monsters! Life = 100. \nEach encounter with a monster = -10 life.\nCome on! Let us free the ocean from plastic \nwaste!', 70, 130, width);
  ellipse(width/2,555, 80);
  fill(0);
  text('GO', width/2-12,560)
  if (mouseX > width/2-50  && mouseX < width/2+50 && mouseY > 540-50 && mouseY < 540+50)
    {
      fill('rgb(221,166,252)');
       ellipse(width/2,555, 80);
      fill(255);
      text('GO', width/2-12,560)
      if(mouseIsPressed)
     {
       startPlaying = true;
     }
    }
}

function startPlayingGame()
{
  //scrolling background
  background('rgb(100,229,255)');
  image(bgImg, x1, 0, width, height);
  image(bgImg, x2,width, height);
  
  //starting text
  text('Use arrow keys to start moving', width/4, height/3);
  fill(55);
  ellipse(width/2, height/2, segWidth-20);
  
  //if player starts moving, begin updating position - start game
  if (direction === 'up' || direction === 'down' || direction === 'left' || direction === 'right' )
 {
   background('rgb(100,229,255)');
   image(bgImg, x1, 0, width, height);
   image(bgImg, x2, 0, width, height);
   
  x1 -= scrollSpeed;
  x2 -= scrollSpeed;
  
  if (x1 < -width){
    x1 = width;
  }
  if (x2 < -width){
    x2 = width;
  }
   newSeg.updatePosition();
   updatePlastic();
   createMonsters();
   checkPlasticCollisions();
   checkMonsterCollisions();
   displayScoreandLife();
   checkLife();
 }
}

function keyPressed() {
  if (keyCode === RIGHT_ARROW) {
    direction = 'right';
    //print('right')
  } else if (keyCode === LEFT_ARROW) {
    direction = 'left';
    //print('left');
  } else if (keyCode === UP_ARROW) {
    direction = 'up';
    //print('up')
  }
  else if (keyCode === DOWN_ARROW) {
    direction = 'down';
    //print('down')
  }
}

function updatePlastic(){
  image(plasticImg, plasticX, plasticY, plasticWidth, plasticHeight);

}
//for every monster, update its speed and if it collides with the walls, reverse their direction of motion
function createMonsters(){
  for (let i = 0; i < numOfMonsters; i++){
    image(monsterImg, monsterXs[i], monsterYs[i], monsterWidth, monsterHeight);
    
    monsterXs[i] += monsterSpeeds[i];
  if (monsterXs[i] < 0 || monsterXs[i]+monsterWidth > width)
    {
      monsterSpeeds[i] = -monsterSpeeds[i];
    }
  }
}
//if plastic collected, generate a new one on the canvas at a random location and update score
function checkPlasticCollisions(){
  //print(newSeg.getX(), " and ", plasticX);
  if (newSeg.getX() > plasticX && newSeg.getX() < plasticX + plasticWidth && newSeg.getY() > plasticY && newSeg.getY() < plasticY + plasticHeight)
    {
      coinSong.play();
      score += 10;
      plasticX = random(0,width-plasticWidth);
      plasticY = random(0,height-plasticHeight);
      image(plasticImg, plasticX, plasticY, plasticWidth, plasticHeight);
    }
}
//if monster collides collected, generate a new one on the canvas at a random location and decrement life
function checkMonsterCollisions(){
  for (let i = 0; i < numOfMonsters; i++){
    if (newSeg.getX() > monsterXs[i] && newSeg.getX() < monsterXs[i] + monsterWidth && newSeg.getY() > monsterYs[i] && newSeg.getY() < monsterYs[i] + monsterHeight)
    {

      powSong.play();
      newSeg.blink();
      monsterXs[i] = random(0,width-monsterWidth);
      monsterYs[i] = random(0,height-monsterHeight);
      life -=10;
    }
  }
}

//when life reaches zero, end the game
function checkLife(){
  if (life === 0){
    gameState = 'stop';
  }
}

function displayScoreandLife(){
  fill('rgb(138,63,196)')
  textFont(scoreFont);
  textSize(18);
  text('Highscore: ' + highscore + '  Score: '+ score, 10,20);
  text('Life remaining: '+ life, 390,20);
}

function stopPage(){
  background('rgb(100,229,255)');
  image(gameOverImg, 0,0,width,height);
  fill('white');
  textAlign(CENTER);
  textFont(scoreFont);
  textSize(24);
  if (score > highscore)
  {
    print('score is ' + score + ' highscore is ' + highscore);
    
    text('Congratulations. You helped free the ocean of harmful plastic and you just beat your previous highscore!', 0, height/2, width);
    fill('gold')
    text('New Highscore =  ' + score, 10, height*2/3, width);
  }
    else 
    { 
      fill('white');
       text('Congratulations. You helped free the ocean of harmful plastic!',10, height/2, width);
      
       text('Score =  ' + score + '\n\nHighscore = '+ highscore, 10, height*2/3, width);
    }
  fill('red')
  ellipse(width/2, 530,200,80);
  fill('white')
  text('Play Again', 295, 538);
  if (mouseX > width/2-100  && mouseX < width/2+100 && mouseY > 530-40 && mouseY < 530+40){
    fill('gold');
    ellipse(width/2, 530,200,80);
    fill('white')
    text('Play Again', 295, 538);
    if (mouseIsPressed){
      reset = true;
      gameOverSong.stop();
      gameState='start';
    }
  }
}

function resetGame(){
  gameOverSong.stop();
  song.loop();
  life =100;
  if (score > highscore){
    highscore = score;
  }
  score = 0;
  startPlaying=false;
  x1=0;
  x2=width;
  direction='start';
  newSeg.setX(width/2);
  newSeg.setY(height/2);
  
  for (i = numOfMonsters; i > 2;i--)
  {
    monsterSpeeds.splice(i, 1);
    monsterXs.splice(i, 1);
    monsterYs.splice(i, 1);
  }
  numOfMonsters = 3;
  step =3;
  
  for (i =0; i<numOfMonsters; i++)
    {
      monsterSpeeds[i] = random(-2,2);
      monsterXs[i] = random(0,width);
      monsterYs[i] = random(0,height);
    }
}

function increaseSpeed(){
  if(startPlaying === true){
  step +=0.5;
  
  append(monsterXs,random(monsterWidth,width-monsterWidth));
  append(monsterYs,random(monsterHeight,height-monsterHeight));
  append(monsterSpeeds, random(-2,2));
  numOfMonsters+=1;
  for (i =0; i < numOfMonsters; i++ ){
    if (monsterSpeeds[i]<0){
      monsterSpeeds[i]-=0.5;
    }
    else{
      monsterSpeeds[i]+=0.5
      }
  }
  }
}

Problems/Future Improvements

Some problems I mentioned earlier. Additionally, I was not able to stop the ‘game over’ song and it kept playing repeatedly at the same time. I figure out that it was due to the draw function being repeatedly called so I set up a flag to check if the song was already playing.

For the future, I can add coins/life randomly on the canvas so the user can “gain” life as well. Also, I can also make the plastic move as well. Different difficulty levels can also be designed instead of one continuous level with increasing difficulty. Also, the diver’s collision with the monster or plastic is not the most accurate and can be made more accurate for a more visually appealing effect.

As a whole, I really enjoyed revamping the snakes game to make one that I enjoy and to also raise awareness on an issue I care about.

Midterm – Traveller

The Concept

The idea has been changed a fair bit based on the class discussions I had with the Professor and I decided to revamp the whole game idea. Now, it is an exploration based game, where the player is an alien flying in a UFO, visiting our solar system for the first time. I wanted to be able to create a scaled version of the solar system, but also emphasize on the vastness of space, thus the canvas is kept at a massive size (5000,5000), and all the orbits are drawn relative to their actual distance from the sun, along with the size of the canvas, I also added a scale to aid with this effect. I still wanted to gamify my project, so I also added a TIMED mode, where the player must fly around the vast solar system and find all the planets. After the planets are all discovered, the time it taken is displayed on screen.

The Sketch

Due to the extensive use of the functions scale() and translate(), the experience is best viewed in full screen mode. Here is the link: https://editor.p5js.org/mhh410/full/9DprniReW

The project starts at the home screen,  to give the user control instructions and a storyline, along with two different modes to play. The code itself is structured in a way that I hope it is easy for someone to understand even though it goes up to 900 lines. I did this by creating a bunch of functions in the beginning of the code.

There are several things I am proud in terms of coding.

Firstly, I was able to get to create all the planets in a way that their distances from each other are scaled in a mathematically accurate way.

//Create all Planets
mercury = new Planet(19, 2);
venus = new Planet(36, 4);
earth = new Planet(50, 5);
mars = new Planet(75, 3);
jupiter = new Planet(250, 25);
saturn = new Planet(500, 20);
uranus = new Planet(950, 13);
neptune = new Planet(1500, 11);
pluto = new Planet(1950, 1);

The first number uses a formula I made to convert AU into dimensions I can use in the canvas. I used the formula 0.5(AU) * 100, this was the best way to fit all the different radii of planet orbits into a 5000,5000 scale.

Secondly, another aspect I am proud of is the function to create every planet on a random spot within their orbit.

class Planet {
  constructor(r, s) {
    this.rad = sunRad + r;
    this.size = s;

    let angle = Math.random() * Math.PI * 2;

    this.x = Math.cos(angle) * this.rad;
    this.y = Math.sin(angle) * this.rad;
  }

 

The math in the Planet class makes sure that a random point x and y always lie on the radius of the orbit corresponding to that planet.

Another aspect I liked working working on was is the radio feature. I wanted to add some tracks to keep the user engaged while they traversed empty space.

//runs the music, if any song is playing, first stops it and then runs a randomized song.
function radio(next){
  if(next==true){
  if(track1.isPlaying()){track1.stop()}
    if(track2.isPlaying()){track2.stop()}
    if(track3.isPlaying()){track3.stop()}
    if(track4.isPlaying()){track4.stop()}
    if(track5.isPlaying()){track5.stop()}
    if(track6.isPlaying()){track6.stop()}
  let num = random(["1","2","3","4","5","6"])
  if(num=="1"){track1.play()}
  if(num=="2"){track2.play()}
  if(num=="3"){track3.play()}
  if(num=="4"){track4.play()}
  if(num=="5"){track5.play()}
  if(num=="6"){track6.play()}
  }
}

This function is triggered when the game mode starts and when a user clicks the music button in-game. The function is meant to play a song at random. When the code runs, all music stops first, and then a track is picked at random.

The Challenges

The project was filled with various challenged, some of them I had anticipated earlier. First of which was figuring out how to incorporate motion into the canvas and zoom in to the ufo moving around. I wanted to use camera() initially but then realized that I can’t use this feature in a 2D canvas. Thats when I moved to using translate() so the whole canvas moves when the keys are struck. Then, I used scale to create the zoom-in effect. Along with that, to extenuate on the motion, I decided to cover the dark canvas with star light.

// starlight creation, parameters based on motion of ufo
function starlight(moveX, moveY) {
  for (let i = 0; i < 500; i++) {
    fill(255, 255, 255, random(100, 255)); // Set the fill to a slightly transparent white
    noStroke();
    starsWidth[i] += moveX;
    starsHeight[i] += moveY;
    ellipse(starsWidth[i], starsHeight[i], random(1, 5)); // Draw a small ellipse at a random location with a random size
  }
}

This starlight function moves all the stars in the back drop in the opposite direction of the motion of the UFO to emphasize on the direction the user is moving around in.

Another major issue was the leaderboard. This was a feature I really wanted to add as a way to track all the quickest times a user has achieved in TIMED mode. I worked on it for ages and tried a bunch of implementation like createWriter(), saveStrings() and local storage, but none of it worked.  I have left my attempts as comments at the end of the code and eventually decided to drop this feature.

 

Midterm Project

Concept

The code for the game can be found here.

The game that I wanted to create was a version of my favorite classic breakout game but actually without the bricks!

So how does the game work?

The game has 3 phases:
  1. The start screen – this screen shows the instructions on how to play the game as well as a button to start the game the first time user is playing. The instructions are fairly simple:

– Move the paddle with the A and D keys to left and right respectively.
– Don’t let the ball fall!
– Catching UFOs rapidly increases score while catching Xs rapidly decreases your score.
– Beware: hitting the paddle at the edges or Xs increases the speed of the ball!
– Overall, the further you go in the sky the higher the score you get.
– Compete with your friends to beat the highest score on your laptop!

2. The game phase – once the user clicks on “Start Game”, the ball flies up in the air and the game begins. The user can start moving the paddle using the A and D keys to save the ball and hit it at specific angles to prevent the ball from speeding up.
The score, UFOs collected and the highscore appears at the top of the game.

3. Game over phase – if the user fails to catch the ball, game is over. This screen displays the user’s final scores and gives and option to replay the game.

Implementation

There were a number of parts about the assignment that proved to be challenging for me.

Ball and paddle collisions

When the game begins the ball is assigned a random speed between 2 and 5. Initially, I tried to keep the variable to change the direction of the ball separate than the speed of the ball. However, this was becoming immensely messy and created further complications when I tried to include the collision with paddle. To cater to this problem, I used negative numbers for ball’s left speed and positive numbers for ball’s right speed and similarly for ball’s top and bottom speed as well. However, because of the number of times the ball’s speed changes throughout the game, it was even more difficult to manage this because it could lead to the ball’s speed becoming 0 in which case the ball would get stuck in the air. So the if conditions had to be used very carefully throughout the code to avoid such problems.

A very simple example of this issue is that when the game starts, I choose a random direction for the ball to start off. If I simply chose a random number between -5 and 5, the ball could start off extremely slow or not even start off if the speed is 0. Therefore, to avoid this problem I first generated a random number between 0 and 1. Then based on the result of this, I chose the speed of the ball. If the random number was between 0 and 0.5, I randomly choose the ball’s speed from -5 to -3 in the left direction. On the other hand, if the first random number is greater than 0.5 than I choose a random number again for the ball’s speed between 3 to 5 in the right direction.

Below is the code for this problem:

let PoNX = random(0, 1);
let PoNY = random(0, 1);

if (PoNX < 0.5) {
  ballSpeedX = random(-5, -3);
} 
else {
  ballSpeedX = random(3, 5);
}
ballSpeedY = random(-5, -3);

The Y direction remains the same in the top direction to allow the user some time to adjust to the game screen. Though this problem is a very short one, it required navigating a lot of error statements to finally reach this conclusion. This is a very small example but I ran into similar problems throughout my code for when all ball collisions, with the wall, paddle, UFO and cross.

In particular, detecting the collisions with the corners of the paddle or from the sides of the paddle was the most difficult challenge costing me hours of hard work which I still doubt if it has been resolved fully. Adding on to the challenge, I had to play the game for a couple of minutes before I could bring the paddle in the exact position of contact with the ball which was causing an issue to be able to detect and test the problem, which made this challenge even more time consuming.

Mapping the paddle speed

Similarly, because of the above stated reasons, to come to a fully working code for mapping the paddle speed was a tough challenge. Below is the code for this:

//if ball hits the paddle
  else if (ballY >= 465 && ballX >= paddleX + 5 && ballX <= paddleX + 95) {
       ballSpeedY *= -1;
       bounces += 1;
       spaceBounce.play();
       ballY -= 3;

       //map ball speed at left of paddle
       if (ballX >= paddleX && ballX < paddleX + 50) {
         newSpeed = map(ballX, paddleX + 49, paddleX, 2, 5);
         if (ballSpeedX < 0) {
           ballSpeedX = -newSpeed;
         }
         if (ballSpeedX > 0) {
           ballSpeedX = newSpeed;
         }
         console.log(ballSpeedX);
   }

   //map ball speed at right of paddle
   else if (ballX >= paddleX + 50 && ballX < paddleX + 100) {
         newSpeed = map(ballX, paddleX + 100, paddleX + 50, 2, 5);
         if (ballSpeedX < 0) {
           ballSpeedX = -newSpeed;
         }
         if (ballSpeedX > 0) {
           ballSpeedX = newSpeed;
         }
         console.log(ballSpeedX);
       }
     }

Scrolling background

To give the idea to the user of a moving background, so they feel the are going upwards not only adds greater user engagement in the game, but also makes it slightly more difficult to judge the speed of the ball. To make it more difficult for the user as the game progresses, every time the user score increases by 500, the background speed increases to make it more challenging for the user and make thes feel as if the game is speeding up even though it’s not.

The following is the code for this effect:

//increase background speed
if (score % 500 == 0){
  bgSpeed += 0.5
}

//starry background
image(artwork, 0, bgY);
image(artwork, 0, bgY2);

//scroll background
bgY += bgSpeed;
bgY2 += bgSpeed;

if (bgY >= 1000) {
  bgY = bgY2 - 1000;
}

if (bgY2 >= 1000) {
  bgY2 = bgY - 1000;
}

Saving high score in local storage

To allow the users to compete with each other and prevent the game from only being a one-off game, every time a user plays the game, the score gets saved in the local storage of the browser. So regardless of the screen being closed or refreshed, the score is saved and pulled from the local storage for each game. This feature motivates the users to compete with each other and beat the highest score.

If time allowed, I could have improved this feature further by storing the high score in a file and then reading from the file each time so regardless of the high score being limited to the browser or laptop that the game is being played on, it would be more accessible to everyone who plays the game.

Changing ball & border colors

Finally, for aesthetic purposes and to prevent the game from becoming boring, I set the border and the ball colors to continuously change over the course of the game. This not only adds color to the game but also make it a little challenging for the user to easily keep track of the ball. This aspect of the game was also particularly challenging to write so I browsed some online sources to write the code below:

// cycle ball colors
   r += rs;
   g += gs;
   b += bs;

   //ball color cycle
   if (r > 255 || r < 50) {
     rs *= -1;
   }
   if (g > 255 || b < 50) {
     gs *= -1;
   }
   if (b > 255 || b < 50) {
     bs *= -1;
   }

Overall, despite all the challenges, I thoroughly enjoyed creating the game, (even though by now I have played it hundreds of time and no longer love it as much as I used to), I learned multiple features of p5.js as well as some logical aspects of the coding which I did not initially consider myself either. The game stimulated me logically as well as creatively as I was continuously think of what new features I could implement which are not commonly found in such games to give my project a unique feel. Because of the different features such as the speeds of the ball and the background, changing colors, UFOs, crosses, and high scores, I believe I was successful in giving this project a new and unique feel which I am very proud of!

Beach Cat! – Midterm Project

 

Final Product

Project Concept

I used to have a cat simulation game on my laptop’s control strip that I found very enjoyable to play with. The game served as an inspiration, along with my love for the beach, to create a similar game featuring a cat on the beach. The game’s concept is to take care of a cat and attend to its many needs (it might not have a lot of needs but requires a lot of close attention). To give this project a more game-simulator-like feel, I went for pixelated aesthetics and I am very happy with how it turned out.

Project Description/Parts I am Proud of

The user selects a cat and moves around the beach to feed the cat, give it water, and use the litter box. If the cat does not receive the needed attention, it dies and the user has to start over 🙁 The game is controlled by the arrow keys and asdw keys and the user is free to walk around all the beach areas.

One technique that I am proud of and which helped me a lot in visualizing the game and putting the building blocks together is that when building the game, I set up a grid on the screen with x and y indexes. I then used this grid to locate my objects and find intersections with them.

Background Terrain with the grid

Another aspect that I am proud of is another helper tool – a helper function to split a sprite sheet according to the global variable tileSize. This function takes a unidimensional sprite sheet image and returns an array of tiles. I wish I came up with this function at the beginning of the coding journey as opposed to the end because it would’ve saved me a lot of time but I am glad I incorporated it still.

//function to get a uni-dimensional spritesheet and return an array of the elements
function splitSheet(imgName){
  let arrayName = []
  for(let i=0; i<imgName.width/tileSize; i++){
    arrayName[i] = imgName.get(i*tileSize, 0, tileSize, tileSize);
  }
  return arrayName;
}

I also tried my best not to hard-code any indexes, locations, etc, which is a good practice for when I am going to come back to the game and built on top of it.

Problems

I really wanted this game to have background music, but because I uploaded too many images (the sprite sheets I had were not convenient to work with so I had to split them into many smaller ones that had the same dimensions)  there was no memory left for a sound that is lengthy enough to play on the background. When I did, the game basically never loaded, which is why I had to give up that idea.

Another problem that I ran into and that took me a while to figure out is storing my 4 different cat sprite sheets for 4 different movements (up, down, left, right), AND 3 different “stages” of each movement. This is when I came up with the function mentioned above and I ended up storing things in a 3d matrix.

Overall, I am really happy with the way the game turned out given the short time limit. There are many more features that I would love to add to the game but for now, this is the final game 🙂

MIDTERM: “Grannys Lovely Maze”

TO PLAY THE GAME CLICK HERE!

If you are a professor and you want to see the code click here!

GAME CONCEPT

Grannys Lovely Maze is a 2-player maze chase game. Kitty (PLAYER 1) is a royal cat who enjoys spending time in the outdoor maze of his castle. He needs to find a way back to his grandma before the Bear (PLAYER 2) turns him into a dinner. The maze is set in the nighttime, meaning that neither the Kitty nor the Bear can see the full maze. Each player is spotlighted to only see the closest surrounding. This feature is the distinguishing point of this game, which adds some uniqueness as well as makes the game more challenging.

The game itself consists of 3 maps. If one of the players scores 2 consecutive wins, while the other player scores 0, the third map is not played and the game ends. Scores are not displayed during the game because I feel like with how short the game is there is no real need for a visual element on the screen that could potentially distract the players from the game.

Below are the screenshots from each of the screens.

IMPLEMENTATION

My project consists of three screens: start (instructions), game, and end. In designing each screen I have tried to be consistent with the sizes, shapes, and colors that I use. The overall visual concept can be probably described as an 8-bit pixel-style arcade. This is a style that I adore and used previously in personal and class projects. I have done some images myself (ground and bushes textures in the maze) as I couldn’t find what I was looking for online. I have sourced the sprites, the music, and the font to match online.

To create some order and organization, I have created 5 classes: Game (connects every class and controls the game logic), Cell (map display), Player (players display), Door (granny display), and Confetti (confetti display). I have used the gameState variable inspired by professor Mang’s example to switch between game screens. There is also a separate file that stores three available maps as arrays.

To create the spotlight effect and display only a part of the maze surrounding the players I have created a function inside of the Player class that uses an if statement to check if the cells around the player position are within the boundaries of the spotlight. I am no mathematician so I didn’t figure out how to make so that the spotlight round instead of boxy. However, I feel like it fits the pixel aesthetics of the game and doesn’t take away from the experience.

inSpot(other) {
    if (
      other.x < this.x * cellWidth + this.spotlight * cellWidth &&
      other.x > this.x * cellWidth - this.spotlight * cellWidth &&
      other.y > this.y * cellWidth - this.spotlight * cellWidth &&
      other.y < this.y * cellWidth + this.spotlight * cellWidth
    ) {
      return true;
    }
    return false;
  }

To add some aspect of celebration to the end screen and make it less plain I have added the Confetti class. It creates a list with randomly colored rectangles that both rotate and move vertically at random speeds. If the position of a rectangle is beyond the height of the screen, the rectangle is deleted from the list using the splice function. I am proud of this little feature. I think it adds some character and adds to the experience.

class Confetti {
  constructor() {
    this.x = random(width);
    this.y = -10;
    this.diameter = random(10, 20);
    this.color = color(random(255), random(255), random(255));
    this.speed = random(1, 5);
    this.rotation = 0;
    this.rotationSpeed = random(-0.05, 0.05);
  }
  
  update() {
    this.y += this.speed;
    this.rotation += this.rotationSpeed;
  }
  
  display() {
    push();
    translate(this.x, this.y);
    rotate(this.rotation);
    rectMode(CENTER);
    noStroke();
    fill(this.color);
    rect(0, 0, this.diameter, this.diameter/2);
    pop();
  }
  
  isOffScreen() {
    return this.y > height + this.diameter;
  }
}

Another part of the implementation I like is the display of the granny (exit point). I found a beautiful spritesheet online. To make her more dynamic, she is looking around before Kitty reaches her. She inherits her spotlight function from the Player class.

class Door extends Player {
  constructor(i, j, type) {
    super(i, j, type);
    this.spotlight = 3;
  }

  // MAKE THE SPRITE CHANGE EVERY SECOND
  update() {
    if (frameCount % 60 == 0) {
      this.sprite++;
      this.sprite %= 6;
    }
  }
  // DISPLAY
  draw() {
    this.update();
    image(
      oldladyImg,
      this.x * cellWidth,
      this.y * cellWidth,
      cellWidth,
      cellWidth,
      40 * this.sprite,
      0,
      40,
      54
    );
  }
}

CHALLENGES

In the implementation of my project, I have run into some problems, most of which were resolved. Main problems that I have tackled include:

        • Maze Building – initially I planned to implement an algorithm that would build random solvable mazes. I did have an algorithm, however, I then realized that I wouldn’t be able to correctly assign player and maze exit positions. In such a way that players can each reach one another and exit using multiple ways, and player 1 is as close to the exit as player 2 is close to player 1 (to make it equally possible for players to win in each round). A solution I found was building the maze from pre-existing maps. As a full maze is not shown to the players anyways, there is not as much need in generating new maze maps each time players want to play the game as they will have a harder time memorizing the map that they can’t fully see. I used the algorithm to show me possible map designs, which I slightly modified to increase the number of possible passages to the exit. For each of the maps, I store a list of starting positions for each player and the exit point.
        • Button Font – I use a custom font loaded as a file for my game. Initially, I loaded it and declared it as a variable in the javascript file. I used textFont("retroGaming"); the setup function to set the font of the body to the custom font, however, it didn’t work for the buttons. I have tried using button.style("font-family","retroGaming"); but it didn’t work either. After lots of thinking and searching, I realized that I can set button properties and load font in CSS, which was the solution to my problem.
        • Players Display – the sprites, though proportional to each other and their surroundings looked tiny in the 47*37 maze. I have recognized that this is an issue only at the very end of developing my project as I left sprite sheets for the very last. Changing canvas size didn’t make the players more visible and also made other screens look disproportionate, so I decided to find another solution, which was decreasing the size of the maze. I believe that this has made the maze less complicated to go through, which I am not happy about. On the other hand, with bigger-sized maps I didn’t use the entirety of the map useful for going through a level, meaning that usually a corner or 2 would be left untouched. If I had a chance to redo this project, I’d try making it so that the size of the map that each player sees is scaled up, so they don’t actually know if they are close to each other or to the exit until there are very few cells left between them and the two viewports merge. I think it would make the game more exciting but at the moment I have no idea how I would implement that.

MidTerm Project- “Help Milo!”

Concept

My midterm project, “Help Milo!”, is a high score based game where the players help a cat named Milo collect as many food items as possible. When it came to deciding the kind of character and theme I would adopt for this game, I instantly knew it had to revolve around cats and specifically my pet cat, unsurprisingly named Milo. Initially, my sketch for this project was very abstract as I had created randomly sized platforms that emerged at different instances. But then the idea of creating an almost picture frame view of the game looked better, so I decided to go for a more organized platform design. 

 

In the game, the player uses LEFT and RIGHT arrow keys to move the cat along the platforms that continuously move upwards. Moving the cat to the top of the ladder causes it to climb down so it avoids touching the top of the frame. The player needs to collect as many food items as possible and avoid touching the upper frame or falling on the bottom frame. The score gets updated and high score is also displayed at the end. The game restarts when ENTER is pressed. 

Game Code

For this game, I created classes for both the player, platforms, and bonuses (food item). Through the classes, I was able to append the class objects into arrays for both platforms and bonuses so that the for loop in the draw function could iterate over the arrays for any function. At a specific frameRate, new platform objects and bonuses are created and then when they move beyond the frame, they are removed from the array to avoid lag in the game. 

 

I think I found it difficult to retrieve the attributes of different classes to use them in another class. I struggled to find a way in which I can use the y coordinate of the platform in the player class so that I can limit the movement of the player beyond the platform. Similarly, I wanted to access the coordinates of the bonuses for the player to collect them in a function. I figured out the solution by passing the entire object ‘platform’ or ‘bonus’ as an argument to the function in player. That way, I was able to access the different attributes of the other classes and use it to change the movement of the player. 

update(platform){
 
  //if the player in on the platform and not on the ladder space, then it stays on the platform
  if(this.y >= platform.y && this.y <= platform.y+platformsize && (this.x<platform.spacex || this.x > (platform.spacex+platform.spacewidth))){
    this.y = platform.y;
    
  }
  //if player on ladder space, it falls
  else if(this.x>platform.spacex && this.x < (platform.spacex+platform.spacewidth) && this.y >= platform.y && this.y <= platform.y+platformsize){
    this.rownum = 0;
    this.y+=1;
    this.col = (this.col+1)%4;
  }
  //initially it falls 
  else{
    this.y+=1;
    //this.col = (this.col+1)%4;

  }
}
//to check if the x and y coodinates of player and food is same to catch the food
bonuscatch(bonus){
  if(this.x>bonus.x && this.x < bonus.x+(bonus.size) && this.y>bonus.y && this.y <bonus.y+(bonus.size)){

    caught = true;
    success.play();
  }
}

 

A bug that constantly proved to be an ever present issue was the code placement in and outside the for loop to iterate over the arrays of platforms and bonuses. Due to the usage of a single for loop within the draw() function, it took me time to figure out how to manage iteration over the different arrays at the same time and call the different functions.  I think I found the use of sprite sheets and variable “gamestate” to change the user interface to be the most interesting to figure out.

Improvements

I think for future improvements, I would really like to implement some sort of obstacle in the game. I was able to implement the obstacles but could not edit their frequency because of the for loop and use of .splice() in the obstacle arrays. I would also like to add different levels to the game, add different elements to the levels to make them complex or easier. Creating different levels of the game through a random structure would also be an interesting aspect to develop.

Midterm Project – Yerkebulan Imanbayev

Concept:

With this project, I decided to focus on the classic game of “Whack-A-Mole.” Initially, I was planning to recreate the game, but then after the realization that it will become more of an exercise than a project, I decided to change the concept. I thought of it more realistically and what a game like that might possibly entail – and one of the interpretations of it could be that it is animal abuse. Therefore, I decided to expand on that concept and use the game as a platform to share awareness about animal abuse. That’s why instead of “whacking” a mole, the user has to “whack” a politician who committed or contributed to animal abuse.

Game features:

When the user hits any key to be redirected to the “game” window, they can see the time and the score being displayed. There are two characters in front of them: the mole and the politician. There is only one way to win in this game: to whack the politician 30 times in the span of 20 seconds. However, there are two different ways to lose in this game: the user can accidentally click on the mole – in which case they lose automatically – or they do not manage to whack the politician 30 times in the span of 20 minutes.

Generally, I attempted to make the game design appealing by creating the background image using DALL-E 2. The colors of the buttons and the text were picked so as to ensure legibility and conveyance of the sense of fun, quirky arcade games that we all know and love. The game is also designed to ensure that all buttons have a sound attached to them, including the background sound that is constantly playing unless the user is on the “win” or “loss” window.

Issues:

There were multiple issues I faced when I first started working on the project. Due to a large number of lines in the code, it was very hard for me to keep track of my variables and the functions. That is why my first step in tackling all of those issues was to give my variables a sensible name and making sure to comment on confusing parts of my code.

The next issue I faced was positioning my characters in the ellipses. I did not know how to do that because the function that contained the coordinates for my ellipses was separate from the class that contained the coordinates for my characters. That is when one of my friends suggested using a double array. After the coordinates of the ellipses were placed in the double array, they were called in the class as the coordinates for the characters.

  //creating an array to hold the coordinates of all the ellipses to refer to it later on
  arrayEllipses = [
    [95, 225],
    [95, 325],
    [95, 425],
    [235, 225],
    [235, 325],
    [235, 425],
    [375, 225],
    [375, 325],
    [375, 425],
  ];
}
//pulling out random elements from my array
   this.randomNumberPolitician = int(random(0, 9));
   this.randomNumberMole = int(random(0, 9));
   
   //check if the integer is the same; if so, choose a different hole for the mole
   while (this.randomNumberPolitician == this.randomNumberMole) {
     hoverMole = false;
     this.randomNumberPolitician = int(random(0, 9));
     this.randomNumberMole = int(random(0, 9));
   }

   //changing the position of the politician if the image is clicked
   if (hoverPolitician && mouseIsPressed) {
     this.politicianX = arrayEllipses[this.randomNumberPolitician][0];
     this.politicianY = arrayEllipses[this.randomNumberPolitician][1];
     this.moleX = arrayEllipses[this.randomNumberMole][0];
     this.moleY = arrayEllipses[this.randomNumberMole][1];
     clicks++;
     buttonSound.play();
   }

One of the elements in the code embedded above was my solution for the next issue I faced: because the coordinates assigned to the characters came from the same array, they were inevitably going to be equal at some point, leading to the mole and the politician appearing in the same ellipse. My way of tackling that was creating a “while()” function that would randomize their position again if their coordinates were equal.

This led me to a different issue: at times, when the character clicks on the politician after their coordinates were reorganized (because they matched as mentioned in the paragraph earlier) the code would consider that to be clicking on the mole and would lead to a loss. I minimized the issue to the best of my abilities by decreasing the range of coordinates that would be considered a “click” for the mole. Despite minimizing that, there is still that probability.

Future improvements:

In the future, I would definitely want to improve the issue mentioned in the previous paragraph in a way that would leave no room for the possibility of losing if the politician was clicked. In the same vein, I would also want to create a more efficient way to match the coordinates of the characters to the coordinates of the ellipses. Despite using the double array, the values within the array were hard-coded.

I would also like to make the design of the characters more game-like by having them appear from under the hole as opposed to simply popping up. I believe that it would make the interaction more user-friendly.

Overall, despite certain issues within the code, I am proud of my work as someone who is learning programming for the first time, and I believe I was able to create a game that the user can play, restart, and enjoy.