Preliminary Concept for the Final Project

For the Intro to IM final project, I will be working with Susanne to build an integrated device utilizing Arduino and Processing.

💡 idea

The idea was inspired when Susanne mentioned “something visual about our connection or memory; something that relates us not being in the same location.” So, we decided to recreate the environment of logging into a Zoom meeting and entering the meeting’s password. The key feature of this project is to make the process accessible, by including both sounds and light.

🛠 how it will work

The password will be entered by the user using a computer keyboard to the graphical user interface, and during the typing process, different sounds and lights on Arduino will play. If the password combination is right, a congratulating tone will ring and the user will be let into the meeting.

⌨️ next steps
  • Finalizee the Processing’s role in the app
  • Decide what the Arduino will do
  • Discuss the feasibility of the project in class
  • Finalize the storytelling aspect of the app

 

Arduino + Processing Game

I had a really fun time exploring the serial communication between Arduino and Processing this week! My project was inspired by the R, G, B color mapping on Arduino buttons, and without further ado, let me explain what I did.

💡 idea

I wanted to figure out a way, in which I could use my Arduino as a joystick for the Processing game. I started off with some LEDs and a potentiometer, but then decided that I would like to try something different than what we did in class last Wednesday. After that, I wired three buttons on the breadboard, and the colors of the buttons immediately reminded me of the (R, G, B) color mapping. I decided to create a movement game and use my buttons as a joystick for moving whatever I have in my Processing console.

When a player starts the Processing sketch, they will see a rectangle on the console, and whenever each button is pressed, the rectangle changes color and moves to a particular side of the screen. The player’s score increments if the rectangle “hits” the side of the screen.

🛠 process

One of the main challenges while implementing this game was to figure out a way to transfer multiple serial communication bits into Processing. At first, I thought I would create an array of bits that occur while pressing the buttons. However, as I was exploring this idea it seemed like I was overcomplicating it, and then I just tried sending a different type of serial message each time a different button is pressed. For example, if the red button is pressed, I send “1”; if the blue one is pressed, I send “2”, and so on.

After reading this serial communication from my Processing, I created a boolean variable to track which button is pressed. For each boolean variable, I then created corresponding code lines to run throughout the game to move the rectangle. When the rectangle reaches the end of the screen, it reappears on the console, and the game continues until they eventually get tired of playing 😅. Right now, there is no way to stop or restart the game without closing the program, but it could be something to think about as a next step.

🎲 demo

Arduino code:

int buttonR = 2;
int buttonB = 4;
int buttonG = 7;
int prevRState = 0;
int prevGState = 0;
int prevBState = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(buttonR, INPUT);
  pinMode(buttonG, INPUT);
  pinMode(buttonB, INPUT);
  
  Serial.begin(9600);
}

void loop() {
    // getting digital communication from buttons
    int buttonRState = digitalRead(buttonR);
    int buttonGState = digitalRead(buttonG);
    int buttonBState = digitalRead(buttonB);
    
    // read if the buttons are pressed
    int valueR = digitalRead(buttonR);
    int valueG = digitalRead(buttonG);
    int valueB = digitalRead(buttonB);
    
    // output for debugging
    Serial.print(valueR);
    Serial.print(valueG);
    Serial.print(valueB);

    if (buttonRState == 1 && prevRState == LOW){
      // send values if buttons are pressed
      Serial.write(valueR);
    }

    if (buttonGState == 1 && prevGState == LOW){
      // send values if buttons are pressed
      Serial.write(valueG+1);
    }

    if (buttonBState == 1 && prevBState == LOW){
      // send values if buttons are pressed
      Serial.write(valueB+2);
    }
    
    // track button states
    prevRState = buttonRState;
    prevGState = buttonGState;
    prevBState = buttonBState;
}

Processing code:

import processing.serial.*; // importing from library
Serial port; // creating a serial port object

int numBtns = 3;  // number of buttons
int locX, locY;  // position of each rectangle
int rectW, rectH; // width and height of each rectangle
int value = 0; // passed value
boolean isRed, isGreen, isBlue = false; // keep track of colors
int numCollide; // keep track of collisions

void setup(){
  size(500, 500);
  rectMode(CENTER);
  locX = 250;
  locY = 250;
  rectW = 100;
  rectH = 100;
  numCollide = 0;
  //printArray(Serial.list()); // debugging
  port = new Serial(this, Serial.list()[1], 9600); // initializing the port
}

void draw(){
  background(255);
  
  // initializing the game
  rect(locX, locY, rectW, rectH);
  displayScore();
  
  // green mode
  if (isGreen == true){
    rect(8, 250, rectW-80, height);
    locX-=5;
  }
  
  // red mode
  if (isRed == true){
    rect(490, 250, rectW-80, height);
    locX+=5;
  }
  
  // blue mode
  if (isBlue == true){
    rect(250, 10, width, rectH-80);
    locY-=5;
  }
  
  // start again
  reStart();
}

void serialEvent(Serial port){
  // reading from the port
  value = port.read();
  // change to red
  if (value == 1){
    fill(255, 0, 0);
    isRed = true;
    isBlue = false;
    isGreen = false;
  }
  /// change to green
  if (value == 2){
    fill(0, 255, 0);
    isRed = false;
    isBlue = false;
    isGreen = true;
  }
  // change to blue
  if (value == 3){
    fill(0, 0, 255);
    isRed = false;
    isBlue = true;
    isGreen = false;
  }
}

// restart each time the rectangle is caught
void reStart(){
  if (locX==-rectW || locY == -rectH || locX == width+rectW){
    numCollide++;
    locX = 250;
    locY = 250;
  }
}

// display the instructions and the score
void displayScore(){
  textAlign(CENTER);
  push();
    textSize(16);
    fill(0);
    text("Press any arduino button to start!", width/2, height-100);
  pop();
  push();
    textSize(32);
    fill(0);
    text("Score: " + numCollide, width/2, height-50);
  pop();
}

 

Musical Instrument!

This week’s assignment was, by far, the most difficult one for me. Not because it required a lot of coding or wiring, but because pushing myself mentally to plan and execute something completely unfamiliar was tough! But I am proud that I did it, despite taking some time to make it work. So, let’s see what I got here.

💡 idea

The idea was to use a switch, an LED light, and a potentiometer to produce some kind of sound altogether to create a musical instrument.

🛠 process

After getting all of the circuit elements, I started to experiment with sounds. I wired my button and wrote the code so that every time I press it, one particular sound plays. Then, I wired a potentiometer and connected it to the LED through the code so that the lighting changes with the potentiometer values. I later added the sounds so that they begin to play whenever the LED is on. The whole system can be turned off by pressing the switch.

The primary challenge was to make sure that the tone() is producing clean notes and not just noise. I guess this depends more on the frequency and duration of the sound, but in the beginning, some of my sounds were like noise. I was able to overcome this by adding delay() and reducing the duration of each sound.

I was talking a bit about the mental challenge at the beginning of the post, and for me personally, it was believing in the idea that hardware and circuits can produce sound! I was struggling for about two days because I couldn’t come up with the idea, but then it finally hit me after some trial and error!

🎼 demo

#include "pitches.h"

int speakerPin = 4;
int ledPin = 2;
int button = 7;
// int photoPin = A0;
int potenPin = A2;
int prevState = 0;
boolean flag = false;
int notes[11] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_DS2, NOTE_E2, NOTE_F2, NOTE_FS2, NOTE_G2, NOTE_GS2, NOTE_A2, NOTE_AS2};
int durations[11];
int whichNote = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(ledPin, OUTPUT);
  pinMode(button, INPUT);
  Serial.begin(9600);
}

void loop() {
  // read button state
  int buttonState = digitalRead(button);

  // if button is pressed, run the program
  if (buttonState == 1 && prevState == LOW){
    flag = !flag;
    tone(speakerPin, notes[0], 300);
    delay(500);
  }

  if (flag){
    int ledValue = analogRead(potenPin);
    
    analogWrite(ledPin, ledValue/4);
    Serial.println(digitalRead(ledPin == HIGH));
    
    if(digitalRead(ledPin)){
      for (int i = 0; i<10; i++){
        tone(speakerPin, notes[i+1], 300);
        delay(200);
      }
    }    
  }
  else {
      pinMode(ledPin, LOW);
    }

  // track button state
  prevState = buttonState;
}

/*************************************************

 * Public Constants

 *************************************************/

#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
🎲 next steps

For now, the musical instrument is fairly simple, and the next steps would probably be adding some interactivity – perhaps adding different sounds as the potentiometer values change or adding a non-switch button to add more sounds.

Analog Input/Output Circuit

For this week, I had to visit a lot of external resources to finally grasp what is analog input/output and how is it different from digital input/output. Things finally started to click in my head after reading this definition on NYU ITP web page:

“While a digital input to a microcontroller can tell you about discrete changes in the physical world, such as whether the cat is on the mat, or the cat is off the mat, there are times when this is not enough. Sometimes you want to know how fat the cat on the mat is.” 

Honestly, why all textbooks/videos/websites can’t explain concepts in this simple manner? 😄

I really like how all of the hardware stuff we are building has applications in real world that I can immediately think of: electricity switches, temperature sensors, etc. etc. Not to say that software isn’t applicable — it is just easier for me to remember engineering applications that we encounter in real life than, say, games.

Anyways, I am a bit digressing here, so let’s get into my homework circuit.

Complete Circuit

⚡️ idea

The idea behind was inspired by the air conditioning and heating system inside different cars. Usually, there is one LED light that turns on if the heater is on and another LED light turns on if the AC is on. So I have used a temperature sensor for the analog input/output communication, two LED bulbs + one button for digital input/output.

🛠 process

I have read quite a bit of documentation and watched some LinkedIn learning videos to finally understand how Arduino analog works.

First off, I almost burned my temperature sensor, xD. After I plugged everything in and ran my script for the first time, I started smelling something weird — and oh boy, it was a temperature sensor! Gladly I was quick to find out and immediately plugged everything out. Turns out I messed up the leg-connection to the 5V and the ground, which has caused this whole trouble.

After a bit of debugging, I was able to connect and read the temperature values. By the way, none of this would have been implemented had I not referred to this documentation on voltage and temperatures. It was super confusing, but apparently we have to manipulate voltage values in order to get the most precise measurements of temperature!

🎥 result

The most fun part was making it all work inside the script:

int analogPin = A0;
float voltageOut;
float temp;
int analog;
int LED1 = 7;
int LED2 = 8;
int button = 4;
int buttonState = 0;
boolean flag = 0;
boolean prevState = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(button, INPUT);
  Serial.begin(9600);
  analogReference(DEFAULT); // specify that I have 5 volts
}

void loop() {
  // calculating temp
  analog = analogRead(analogPin);
  buttonState = digitalRead(button);
  temp = (((double)analog/1024)*5-0.5)*100;

  // track the values on serial
  Serial.print("Current Temperature: ");
  Serial.println(temp);
  delay(500);

  // if button is pressed, run the program
  if (buttonState == 1 && prevState == LOW){
    flag = !flag;
  }

  // if button is pressed, turn on
  if (flag){

    // "heater" is on
    if (temp >= 26){
    digitalWrite(LED1, HIGH);
    digitalWrite(LED2, LOW);
    }

    // "AC" is on
    else {
      digitalWrite(LED1, LOW);
      digitalWrite(LED2, HIGH);
    }

  }
  // turn off
  else {
    digitalWrite(LED1, LOW);
    digitalWrite(LED2, LOW);
  }

  // track button state
  prevState = buttonState;
}

Some of the challenges I faced was making a code run only when a button is pressed and turn everything off when the button is pressed the second time. I have spent about an hour trying to debug when I realized that I was missing the “else”-snippet when the flag is off. But now it works! 🥳

 

I am late but here it is! Midterm Update – fixed!

This is to let everyone know that – thanks to Aaron’s help last week – I managed to fix the scoring glitch that I had in my game!

My game logic was missing a lot of elements here and there, which resulted in a massive bug that crashed the whole game.

Fixing a few functions inside the main class and adding id tracking variables and array in mousePressed() helped to resolve this issue.

void mousePressed() {
  // play sound
  click.play();
  for (int i=0; i<cards.length; i++) {
    // only flip cards over if there are less than 2 flipped
    if (amountFlipped<2) {
      // only flip if the card hasn't been flipped
      // and also hasn't been matched (just to be safe)
      if (!cards[i].flipped && !cards[i].matched) {
        // check which card got clicked
        if (cards[i].checkClicked()) {
          // we've flipped it now so increase the amount that have been flipped
          amountFlipped++;
        }
      }
    }
  }
}

// check the IDs of the flipped cards
void checkIDs() {
  // make sure we have two flipped 
  // (already checked in mousePressed to make sure we're not counting matched cards)
  if (amountFlipped == 2) {
    // we need to keep track to make sure we check two cards
    // this index does that (we already made sure it's only two just above)
    int thisIndex = 0;
    for (int i=0; i<cards.length; i++) {
      // if the card is flipped, AND is NOT matched
      if (cards[i].flipped && !cards[i].matched) {
        // get that card's ID and its index
        ids[thisIndex] = cards[i].id;
        whichCard[thisIndex] = cards[i].index;
        // increment our local index
        thisIndex++;
      }
    }
    // if we have something for both cards (otherwise one would be -1)
    if (whichCard[0]>=0 && whichCard[1]>=0) {
      // if they match set to matched and increase score
      if (ids[0] == ids[1]) {
        cards[whichCard[0]].matched = true;
        cards[whichCard[1]].matched = true;
        score++;
        if (score == numCards/2){
           scene = 2;
        }
      } else { // otherwise trigger the timer and reset things back to -1
        cards[whichCard[0]].triggerTimer();
        cards[whichCard[1]].triggerTimer();
        ids = new int[]{-1, -1};
        whichCard = new int[]{-1, -1};
      }
      // always reset the amountflipped back to 0 so we can start again
      amountFlipped = 0;
    }
  }
}

 

Arduino LED Puzzle

3 hours ago I was sitting in front of my Arduino kit and was absolutely terrified: What on Earth do I do with all these lights and buttons?!!

After a couple of YouTube tutorials and going back to our class recording, I slowly figured out where all these wires go, how to connect a button, and an LED light.

Soo…I managed to get a circuit of 4 buttons and 3 LED lights (let’s call them B1, B2, and G):

  • button #0 does not trigger anything,
  • button #3 triggers two lights – B1 and G,
  • button #2 triggers B1,
  • button #1 triggers G.

If you may have noticed, B2 cannot be triggered by pressing on any of the single buttons, so there has to be some trick around this! Your task is to figure out which and trigger all three lights at the same time (preferably with a minimum number of buttons pressed).

The solution is to press button #0 and #1 simultaneously to trigger B2 and G, and then press button #3 to trigger B1.

Amina’s Arduino

Here is the code:

// declare pins and buttons
int ledPin1 = 2;
int buttonPin1 = 3;

int ledPin2 = 4;
int buttonPin2 = 5;

int ledPin3 = 7;
int buttonPin3 = 6;

int buttonPin4 = 8;

void setup() {
  // put your setup code here, to run once:
  pinMode(ledPin1, OUTPUT);
  pinMode(buttonPin1, INPUT);
  Serial.begin(9600);

  pinMode(ledPin2, OUTPUT);
  pinMode(buttonPin2, INPUT);
  Serial.begin(9600);
}

void loop() {
  // green light
  int buttonState1 = digitalRead(buttonPin1);
  Serial.println(buttonState1);
  digitalWrite(ledPin2, buttonState1); // HIGH = 1 or true

  // green light
  int buttonState2 = digitalRead(buttonPin2);
  Serial.println(buttonState2);
  digitalWrite(ledPin2, buttonState2);

  // blue light
  int buttonState3 = digitalRead(buttonPin3);
  Serial.println(buttonState3);
  digitalWrite(ledPin3, buttonState3);

  // blue and green
  int buttonState4 = digitalRead(buttonPin4);
  if (buttonState4 == buttonState1)
    digitalWrite(ledPin1, buttonState4);
}

Midterm Project – Card Memory Game

Idea

When I was a child, I used to play this memory game that comes by default on every Windows 7 machine:

Memory Game, Windows

Hence the inspiration for my project.

Process

October 13, 2020.

Today marks the first day of my *official* work on the midterm project. I came up with an idea last week – the Card Memory Game. For now, I am doing some UI research to see what type of interface I would like to create for my staring page. I haven’t started my work yet, but I know for sure that I want to include some buttons and sounds. Today I also implemented a function for my button to check if it is being hovered over:

// check if the mouse is over the button
  boolean isHover(){
    if(mouseX <= rectX+rectWidth/2 
      && mouseX >= rectX - rectWidth/2
      && mouseY <= rectY + rectHeight/2
      && mouseY >= rectY - rectHeight/2)
      return true;
     else
       return false;
  }

October 14, 2020.

Keeping up the consistency! Worked on the Welcome UI scene, included some text instruction placeholders, built the button, and figured out how to move between the scenes (has some bugs, will improve next time).

October 16, 2020.

Fixed the function to switch between the scenes, designed the cards and made the cover for them. Created a function to load the card images into the array and display them in the second scene.

Making cards in Figma. Emoji design credits: imagiLabs.com

October 17, 2020

D e b u g g i n g

Debugging improper card deck display

<…>

October 26th, 2020.

Well, a lot of time has passed since my last update, but I was fixing the code here and there in the meanwhile. A few challenges during those few days was fixing the score to display on the screen properly (which I got fixed, yay!), displaying and shuffling the card deck properly (resolved!), storing the card flips (not resolved), and working out the complete logic of the game.

Final Notes

I believe it is worth mentioning that I got tremendous help along the way from Professor Aaron (thank you!), Professor Discord and Professor Google (xD). There were many things that I had to look up online, for example, the list shuffle method.

Does my program work perfectly now? No. Did I learn something new in the meanwhile? YES. Before embarking on this assignment, I had no clue how to switch between the game scenes, use lists and manipulate Java arrays dynamically. While my game still cannot count the score and flip the cards the way it is supposed to, I have learned that asking for help and needing some time to build a good code is normal and does not equal failing.

Demo

I have done the majority of the code, but have a tiny hidden bug somewhere that crashes the whole game. I suspect that happens somewhere between passing each card’s value and comparing selected cards, but I will investigate this further and am determined to make it work! Unfortunately, this might not happen on time for the showcase, but it will happen in the next few steps. For now, enjoy the code & demo:

// v0.4 24.10.2020
// importing sound
  import processing.sound.*;
  SoundFile click;

// v0.3 21.10.2020
// moving everything into class
  int margin = 3;
  IntList imageIndex = new IntList();
  int[] flippedCard;

// v0.2 17.10.2020
// Outputting the card deck

// v0.1 16.10.2020
// Creating different scenes + images

  // set the scene
  int scene, score;
  int numCards = 20;
  //PImage[] cards = new PImage[numCards];
  Card[] cards = new Card[numCards];
  PImage[] images = new PImage[numCards];
  PImage cardCover;

// v0.1 14.10.2020
// Creating the text instructions

  PFont font;
  String instructions_1, instructions_2, instructions_3, instructions_4, btn_start, btn_quit;
  int fontSize = 50;

// v0. 13.10.2020
// Creating a button

  // defining colors as [r,g,b] arrays
  int[] pink = {242, 170, 224};
  int[] green = {51, 225, 142};
  int[] orange = {248, 163, 15};
  int[] blue = {103, 212, 239};
  int[] yellow = {255, 244, 108};
  
  // defining shape coordinates
  float rectX, rectY, rectWidth, rectHeight;
  
  void setup(){
    size(1100, 800);
    rectMode(CENTER);
    click = new SoundFile(this, "clickSound.wav"); 
    scene = 0;
    
    // setting the button coordinates
    rectX = width/2;
    rectY = height/2;
    rectWidth = 170;
    rectHeight = 80;
    
    // setting the text
    push();
      fontSize = 40;
      instructions_1 = "Welcome!";
      instructions_2 = "You are presented a deck of cards.";
      instructions_3 = "Your task is to flip and match every one of them.";
    pop();
    
    btn_start = "START";
    btn_quit = "QUIT";
    font = createFont("VT323-Regular", fontSize);
    textFont(font);
    
    // setting flipped cards to null
    flippedCard = new int[2];
    score = 0;
    
    // load cards once
    loadCards();
  }
  
  void draw(){
    background(green[0], green[1], green[2]);
    // display scenes
    if (scene == 0){
      displayMenu();
    }
    if (scene == 1){
      playScene();
      //displayScore();
    }
    if (scene == 2){
      playWin();
    }
  }
    
  // display the UI scene
  void displayMenu(){
    
    // create a button
    push();
      noStroke();
      if(isHover())
        fill(yellow[0]-20,yellow[1]-20,yellow[2]-20);
      else
        fill(yellow[0],yellow[1],yellow[2]);
      rect(rectX, rectY, rectWidth, rectHeight);
    pop();
    
    // place instructions
    textAlign(CENTER);
    fill(0);
    text(instructions_1, width/2, height/6);
    text(instructions_2, width/2, height/6+80);
    text(instructions_3, width/2, height/6+120);
    text(btn_start, width/2, height/2+12.5);
    push();
      textSize(22);
      text("Intro to IM, Fall 2020", width/2, 3*height/4);
    pop();
  }
  
  // check if the mouse is over the button
  boolean isHover(){
    if(mouseX <= rectX+rectWidth/2 
      && mouseX >= rectX - rectWidth/2
      && mouseY <= rectY + rectHeight/2
      && mouseY >= rectY - rectHeight/2 && mousePressed){
      scene = 1;
      return true;
    }
     else
       return false;
  }
  
  // display scene 1
  void playScene(){
    background(blue[0], blue[1], blue[2]);
    
    // show the cards
    for (int i =0; i<numCards; i++){
      cards[i].displayCards();
    }
    push();
      textSize(60);
      text("Score: " + score, width-200, height/2);
    pop();
  }
  
  // load cards
  void loadCards(){
    
    // store all the cards in the image array
    for (int i = 0; i < numCards; i ++) {
      images[i] = loadImage(i+".png");
      imageIndex.append(i);
    }
    
    // initialize counters
    int counter = 0;
    int index;
    
    // initialize a deck matrix
    for (int i = 0; i < 5; i++) { //i = x
      for (int j = 0; j < 4; j++) {//j = y
        index = i + j * 5; // width is 5
        cards[index] = new Card(i*(margin+images[i].width), j*(margin+images[i].height), images[imageIndex.get(counter)], index);
        counter++;
      }
    }

    // shuffle images
    imageIndex.shuffle(); 
  }
  
  // flip the cards
  void mousePressed(){
    
     // add sound
     click.play();
    
     for (int i = 0; i<numCards; i++){
       cards[i].flipCard();

        if (flippedCard[0] == -1){
          flippedCard[0] = cards[i].value;
        }
        
        else{
          flippedCard[1] = cards[i].value;
        }
        // compare flipped cards
        if (flippedCard[0] != -1 && flippedCard[1] != -1){
          if (flippedCard[0] == (flippedCard[1] - 10) || flippedCard[0] == (flippedCard[1] + 10)){
            score++;
             // check for win
             if (score == numCards/2){
               scene = 2;
            }
          // flip cards back if they don't match
          else {
            cards[i].isFlipped = false;
          }
          // reset the storage array
            flippedCard[0] = -1;
            flippedCard[1] = -1;
          }
        }
      }
    }
  
  // display the win scene
  void playWin(){
     background(pink[0], pink[1], pink[2]);
     text("You have matched them all!", width/2, height/2-200);
     push();
      noStroke();
      if(isHover())
        fill(yellow[0]-20,yellow[1]-20,yellow[2]-20);
      else
        fill(yellow[0],yellow[1],yellow[2]);
      rect(rectX, rectY, 2*rectWidth, rectHeight);
    pop();
    text("Play Again?", rectX, rectY+10);
    score = 0;
    // reset the cards
    for (int i = 0; i < numCards; i++){
      cards[i].isFlipped = false;
    }
  }

class Card {
  PImage card;
  PImage cover;
  int locX, locY, value;
  int cardWidth = 135;
  int cardHeight = 200;
  boolean isFlipped = false;
  int numFlipped = 0;
  boolean isMatched = false;
  
  Card(int _locX, int _locY, PImage _card, int _value){
    locX = _locX;
    locY = _locY;
    card = _card;
    cover = loadImage("cover.png");
    value = _value;
  }
  
  void displayCards() {
    // display cover if not flipped
    if (!isFlipped && !isMatched){
      image(cover, locX, locY);
    }
    else {
      image(card, locX, locY);
    }
  }
  
  // debugging
  void displayValue(){
    print(value);
  }
  
  void flipCard(){
    if(mouseX <= locX+card.width 
      && mouseX >= locX
      && mouseY <= locY + card.height
      && mouseY >= locY){
       isFlipped = !isFlipped;
       
       // count every time the card is flipped
       numFlipped++;
       
       // reset the counter when 2 cards are selected
       if (numFlipped == 3){
         isFlipped = !isFlipped;
         numFlipped = 0;
       }
    }
  }
}

Zip file: amina_midterm

I tried to make a Mario game?

Prologue

Since the start of the class, I have been really struggling with practicing Processing consistently – I would usually start my homeworks 1-2 days in advance and not practice much outside of class hours. But this week is very special: I started working on the weekly assignment 4 days in advance and have even taken some time to practice geomerative* and come up with the midterm idea! 🥳 So here I am, typing this post on a Saturday afternoon (edit: publishing on Sunday though). Enjoy!

Introduction

Fascinated by last week’s spritesheet example, I could not resist using this idea for my assignment. General idea was to make a small game with a moving character catching objects – diamonds in this case. After browsing the web, I have downloaded this pixelated Mario (?) image and cropped it in Photoshop as it wasn’t a PNG.

Mario Spritesheet

Process

Milestone 0: learning how to use the spritesheet. I have re-visited the class example and have carefully studied it to understand what is going on behind the code. And especially this – confusing at first – part:

// loops through each step and returns to image 0 after reaching image 3
    if(frameCount%speed==0){
      step = (step+1)%col;
    }

Milestone 1: placing the diamond and building a collision detection function. It took me a good day (coding on and off) to figure out what is going on wrong with my collision detection function. Oh, I have repeated the same mistake as I did when building the OOP assignment! 😅 Good thing now I finally know how to detect collision between two rectangular objects:

// Mario right corner > diamond left AND Mario left corner < diamond right
  if ((posX + person.width/16) > diamondX && posX < (diamondX + diamond.width/4) && (posY+person.height/16) > diamondY && posY < (diamondY + diamond.height/4)){

Milestone 2: collision detection debugging  – spritesheet’s width and height. OK, the function was built, BUT it didn’t work out the way I wanted it to. I took all of the creative debugging ways I had in mind and just started printing out line by line what the function is doing…After a few coordinate manipulations, it FINALLY hit me that I am uploading my spritesheet in the original size and then I manually resize the image without providing the new size coordinates to my collision function. So, after fixing the sizes I thought that the problem was fixed…Not quite!

trying to detect the right coordinates

Milestone 3: The spritesheet’s dimensions aren’t perfect! 🤦‍♀️😅 After fixing the sizes in Processing I actually went to the original image to see what might be wrong – and there it was: there were a lot of blank spaces between spritesheet’s movements that counted towards the current image’s width and height. Yay, now I finally understand what is happening in my program!

Milestone 4: Beautifying the program. After the main logic of the program was done, I decided to add some functions, such as counting and displaying the score, checking for win, and checking borders for Mario’s movement around the screen.

Epilogue 

I actually never thought that building small games can be this fun and … actually simple? Definitely am proud for working consistently on this throughout the week and can also see some room for improvement (see milestone 3 and demo – there is a moment when Mario catches a diamond despite being a few pixels away from it xD)

Result

Code:

PImage diamond;
PImage person;
PImage[][] movement;
PImage background;
PFont font;

int direction = 1;
int step, score = 0;
int posX, posY;
int diamondX, diamondY;
int speed = 5;
int row = 4;
int col = 4;
int currentWidth, currentHeight;
String textScore = "Score: ";

void setup(){
  
  // output window
  size (600, 600);
  
  // define font
  font = createFont("VT323-Regular", 40);
  textFont(font);
  
  // get images
  diamond = loadImage("diamond.png");
  person = loadImage("person.png");
  background = loadImage("background.png");
  
  // place each direction in 2-dimensional array
  movement = new PImage[row][col];
  
  // divide each step according to each step's dimenstions
  currentWidth = person.width/row;
  currentHeight = person.height/col;
  
  // place each step into the right location
  for(int posY = 0; posY < 4; posY++){
    for(int posX = 0; posX < 4; posX++){
      movement[posY][posX] = person.get(posX*currentWidth, posY*currentHeight, currentWidth, currentHeight);
    }
  }
  
  // always start with these coordinates
  posX = 0;
  posY = height-person.width/8+50;
  
  // dimanod location
  diamondX = (int)random(width-diamond.width-100);
  diamondY = (int)random(height-diamond.height-100);
  
}

void draw(){
  
  background(255);
  image(background, 0, 0, width, height);
  
  // place the character onto the board
  image(movement[direction][step], posX, posY, width/4, height/4);
  
  // move the character
  movePerson();
  checkBorder();
  
  // place the diamond
  image(diamond, diamondX, diamondY, diamond.width/4, diamond.height/4);
  
  // check for collision
  detectDiamond();
  
  // display score:
  displayScore();
  
  // check for win
  checkWin();
}

// move the character
void movePerson(){
  
  // activate if key is pressed
  if(keyPressed){
    
    // move DOWN
    if(keyCode == DOWN){
      // select the corresponding row
      direction = 0;
      // animate the steps to go DOWN in the y-dir (increase)
      posY+=speed;
    }
    
    // move UP
    if(keyCode == UP){
      // select the corresponding row
      direction = 1;
      // animate the steps to go UP in the y-dir (decrease)
      posY-=speed;
    }
    
    // move LEFT
    if(keyCode == LEFT){
      // select the corresponding row
      direction = 2;
      // animate the steps to go LEFT in the x-dir (decrease)
      posX-=speed;
    }
    
    // move RIGHT
    if(keyCode == RIGHT){
      // select the corresponding row
      direction = 3;
      // animate the steps to go RIGHT in the x-dir (increase)
      posX+=speed;
    }
    
    // loops through each step and returns to image 0 after reaching image 3
    if(frameCount%speed==0){
      step = (step+1)%col;
    }
  }
}

// needs some bug fixing
void checkBorder() {
  
  // return from the left
  if (posX >= width) {
    posX = -currentWidth;
  }
  
  // return from the right
  if (posX+currentWidth < 0) {
    posX = width;
  }
  
  // return from the bottom
  if (posY+currentHeight < 0) {
    posY = height;
  }
  
  // return from the top
  if (posY > height) {
    posY = -currentHeight;
  }
}

// check for the image collision (diamond collection)
void detectDiamond(){
  
  // Mario right corner > diamond left AND Mario left corner < diamond right
  if ((posX + person.width/16) > diamondX && posX < (diamondX + diamond.width/4) && (posY+person.height/16) > diamondY && posY < (diamondY + diamond.height/4)){
 
    // increment the score
    score++;
    
    // replace diamond
    diamondX = (int)random(width-diamond.width-100);
    diamondY = (int)random(height-diamond.height-100);
  }
}


// display the score
void displayScore(){
  push();
    fill(0);
    text("Score: " + score, width-180, 40);
  pop();
}

void checkWin(){
  if (score == 5) {
    background(103, 212, 239);
    push();
      textAlign(CENTER);
      fill(random(255), random(255), random(255));
      textSize(100);
      text("You WON!", width/2, height/2);
    pop();
  }
}

 

Displaying Text

For this assignment, I chose to display the text in the Processing window.

My initial idea was to create a string of letters that would appear in the window and move in some interesting way. After watching some YouTube tutorials and browsing the Processing forum, my first attempt to create the letter-objects that would appear in a typewriter effect, move around some arbitrary curve and then “find home” miserably failed (I am still not too sure why, but my best-educated guess is because I had a hard time with passing polar coordinates and translating them to regular coordinates).

From the second try, my goal was to create a string of letters that would be placed around the circle and would move around it if the mouse is pressed. To add some fun to it, I decided to place another string inside the circle, and make it change the size in a noise-like pattern, also when the mouse is pressed. This appeared to be more realistic and feasible with my skills, so I just went for implementing this with my code.

The most challenging part appeared to be the math: finding the arclength, placing each letter and making sure the spacing is right, and doing the whole polar coordinates thing.

Here’s the code:

String myText = "hello, I am...";
String anotherText = "Movement!";
PFont font;
float radius = 100;
float x, arclength, angle, move, t;

void setup(){
  size (340, 680);
  font = createFont("Calm", 64);
  textFont(font);
  textAlign(CENTER);
  smooth();
}

void draw(){
  
  background(0);
  
  x = 10;
  arclength = 0;
  
  circles();
  
  // Place the bigger circle
  translate(width/2, height/2);
  noStroke();
  ellipse(0, 0, radius*2, radius*2);
  
  push();
    fill(0);
    textSize(50+t);
    text(anotherText, 0, x);
  pop();
  
  for(int i=0; i<myText.length(); i++){
    
     // Place the next letter
    arclength += textWidth(myText.charAt(i))/2;
    angle = 5*PI/4 + arclength/radius + move;
    
    push();
      translate(radius*cos(angle), radius*sin(angle));
      rotate(angle+PI/2);
      text(myText.charAt(i), 0, 0);
    pop();
    
    // Place the next letter
    arclength += textWidth(myText.charAt(i))/2;
  
  // Move the string if the mouse is pressed
    if (mousePressed){
      move += 0.0082;
      t += random(-0.5,0.5);
    }
  }

}

// Make a background of color-changing circles
void circles(){
  for (int i = 0; i < 50; i++) {
    push();
      fill(random(200,250), random(200,250), random(200,250));
      ellipse(random(width), random(height), radius/10, radius/10);
    pop();  
  }
}

And the demo:

I Ran Out of Creative Titles – Amina’s Game

After watching some of the Coding Train’s (Dan Shiffman) YouTube tutorials on OOP, I took my inspiration for the game from this purple rain. Except that instead of the falling droplets I decided to make falling balls of random colors, and the whole trick of the game is to catch all of them with a net.

I started by creating a class for the ball object, along with the functions to initialize the objects, make them fall, create a net, and check if the ball is caught by the net.

One of the things that I was not too sure about is whether I should have had my net as a separate class or a part of the Ball class. I chose to go with the latter, because it allowed me to have functions inside of one class that checked the position and the catch:

void makeNet() {
    fill(0);
    rectMode(CENTER);
    rect(posX, posY, netWidth, radiusX);
  }
  
  void checkNet() {
    if (locX + radiusX >= posX 
    && locX + radiusX <= posX + netWidth 
    && locY + radiusY == posY) {
      locX = -200;
      score++;
    }
  }

My second challenge was to connect the keyPressed() function to the class for the net to move. I was a bit confused about whether to make it a part of the class functions or let it be a function outside the class, so I started to experiment. Being a part of the Ball class, the function did not work properly when I pressed the necessary keys, so I let it be outside the class. However, that chunk of code still seems a bit inefficient to me so far:

void keyPressed() {
   if (keyPressed) {
     
    for(int i = 0; i < balls.length; i++){
      if (key == 'a'){
        balls[i].posX -= 100;
      }
      
      if (key == 'd'){
        balls[i].posX += 100;
      }
     }
   }
}

One of my main intentions for this game was to display the score, which is calculated based on the number of balls caught, on the console. However, I still could not figure out the best way to do it:

print(balls[i].score);

There is room for improvement in this code, so I hope to figure out by the end of the week!

// Declare the object class
class Ball {
  float radiusX, radiusY, locX, locY;
  color ballColor;
  int score;
  float posX, posY;
  float netWidth = 250;
  
  // Constructor 
  Ball () {
    radiusX = 50;
    radiusY = 50;
    locX = random(10, width-10);
    locY = random(height);
    ballColor = color(random(255), random(255), random(255));
    posX = width/2;
    posY = height-10;
  }
  
  // Make the object
  void makeBall() {
    fill(ballColor);
    stroke(1);
    ellipse(locX, locY, radiusX, radiusY);
  }
  
  // Updating location of the object
  void falling() {
    locY += 1;
    
    if (locY > height+radiusY/2) {
      locY = -radiusY*2;
    }
  }
  
  // Make the net
   void makeNet() {
    fill(0);
    rectMode(CENTER);
    rect(posX, posY, netWidth, radiusX);
  }
  
  // Check if the object is in the net
  void checkNet() {
    if (locX + radiusX >= posX 
    && locX + radiusX <= posX + netWidth 
    && locY + radiusY == posY) {
      locX = -200;
      score++;
    }
  }  
}


// Initialize an array of objects
Ball[] balls;



void setup(){
  size(1280, 720);
  // Put objects in the array
  balls = new Ball[10];
  for(int i = 0; i < balls.length; i++){
    balls[i] = new Ball();
  }
}

// Display and run the game
void draw(){
  background(255);
  for(int i = 0; i < balls.length; i++){
     balls[i].makeBall();
     balls[i].falling();
     balls[i].makeNet();
     balls[i].checkNet();
     print(balls[i].score);
  }
}

// Move the net if the keys are pressed
void keyPressed() {
   if (keyPressed) {
     
    for(int i = 0; i < balls.length; i++){
      if (key == 'a'){
        balls[i].posX -= 100;
      }
      
      if (key == 'd'){
        balls[i].posX += 100;
      }
     }
   }
}

You can watch the demo here: