Daniel Green Midterm work

In the realm of entertaining carnival games, surely nobody is unfamiliar with the infamous game known as Whack-A-Mole. It is already known as a widespread game that we all surely must have enjoyed at least once during our lives; after all, who doesn’t enjoy bonking moles that pop out of dirt holes in the head with a big hammer? I am happy to make it clear that I myself am among the folks who have always enjoyed the game; hence why I chose to create my own unique take of the Whack-a-Mole game, but with a few twists to make it be a little bit different from the original. The twist to mention, the one that is the elephant in the room, the moles in my game are instead replaced by colored 2D versions of Mickey Mouse’s head! This is because of how not only was he my favorite during my childhood, but also because my major is based in getting involved in digital business with certain theme parks, which does include Disney! Therefore, I would consider this project as my own practice for when the day comes of me hopefully getting the chance to work alongside a major theme park like Disney. Speaking of colored heads, allow me to successfully reveal to you how the game works.

The way my game works is that it takes the classic Whack-a-Mole formula and formulates two rows of three holes, which altogether make up six holes. On top of that, the game would also supposedly take place in a forest, which is apart of my attempt to have the game feel as if you found these six holes somewhere in a special kind-of forest where these six holes have these Mickeys popping up in an act to simply challenge to see how fast your reflexes are; something that just sounds as if it popped right of a literal Disney short film! The only exception, however, would be the scoreboard on the top left corner of the game that will have points (aka your points) increase each time you hit a specific Mickey Head. Now, as for the Mickey heads that will pop out, there are about four different variations of them. The first variation is the Mickey Head whose head is colored cyan and his ears are colored white; simply, he is the variation that you are supposed to hit, since each time you hit one, you earn five points added to your score! Of course, that isn’t the only Mickey Head you are able to hit, for you must also be on the lookout for a yellow Mickey Head that will pop up anywhere between the gaps of the rows or in any of the six holes; it only shows you for one second, so you are going to be tested to see how fast you can click on it being it disappears into a three-minute cooldown. The reward for clicking it on time will add thirty points to your score! However, there are two other Mickey Heads that are best recommended for you to avoid. The first Mickey head to avoid is an orange-colored Mickey head that will be anywhere in either the row gaps or holes by random; this is because if you click on that one, it will reset your scoreback to zero as a penalty! Lastly, the other Mickey head to avoid is a red-colored Mickey head that will be unpredictable and pop up in almost the same spots as the yellow and orange ones, because clicking on it will immediately send you to a game over screen that lasts six seconds before taking you back to the main menu! However, a funny thing that I made sure was possible in my game, was that if a player were to wish to quit the game via being retreated back to the main menu and closing the game from there, they would have to click on a red Mickey head on purpose to do; I simply that since the game over screen is kind-of hilarious on its own without its awkward transition and silly trombone sound effect, it would be funny for the person and thus leave them with a smile on their face.

let mickeys = [];
let lastSpawnTime = 0;
let spawnDuration = 2500;

let score = 0;
let hitSound;

let redMickey = null;
let nextRedSpawn = 0;
let redSpawnTime = 0;
let redDuration = 1500;

let gameOverSound;
let gameOverStartTime = 0;

let yellowMickey = null;
let nextYellowSpawn = 0;
let yellowSpawnTime = 0;
let yellowDuration = 1000;
let yellowCooldown = 180000;

let orangeMickey = null;
let nextOrangeSpawn = 0;
let orangeSpawnTime = 0;
let orangeDuration = 1500;


// Being sure to check whether it's time for a new group to come up
    if (millis() - lastSpawnTime >= spawnDuration){
      spawnMickeys();
    }

    // Making sure to draw every Mickey currently in "mickeys=[]" array
    for (let mickey of mickeys){
      mickey.display();
    }

    // Spawn in red Mickey Head at the time as scheduled for it
    if(
      redMickey === null && 
      orangeMickey === null &&
      millis() >= nextRedSpawn
    ){

      spawnRedMickey();
      
    }
      
    // Take away red Mickey head after 1.5 second timer is up
    if (
      redMickey != null &&
      millis() - redSpawnTime >= redDuration
    ){
      redMickey = null;
      scheduleRedMickey();
    }

    // have the Red Mickey head be drawn
    if (redMickey !== null){
      redMickey.display();
    }

    // Spawn in yellow Mickey Head at the time as scheduled for it
    if (yellowMickey === null && millis() >= nextYellowSpawn){
      spawnYellowMickey();
    }

    // Put yellow head away after 1 second
    if (
      yellowMickey !== null &&
      millis() - yellowSpawnTime >= yellowDuration
    ){

      yellowMickey = null;

      // Begin cooldown (maximum 3 minutes)
      scheduleYellowMickey();
      
    }
    
    // Letting the yellow Mickey head be drawn
    if (yellowMickey !== null){
      yellowMickey.display();
    }

    // Spawn in orange Mickey Head at the time as scheduled for it
    if (
      orangeMickey === null &&
      redMickey === null &&
      millis () >= nextOrangeSpawn
    ){

      spawnOrangeMickey();
      
    }

    // Take out orange Mickey Head after 1.5 seconds
    if (
      orangeMickey !== null &&
      millis() - orangeSpawnTime >= orangeDuration
    ){

      orangeMickey = null;
      scheduleOrangeMickey();
    }

    // Let orange Mickey head be drawn and displayed
    if (orangeMickey !== null){

      orangeMickey.display();
      
    }

 function spawnMickeys(){

    // Remove any previous Mickey Heads
    mickeys = [];

    // Making the decision of whether 1, 2, or 3 Mickey Heads appear
    let amount = floor(random(1, 4));

    // Making sure it remembers which holes have been selected
    let usedHoles = [];

    for (let i = 0; i < amount; i++){

      // Decide on a random hole from 0 to 5
      let hole = floor(random(6));

      while(usedHoles.includes(hole) ||
            (redMickey !== null &&
             redMickey.holeIndex === hole
              
            )
           ){
        hole = floor(random(6));
           }

      // Making sure it remembers that this hole is in-use
      usedHoles.push(hole);

      // Being sure to calculate the hole's role and column
      let row = floor(hole / 3);
      let col = hole % 3;

      // Being sure to calculate the position of the Mickey Head
      let x = width * (0.25 + col * 0.25);
      let y = height * (0.45 + row * 0.3);

      // Bring the Mickey Head to life above the hole by a tiny bit
      let mickey = new MickeyHead(x, y - 30);

      // Adding Mickey Head to "let mickeys = []" array
      mickeys.push(mickey);
    }

    // Remembering when this circle group appeared
    lastSpawnTime = millis();

    // Choosing random duration between 2-3 seconds
    spawnDuration = random(2000, 3000);
  }

  function spawnRedMickey(){

    // Choose which of the 9 positions for the red head to go by random
    let position = floor(random(9));

    let x;
    let y;

    if (position < 6){

      // one of the 6 holes, should the Mickey head appear there
      let row = floor(position / 3);
      let col = position % 3;

      x = width * (0.25 + col * 0.25);
      y = height * (0.45 + row * 0.3) - 30;
      
    } else {

      // One of the 3 gaps between columns
      let col = position - 6;

      x = width * (0.25 + col * 0.25);
      y = height * 0.60 - 30;
      
    }

    redMickey = new RedMickeyHead(x, y);
    redMickey.holeIndex = position < 6 ? position : -1;
    redSpawnTime = millis();
    
  }

  function spawnOrangeMickey(){
    let availablePositions = [];

    // Be on lookout for all 9 possible positions for orange head to use
    for (let position = 0; position < 9; position++){

      let x;
      let y;

      if (position < 6){

        let row = floor(position / 3);
        let col = position % 3;

        x = width * (0.25 + col * 0.25);
        y = height *(0.45 + row * 0.3) - 30;
      } else {
        let col = position - 6;

        x = width * (0.25 + col * 0.25);
        y = height * 0.60 -30;
        
      }

      let occupied = false;

      // Tracking of cyan heads (if need be)
      for(let mickey of mickeys){

        if (dist(x, y, mickey.x, mickey.y) < 65){
          occupied = true;
        }
        
      }

      // Check for yellow head (if need be)
      if (yellowMickey !== null){

        if (dist(x, y, yellowMickey.x, yellowMickey.y) < 65){
          occupied = true;
        }
        
      }

      if (!occupied){
        availablePositions.push({ x: x, y: y, position: position});
      }
      
    }

    // Orange head should only spawn if there's a free open-position
    if (availablePositions.length === 0){
      scheduleOrangeMickey();
      return;
    }

    let chosen = random(availablePositions);

    orangeMickey = new OrangeMickeyHead(chosen.x, chosen.y);
    orangeMickey.holeIndex = chosen.position < 6 ? chosen.position : -1;

    orangeSpawnTime = millis();
    
  }

  function spawnYellowMickey(){

    let availablePositions = [];

    // Keep track of yellow Mickey Head's 9 possible spots
    for (let position = 0; position < 9; position++){

      let occupied = false;

      // Check on the cyan Mickey heads
      for (let mickey of mickeys){

        let row = floor(position / 3);
        let col = position % 3;

        if (position < 6){

          let x = width * (0.25 + col * 0.25);
          let y = height *(0.45 + row * 0.3) - 30;

          if (mickey.x === x && mickey.y === y){
            occupied = true;
          }
          
        }
        
      }

      // Check on the red heads if necessary on behalf of yellow heads
      if (redMickey !== null){

        let redPositionX = redMickey.x;
        let redPositionY = redMickey.y;

        let x;
        let y;

        if (position < 6){

          let row = floor(position / 3);
          let col = position % 3;

          x = width * (0.25 + col * 0.25);
          y = height * (0.45 + row * 0.3) - 30;
        } else {
          
          let col = position - 6;

          x = width * (0.25 + col * 0.25);
          y = height * 0.60 - 30;
        }

        if ( x === redPositionX && y === redPositionY){
          occupied = true;
        }

        
      }

      if (!occupied){
        availablePositions.push(position);
      }
      
    }
    // decision-making on picking any available position
    let position = random(availablePositions);

    let x;
    let y;

    if (position < 6){

      let row = floor(position/3);
      let col = position % 3;

      x = width * (0.25 + col * 0.25);
      y = height * (0.45 + row * 0.3) - 30;
    } else {
      
      let col = position - 6;

      x = width * (0.25 + col * 0.25);
      y = height * 0.60 - 30;
    }

    yellowMickey = new YellowMickeyHead(x, y);
    yellowMickey.HoleIndex = position < 6 ? position : -1;

    yellowSpawnTime = millis();
     
  }

  function scheduleRedMickey(){

    // Decide random delay from 4 to 10 seconds
    nextRedSpawn = millis() + random(4000, 10000);
  }

  function scheduleOrangeMickey(){

    nextOrangeSpawn = millis() + random(4000, 10000);
  }

  function scheduleYellowMickey(){

    //Reminder to wait a few minutes (3 maximum at least) before next head appears
    nextYellowSpawn = millis() + yellowCooldown;
  }

  function drawGameOver(){

    // Set-up of the background color for Game Over screen
    background(0);

    //Set-up of game over message appearance
    textAlign(CENTER, CENTER);
    textFont("Press Start 2P");
    textSize(30);

    fill(255, 80, 180);
    text("FAIL!\nSORRY SON!", width / 2, height / 2);

    // Keep track of whether 6 seconds have finished being passed
    if(millis() - gameOverStartTime >= 6000){

      // Bring the player back to the main menu automatically
      gameState = "menu";

      playMenuMusic();
    }
    
  }

  function mousePressed(){
   if (gameState === "menu") {

     console.log("Mouse clicked! Game state:", gameState);

     // Making sure that PLAY button is activated
     if (
       mouseX > width / 2 - 110 &&
       mouseX < width / 2 + 110 &&
       mouseY > height - 170 &&
       mouseY < height - 90
     ){

       // Begin soundtrack after user interacts with menu
       userStartAudio();
       menuSoundtrack.play();
       

       

       gameState = "instructions";
     }
     
   }
     
   else if (gameState === "instructions"){

     // Making sure that GO TIME (start) button gets activated
     if (
       mouseX > width / 2 - 130 &&
       mouseX < width / 2 + 130 &&
       mouseY > height * 0.83 - 32.5 &&
       mouseY < height - 0.83 + 32.5
     ){

       // Have score be resetted within a new game
       score = 0;

       // Gameplay start
       gameState = "game";

       // cease main menu music
       menuSoundtrack.stop();

       // Begin gameplay music
       gameplayMusic.play();
       console.log("Attempted to start gameplay music!");
       
       spawnMickeys();

       // Reset scheduling of red Mickey head
       redMickey = null;
       scheduleRedMickey();

       // Reset yellow Mickey head for new game
       yellowMickey = null;

       // First yellow Mickey Head appearing after 10-20 seconds by random
       nextYellowSpawn = millis() + random(10000, 20000);

       // Reset orange Mickey head for new game
       orangeMickey = null;
       scheduleOrangeMickey();
       
       // cease menu music upon gameplay beginning
       if (menuSoundtrack.isPlaying()){
         menuSoundtrack.stop();
       }
       
     }
     
   }

   else if (gameState === "game"){

     // Be on lookout for player clicking on red Mickey head
     if (
       redMickey !== null &&
       redMickey.isClicked(mouseX, mouseY)
     ){

       // have gameplay music stop immediately
       gameplayMusic.stop();
       
       // Switch over to game over
       gameState = "gameover";

       //Setting put of the memory of when the player lost
       gameOverStartTime = millis();

       // Play sad trombone at precise moment
       gameOverSound.play();

       // Take away Red Mickey Head for game over to operate with ease
       redMickey = null;

       return;
     }

     // Check to see if player clicked on the orange Mickey head
     if (
       orangeMickey !== null &&
       orangeMickey.isClicked(mouseX, mouseY)
     ){

       // bring score back to zero
       score = 0;

       // Play sound effect indicating loss of score
       orangeSound.play();

       // Take out Mickey head right away
       orangeMickey = null;

       // get to scheduling the orange Mickey head's next pop-up moment
       scheduleOrangeMickey();

       return;
     }

     // Check to see if player clicked on the yellow Mickey head
     if (
       yellowMickey !== null &&
       yellowMickey.isClicked(mouseX, mouseY)
     ){

       // Award player with +30 points
       score += 30;

       // Play existing hit sound
       bonusSound.play();

       // Take away yellow Mickey head instantly
       yellowMickey = null;

       // Let cooldown (whose maximum is technically 3 minutes) begin
       scheduleYellowMickey();

       return;
     }
     
     //set-up of loop rountine regarding when a head gets hit/clicked
     for(let i = mickeys.length - 1; i >= 0; i--){

       if (mickeys[i].isClicked(mouseX, mouseY)){

         // Letting 5 points be added to scoreboard
         score += 5;

         // Letting "hit" sound effect be played
         hitSound.play();

         // Take away Mickey Head upon it being hit
         mickeys.splice(i, 1);

         // Quit checking the hit was landed and successful
         break;
         
       }
       
     }
     
   }
    
    
  }

class MickeyHead{

  // Making sure that the Mickey Heads are positioned
  constructor(x, y){
    this.x = x;
    this.y = y;
  }

  display(){

    // The 2 white Mickey ears set-up
    fill(255);
    circle(this.x - 25, this.y - 30, 35);
    circle(this.x + 25, this.y - 30, 35);

    // The cyan head of the Mickey head
    fill(0, 255, 255);
    circle(this.x, this.y, 65);
  }

  isClicked(mx, my){

    // Measurement of how far is the player from hitting the head
    let clickDistance = dist(mx, my, this.x, this.y);

    // Check to make sure the player indeed clicked inside of the head
    return clickDistance < 32.5;
    
  }
    
}

// Set-up of the red Mickey Head that borrows elements from the "MickeyHead" class function
class RedMickeyHead extends MickeyHead {

  display(){

    // Set-up of red Mickey head ears
    fill(255, 0, 0);
    circle(this.x - 25, this.y - 30, 35);
    circle(this.x + 25, this.y - 30, 35);

    // The circular head of the Mickey head
    fill(255, 0, 0);
    circle(this.x, this.y, 65);
  }
    
}

// Set-up of the yellow Mickey Head that borrows elements from the "MickeyHead" class function
class YellowMickeyHead extends MickeyHead{

  display(){
    // Set-up of yellow Mickey head ears
    fill(255, 225, 0);
    circle(this.x - 25, this.y - 30, 35);
    circle(this.x + 25, this.y - 30, 35);

    // Circular head of the Mickey head
    fill(255, 225, 0);
    circle(this.x, this.y, 65);
  }
  
}

// Set-up of the orange Mickey Head that borrows elements from the "MickeyHead" class function
class OrangeMickeyHead extends MickeyHead{

  display(){

    // Set-up of orange Mickey head ears
    fill(255, 140, 0);
    circle(this.x - 25, this.y - 30, 35);
    circle(this.x + 25, this.y - 30, 35);

    // Orange Mickey's circular head
    fill(255, 140, 0);
    circle(this.x, this.y, 65);
  }
  
}

If there was a part that I am proud of, it is definitely implementing the Mickey heads themselves, as well as the sound effects that accompany them! It is because of how I understood that most people like it when a game is as goofy as it possibly could be. It’s something about that hilarious energy that would cause people to continue playing for however long they need, especially if there’s sound effects resembling that of memes that almost anybody would recognize. Not to mention that I found myself laughing at the sound effects that each Mickey Head is accompanied with, which already proved to me that what I’m hearing is something that anybody would be delighted to hear as well. After all, a game is all about engagement and it can maybe be convincing for a user to keep on indulging in the humorous nature from within the game.

let menuSoundtrack;
let gameplayMusic;

menuSoundtrack = await loadSound("menuSoundtrack.mp3");
  gameplayMusic = await loadSound("gameplay_music.mp3");

 // Keep track of whether 6 seconds have finished being passed
    if(millis() - gameOverStartTime >= 6000){

      // Bring the player back to the main menu automatically
      gameState = "menu";

      playMenuMusic();

// Begin soundtrack after user interacts with menu
       userStartAudio();
       menuSoundtrack.play();
       

       

       gameState = "instructions";

 // Be on lookout for player clicking on red Mickey head
     if (
       redMickey !== null &&
       redMickey.isClicked(mouseX, mouseY)
     ){

       // have gameplay music stop immediately
       gameplayMusic.stop();
       
       // Switch over to game over
       gameState = "gameover";

       //Setting put of the memory of when the player lost
       gameOverStartTime = millis();

       // Play sad trombone at precise moment
       gameOverSound.play();

function playMenuMusic(){

    // Cease gameplay music if it's playing right now
    if (gameplayMusic.isPlaying()){
      gameplayMusic.stop();
    }

    // Start up menu music ONLY if it ain't playing at the moment
    if (!menuSoundtrack.isPlaying()){
      menuSoundtrack.loop();
    }
    
  }

  function playGameplayMusic(){

    // Cease menu music
    if (menuSoundtrack.isPlaying()){
      menuSoundtrack.stop();
    }

    // Start up gameplay music
    if (!gameplayMusic.isPlaying()){
      gameplayMusic.loop();
    }
    
  }

The biggest challenge for me, however, would definitely be the implementation of music in my game; this is often due to how even though I would have my downloaded mp3 songs uploaded within my project fair and square, the music would never seem to be activated. It was almost as if they weren’t recognized enough to even be played where I wanted them to be played in, despite me doing what I thought was correctly defining the variables/functions that would cause the music to be played (such as defining the function that was meant to play music during the gameplay as “gameplayMusic”).  This is when I knew that p5.js sure knew a thing or two about being strict with functions/variables and how to activate them properly. Which is why I have had to refer to some of the older example projects that were linked, as well as proofread my mistakes with the assistance of Ai so that I would see if I didn’t write something correctly or whatnot. Eventually, I was able to figure it out via switching certain commands with the ones that my program was able to read, such as how for my “gameplayMusic” variable, i was able to switch out “loop()” with “play()” so that it can understand better on where and how to play said-music. Before you know it, I was able to sort out an evolution of music within my game that would go as follows: main menu (no music) => instructions page (club music that is supposed to get people’s energy up and running for the game) = gameplay itself(another form of techno club music that will boost the already-risen energy of the player as they get to enjoying themselves in the game! It is these simple steps of build-up that I was able to debug and bring to life within my game.

All in all, I believe that despite the complicated strict lines I had to follow in order for p5.js to listen to me, this was quite the eventful project! Now, I still consider myself a giant personified rookie when it comes to game creation, but what I can say is that this may have given me the idea of what the whole process may technically be like. The only thing I wish I could have done is see if there could have been more Mickey heads that could have been added to the game to add even more value to the game. Sadly, I didn’t exactly have enough ideas, but at least four ideas are better than nothing, you know? Overall, these are nothing short but my thoughts of how the project was like for me.

Leave a Reply