Midterm Project – Memory Card Game

Concept:

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

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

Final Outcome:

Game Code:

Following is the code for the game:

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

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

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

Process:

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

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

previously made cards:

final card: 

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

frameRate(120);

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

Future Improvements:

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

Leave a Reply