QR Code Memory Game

About:
The QR Code Memory Game is an ongoing project created for the Town and Country Learning Center in southeast San Diego as a part of UCSD’s Global TIES program. Team page is here: http://globalties.ucsd.edu/tnc.html

Global TIES:
“is an innovative humanitarian engineering program of the Jacobs School of Engineering at the University of California, San Diego.  Global TIES puts multi-disciplinary teams of undergraduates to work building the dreams of not-for-profit organizations and their clients in San Diego and in developing countries around the world.  Renowned UC San Diego faculty and researchers advise the teams, and students receive course credit for their work.  Global TIES teams give students an invaluable opportunity to apply their skills in a real world setting, while learning firsthand the role that engineering and technology can play in solving the problems that face their local community and the world.  Not-for-profit organizations receive critically needed but often cost-prohibitive technical expertise to help them improve the lives of their clients.”

Town and Country Learning Center:
“The Town and Country Village Learning Center is a community organization located in Southeast San Diego. The Learning Center provides computing facilities, space for homework and study, and various other educational, afterschool activities such as cooking, dance, or gardening to children and young adults who live there.”

About QR Code Memory Game:
The game takes the idea of the normal memory game of concentration (the game where players place cards face down and flip two at a time to get matches) and expands it to use technology to get the client interested and curious about the technology. At the start of the quarter the game’s state was a very basic QR code scanner that just scanned QR codes and opened up the corresponding image in a browser.

By the end of the quarter we revamped the game to make a proper unified video game screen complete with scoring. To play the players layout cards we created with different QR codes on them. Each player takes turning choosing two cards and scanning them using the webcam mounted on a stand. When a card is scanned the corresponding image for each card is drawn onto the screen. The game then prompts the user with a message if a match was found or not, increments or decreases score accordingly and then prompts the next user to start. The game continues until all cards are matched.

My contributions:
-Support for multiple scenes (start menu, help screen, main game, custom)
-Helped with team to construct stand
-Support for two players
-Music and sound effects
-GUI and game drawing

Future Plans:
-Support for custom cards
-More than 2 Players
-High Score Table

Developer Details:
-Written in Processing language for use on PC

Screen Shots:

Game Screen:

Webcam Stand (cards placed into stand):

Source Code: 


import processing.video.*;
import pqrcode.*;
import ddf.minim.*;

/*
 QRcode reader
 Generate images from a QRcode generator such as
 http://qrcode.kaywa.com/ and put them in this sketch's
 data folder.
 Press spacebar to read from the camera, generate an image,
 and scan for barcodes.  Press f to read from a file and scan.
 Press s for camera settings.
 Created 9 June 2007
 by Tom Igoe / Daniel Shiffman
*/
 
//for audio
Minim minim;
AudioPlayer bgMusic;
AudioPlayer matchSound;
AudioPlayer notMatchSound;
AudioPlayer cameraClick;

Capture video;                                 // instance of the video capture library
String statusMsg = "Please place a card under the camera and press the spacebar!";// a string to return messages:
String isMatch = "";
String storedFirstCard = "0";
String storedSecondCard = "0";
String[] firstName, secondName;
int score = 0;
int score2 = 0;
String scoreReadout = "Player 1: ";
String scoreReadout2 = "Player 2: ";
String turnReadout = "It's Player ";
//strings for start screen
String startScreen1 = "QR CODE MEMORY GAME!";
String startScreen2 = "PUSH SPACE BAR TO BEGIN";

ArrayList<String> discard = new ArrayList<String>();

PImage bground; 

PImage card1 = new PImage();
PImage card2 = new PImage();

String decodedStr;

boolean displayBoth = false;
boolean displayFirst = false;

boolean turn1;
int turnNum = 0;
int gameState;
final int gameState_start = 0;  //start up screen
final int gameState_main = 1;   // main game
final int gameState_help = 2;   //help/how to play screen
final int gameState_custom = 3; //customization menu


// Decoder object from prdecoder library
Decoder decoder;

void setup() {
  size(750, 500);
  gameState = 0;
  bground  = loadImage("background.jpg");
  //background(0);
  video = new Capture(this, width-350, height-180, 30);
  // Create a decoder object
  decoder = new Decoder(this);

  // Create a font with the second font available to the system:
  PFont myFont = createFont(PFont.list()[2], 20);
  textFont(myFont);
  turn1 = true;
  
  //load audio
  minim = new Minim(this);
  //load audio file from data folder
  bgMusic = minim.loadFile("guile.mp3");
  matchSound = minim.loadFile("yay.wav");
  notMatchSound = minim.loadFile("sad.wav");
  cameraClick = minim.loadFile("camera_click.mp3");
  bgMusic.setGain(-8);
  cameraClick.setGain(8);
  matchSound.setGain(8);
  notMatchSound.setGain(8);
  bgMusic.play();
  bgMusic.loop(); 
}

// When the decoder object finishes
// this method will be invoked.
void decoderEvent(Decoder decoder) {

  try 
  {
    decodedStr = decoder.getDecodedString();
  }
  catch (IllegalStateException i)
  {
    decodedStr = "NO QRcode image found";
  }
  
  if (displayBoth)
  {
    displayFirst = false;
    displayBoth = false; 
  }
  //don't display cards at first
    
  if (decodedStr.equals("NO QRcode image found"))
    {
          statusMsg = "Could not read the card; please try again!";
          isMatch = "";
          
    }
  else if(storedFirstCard.equals("0")) {
          
          storedFirstCard = decodedStr;
                   
          firstName = storedFirstCard.split(" ");
          
          if (discard.contains(firstName[0] + firstName[2]))
          {
            statusMsg = "You have already scanned this card. Please select another!";
          } 
          else
          {
            card1 = loadImage(firstName[0].toLowerCase() + "_" + firstName[2].toLowerCase() + ".png");
            
            if (card1 != null)
            {
              cameraClick.play();
              statusMsg = "Your first card is the: " + storedFirstCard + ". Please scan another card!";        
              displayFirst = true;
            }
          }
  
          isMatch = "";

      }
   else {   
          storedSecondCard = decodedStr;
          //checkIfMatch(storedFirstCard, storedSecondCard);
          
          firstName = storedFirstCard.split(" ");
          secondName = storedSecondCard.split(" ");
          //link("http://processing.org");
          card2 = loadImage(secondName[0].toLowerCase() + "_" + secondName[2].toLowerCase() + ".png");      
          //open( "C:/Users/Rentao/Downloads/memoryGame/HTMLimagepages/" + firstName[0].toLowerCase() +"_"+ firstName[2].toLowerCase() +".html");
          
          if (card2 != null)
          {
            if (firstName[0].equals(secondName[0]) && firstName[2].equals(secondName[2]))
            {
               statusMsg = "You have already scanned this card! Please scan another."; 
               isMatch = "";
            }
            else if (discard.contains(secondName[0] + secondName[2]))
            {
               statusMsg = "You have already scanned this card! Please scan another."; 
               isMatch = "";
            }
            else
            {
              statusMsg = "Your second card is the: " + storedSecondCard;      
               
              discard.add(firstName[0] + firstName[2]);
              discard.add(secondName[0] + secondName[2]);
              
              if(turn1)
                score += 2;
              else
                score2 +=2;
                
              displayBoth = true;
              
              if(firstName[0].equals(secondName[0])) 
              {
                  isMatch="Nice job, you found a match!";
                  matchSound.play();
              }
              else 
              {
                  if (score > 0 && turn1)
                    score--;
                  else if(score2 > 0 && !turn1)
                    score2--;
                    
                  isMatch="Sorry, the cards do not match. Try again!";
                  notMatchSound.play();
              }
              
              storedFirstCard="0";
              storedSecondCard="0";
              turn1 = !turn1;
            }
          }
      }
    
  }
/**
void checkIfMatch(String storedFirstCard, String storedSecondCard) {
    if(storedFirstCard==storedSecondCard) {
        isMatch="do the cards match? YES!";
    }
    storedFirstCard="0";
    storedSecondCard="0";
}
*/

void captureEvent(Capture video) {
  video.read();
}

void draw() {
  switch(gameState)
  {
    case gameState_start:
      background(0);
      text(startScreen1, width/4, height/2);
      text(startScreen2, width/4, height/2 + 20);
      
    
    break;
    
    case gameState_main:
    background(bground);
    if(turn1)
      turnNum = 1;
    else
      turnNum = 2;
    scoreReadout = "Player 1 score: " + score;
    scoreReadout2 =  "Player 2 score: " + score2;
    turnReadout = "It's Player " + turnNum + "'s turn!";
    // Display video
    image(video, 0, 0);
    // Display status
    text(isMatch, 20, height-25);
    text(statusMsg, 20, height-50);
    text(scoreReadout2, 20, height-75);
    text(scoreReadout, 20, height-100);
    text(turnReadout, 20, height-125);
  
    if (card1 != null && displayFirst)
    {
         image(card1, 414, 105);
       
         if (card2 != null && displayBoth)
           image(card2, 581, 105);
    }
    
    break;
    
  }
  // If we are currently decoding
  //if (decoder.decoding()) {
    // Display the image being decoded
  //  PImage show = decoder.getImage();
  //  image(show,0,0,show.width/4,show.height/4); 
  //  statusMsg = "Decoding image";
  //  for (int i = 0; i < (frameCount/2) % 10; i++) statusMsg += ".";
  //}
  
}

void keyReleased() {
  String code = "";
  if(gameState == gameState_start)
  {
    switch (key) { 
      case ' ':
        bgMusic.pause();
        bgMusic = minim.loadFile("bgm2.mp3");
        bgMusic.setGain(-10);
        bgMusic.play();
        bgMusic.loop();
        gameState = 1;
        matchSound.play();
      break;  
    }
  }
  else if(gameState == gameState_main)
  {
    // Depending on which key is hit, do different things:
    switch (key) { 
      case ' ':        // Spacebar takes a picture and tests it:
        // copy it to the PImage savedFrame:
        PImage savedFrame = createImage(video.width,video.height,RGB);
        savedFrame.copy(video, 0,0,video.width,video.height,0,0,video.width,video.height);
        savedFrame.updatePixels();
 
        // Decode savedFrame
        decoder.decodeImage(savedFrame);
      break;
      
      case 'f':    // f runs a test on a file
        PImage preservedFrame = loadImage("qrcode.png");
        // Decode file
        decoder.decodeImage(preservedFrame);
      break;
      
      case 's':      // s opens the settings page for this capture device:
        video.settings();
      break;
     }
  }
  
}

void stop()
  {
   bgMusic.pause();
   bgMusic.close();
   minim.stop();
   super.stop();  
    
  }