Midterm Project: Catch ’em

Game:

Link to fullscreen: https://editor.p5js.org/aneekap/full/En7_lTESA

Overall Concept of the Project:
CATCH ‘EM is a catching game with two levels but with a twist. Inspired by two of my favorite shows, Demon Slayer and Avatar: The Last Airbender, in this game, the player needs to catch the “good guys” and avoid the “bad guy” to survive. Elements fall from the top of the screen randomly, and the player controls a basket at the bottom to catch the falling elements. The goal is to accumulate points by catching enough good guys before the timer runs out. The game incorporates changing themes and increasing difficulty as the player progresses. Additionally, power-ups appear occasionally, providing the player with special abilities such as temporary immunity to the villain.

Working :
The game utilizes OOP to create subclasses of the Element class with varying features. The game grid is defined by invisible tiles, allowing precise positioning of falling elements. Elements move vertically in tile increments, and collision detection occurs when their coordinates align with the basket’s position, triggering consequences. The game employs frame-based updates to control element speeds. Levels are managed through conditional checks on scores. The player controls a basket moves horizontally using left and right arrow keys to catch the elements.

More specifically, the game operates within a structured grid system defined by the number of rows and columns. Each element, whether it be a hero, villain, or power-up, is represented by an object that inherits from the Element class. These objects are initialized with random starting positions along the columns of the grid. The update method of the Element class ensures a downward motion, simulating the falling effect. The catch method detects whether an element is caught by the player’s basket.

The code I wanted to highlight is how the gameplay works for each level. I used arrays for the different classes to create the elements at each level and used framecount to control the number of elements on the screen to avoid overcrowding.

display() {
    if (SCORE >= 3 && SLIDE == 2) {
      SCORE = 0;
      this.villains = [];
      this.powerups = [];
      this.heroes1 = [];
      SLIDE = 3;
      TIMESTART = millis();
    }

    if (SLIDE == 3) {
      image(inst2, 0, 0, width, height);
    }

    if (SCORE >= 5 && SLIDE == 4) {
      SCORE = 0;
      this.villains = [];
      this.powerups = [];
      this.heroes2 = [];
      SLIDE = 5;
    }

    if (SLIDE == 5) {
      WIN = true;
      this.Play = false;
    }

    // LEVEL 1
    if (SLIDE == 2) {
      image(bg1, 0, 0, this.w, this.h);

      if (frameCount % 3 == 0) {
        this.heroes1.push(new Heroes1());
      }
      for (let hero of this.heroes1) {
        hero.display();
        if (hero.y >= height + 40) {
          // Removes character once it exits the screen
          this.heroes1.splice(this.heroes1.indexOf(hero), 1);
        }
      }
      if (frameCount % 5 == 0) {
        this.villains.push(new Villain(1));
      }
      for (let villain of this.villains) {
        villain.display();
        if (villain.y >= height + 40) {
          this.villains.splice(this.villains.indexOf(villain), 1);
        }
      }

      if (this.powerups.length < 2) {
        this.powerups.push(new PowerUp(1));
      }
      for (let powerup of this.powerups) {
        powerup.display();
        if (powerup.y >= height + 40) {
          this.powerups.splice(this.powerups.indexOf(powerup), 1);
        }
      }

      textSize(30);
      text("LEVEL 1", width / 2 - 60, 45);
      textSize(25);
      text("Aim: 3", width - 100, 80);
      textSize(25);
      text("SCORE: " + SCORE, width - 120, 45);

      let COUNTDOWN = 20 - (millis() - TIMESTART) / 1000;
      if (COUNTDOWN <= 0) {
        this.Play = false;
      }
      text("Time: " + floor(COUNTDOWN), 20, 45);

      this.basket.display();
      this.basket.update();
    }

    // LEVEL 2
    if (SLIDE == 4) {
      image(bg2, 0, 0, this.w, this.h);

      if (frameCount % 2 == 0) {
        this.heroes2.push(new Heroes2());
      }
      for (let hero of this.heroes2) {
        hero.display();
        if (hero.y >= height + 40) {
          this.heroes2.splice(this.heroes2.indexOf(hero), 1);
        }
      }

      if (frameCount % 4 == 0) {
        this.villains.push(new Villain(2));
      }
      for (let villain of this.villains) {
        villain.display();
        if (villain.y >= height + 40) {
          this.villains.splice(this.villains.indexOf(villain), 1);
        }
      }

      if (this.powerups.length < 2) {
        this.powerups.push(new PowerUp(2));
      }
      for (let powerup of this.powerups) {
        powerup.display();
        if (powerup.y >= height + 40) {
          this.powerups.splice(this.powerups.indexOf(powerup), 1);
        }
      }

      textSize(32);
      text("LEVEL 2", width / 2 - 75, 45);
      fill(0, 0, 0);
      textSize(30);
      text("Aim: 5", width - 100, 80);
      textSize(25);
      text("SCORE: " + SCORE, width - 120, 45);

      fill(255, 255, 255);
      let COUNTDOWN = 15 - (millis() - TIMESTART) / 1000;
      if (COUNTDOWN <= 0) {
        this.Play = false;
      }
      text("Time: " + floor(COUNTDOWN), 20, 45);

      this.basket.display();
      this.basket.update();
    }
  }

I took the graphics from the web and cropped them accordingly.

 

The background music was taken from YouTube and was taken from the Original Soundtrack of the shows.

Level 1: https://www.youtube.com/watch?v=HcvK20wWQDw

Level 2: https://www.youtube.com/watch?v=OqJec3–RXc

 

Challenges Faced and Areas for Improvement:
The hardest part was having an organized structure of classes and reducing redundancy by using inheritance wherever possible. The other challenge was having so many elements on the screen at once and ensuring it didn’t glitch by deleting elements correctly. I was also not able to make a full-screen mode since the images and tiles were getting stretched and did not work as well.
An area for improvement is ensuring that elements don’t overlap. Since elements are generated randomly, preventing them from overlapping proved difficult. I would also like to add more levels/modes.

Overall, I am happy with the outcome and it was a fun project to work on.

Leave a Reply