Final Project Progress – Sarah & Ons

For this week, we decided to focus on the Processing side of things, since Arduino will only play a basic role in our project (control x and y positions using potentiometers).

research

When the two users’ paths meet on the screen in an area where they have something in common, a blooming effect gets triggered. (PS: Determining a commonality is done through comparing the answers to questions that are collected at the beginning of the program).
This is the part we needed to do the most research on. We wanted to get some inspiration to figure out what the blooming effect should look like, and how it should work.
We found two concepts that seemed interesting to us so far, and messed around with the code so that it would fit our project a bit better:

        • The first one draws fireworks, which we thought would be cool as it can represent a spark between the users if they have something in common. However, in the case that we end up choosing this effect, we are still trying to figure out how to make the fireworks stay displayed on the screen properly after the “animation” bit of it is over.

        • The second one is represented by the blooming of a flower.
          We added the option to add the number of “petals” so that we can see how different it would make the sketch look. We were also thinking of using this functionality to go along with how similar the answers of the users are.
          The possible answers for each of our questions are going to be: 1, 2, 3, 4, which signify how much you agree or disagree with the question. So we thought that the number of petals could increase the more the answers of the users are closer together.
          You can visualize this through this video:

What we got done

        • The first thing we wanted to make sure would work properly is the trace the users would leave when moving around the screen. In our final project, each user will have two potentiometers, one would control the x-position, the other will control the y-position, This allows them to move around and “draw” freely on the screen. Since hooking this up to Arduino will be fairly easy, for now, we are just using 4 keys on the keyboard to simulate this for each user.
          We wanted the current position of the user to be represented by a point that is fully opaque, and the trail it leaves behind it to be a bit more transparent.
          This took a lot of time for us to figure out but the solution turned out to be quite simple. All we needed to do was draw a black rectangle with the screen dimensions, that had a really low alpha value (3 in our case) each frame, i.e., in the draw loop.

        • The second thing we did was try to detect collisions between the traces of the two users. We did this by turning the screen into a grid. Every time a user crosses a square on the grid, a boolean for that user turns to True. If both the booleans for both users become True, then a collision is detected. For now, we are representing the collision by a big dot. But this will later be replaced by the blooming effect we mentioned earlier.
          To fix: One issue we still have with this, however, is that if the user’s path is between two squares of the grid (each half in a different square), two dots may be displayed to represent one collision. We are working on fixing this.

        • Finally, we also started working on our Question class. This will be a “template” for all of the questions we are going to include in the beginning of our program. We started to think about all of the variables that the class should have.
          Here is the current code for it, it’s just a start as most of our work on it was brainstorming and discussion:
class Question{
  //variables to store input answers
  int user1Answer;
  int user2Answer; 
  //question location (corresponding to area on screen)
  int x,y;
  //question box dimensions (it could be something that we design and upload to processing(?))
  int w,h; 
  
  String questionText; 
  
  boolean user1Answered;
  boolean user2Answered;
  
  boolean sameAnswer; 
  
  
  
  void displayQuestion(){
   rect(x,y,w,h);
   text(questionText,x+w/2, y+h/2);
   //rect();
   //rect();
   //rect();
   //rect();
  }
}

 

Don’t let it explode – Week 10

introduction

For this week’s assignment, the first thing I started thinking about was which elements to use from Arduino. I wanted Arduino to offer some sort of functionality that Processing couldn’t.
So, I decided to use a potentiometer that would allow you to rotate a line within a circle, by converting the potentiometer values into angles.
Yes, this can be done with just Processing, but I think that physically twisting a button and having the line rotate as you twist, is something you can’t replicate with the keyboard or mouse.

idea

The point of the game is to try and deactivate a bomb before it explodes.
A red arc shows up randomly on the screen of the bomb and you have to move the potentiometer to get the line inside that arc before a specific amount of time passes.
When you first run the game, you get around 5 seconds to deactivate the first arc. This is because it was taking some time at first to load everything. But after that, you have 2 seconds for each arc.
You would win/deactivate the bomb if you are able to deactivate 20 arcs. And, you would lose if 2 seconds go by before you are able to reach one of the arcs.
You can restart the game by clicking the mouse anywhere on the screen.

         

Here, you can see how the potentiometer controls the line:

Circuit

The wiring is really basic. All I used was a potentiometer.

code

This the Arduino Code:

//get value of potentiometer at where you want degree 180 to be
float at180=862; 
//get value of potentiometer at where you want degree 0 to be
float at0=144;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println('0'); //sending an ASCII version
  //println sends a string of ascii values
}

void loop() {
    
  if(Serial.available()>0)
  {
    float toAngle = ((at180-at0)/180);
    char inByte=Serial.read();
    //read the poteniometer value
    int sensor = analogRead(A0);
    //convert it to an angle
    float angle = (sensor - at0) / toAngle;
    delay(1);
    //send the angle value to processing
    Serial.println(String(angle)); 
  }
}

And this is the Processing code:

import processing.serial.*;
Serial myPort;
float pAngle;
PImage bomb;
int x =0;
int y =0;
int r = 170;
float angle, dist;
boolean done, lost, won;
color c;
int score;
int timer, timeleft, timestart,seconds;
int coolingTime;
float lineX, lineY;

void setup()
{
  size(420,750);
  //connection with arudino
  printArray(Serial.list());
  String portname=Serial.list()[1];
  println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  //variable initialization
  bomb = loadImage("b.png");
  lost = false;
  won = false;
  done = false;
  angle = random (-8*PI/7,PI/6); //radnom angle 
  dist = angle + PI/8; //another angle distant from the first one by PI/8 to make up the arc
  c = color(255,0,0);
  pAngle = radians(36); 
  score=0;
  timer = timeleft = 5500; // set timer for about 5 seconds the first time
  //because it takes some time to load the potentiometer
  //but then you get only 2 seconds
  timestart = millis();
  coolingTime =0; //variable used to pause a bit before switching to another random angle value
}

void draw(){
  //display once you lose
  if (lost) 
  {
    background(0);
    fill(255,0,0);
    textSize(30);
    textAlign(CENTER);
    text("YOU LOST!!", width/2,height/3 - 50);
    text("Bomb was not deactivated!!", width/2,height/3);
    textSize(20);
    fill(255);
    text("Remaining moves until deactivation: "+ str(20-score), width/2,height/2);
    textSize(20);
    text("Click anywhere to start again.", width/2,3*height/4);
    textSize(13);
    text("Make sure to reset your potentiometer at lowest angle", width/2,4*height/5);
    text("(which is at the far right) before restarting.", width/2,5*height/6);
  }
  //display once you won
  else if (won)
  {
    background(100);
    fill(0,255,0);
    textSize(50);
    textAlign(CENTER);
    text("BOMB", width/2,height/3);
    text("DEACTIVATED", width/2,height/3 + 60);
    fill(255);
    textSize(20);
    text("Click anywhere to start again.", width/2,3*height/4);
    textSize(13);
    text("Make sure to reset your potentiometer at lowest angle", width/2,4*height/5);
    text("(which is at the far right) before restarting.", width/2,5*height/6);
  }
  //display during the game
  else if (!won && !lost)
  {
    background(50);
    image(bomb,-2,4);
    translate(width/2,height/2);
    timeleft =  timer - (millis() - timestart);
    seconds = (timeleft/1000) % 60;
    //println(seconds);
    fill (255,190);
    rect(-200,-370,250,65);
    fill(0);
    textSize(20);
    textAlign(LEFT);
    text("Timer: "+ seconds, -185, -345);
    text("Moves to deativate: "+ str(20-score), -185, -320); 
    
    //drawing a circle and the arc
    strokeWeight(3);
    fill(255);
    ellipse(0,0,r,r);
    fill(c);
    arc(x, y, r, r, angle, dist);
    //drawing the line controlled by the potentiometer angle value
    lineX = cos(pAngle)*(r/2);
    lineY = sin(pAngle)*(r/2);
    line(0,0,lineX,lineY);
    //losing condition : you run out of time before reaching an arc
    if (seconds <= 0)
    {
      lost = true;
    }
    //winning condition : you manage to reach 20 arcs
    if (score == 20)
    {
      won = true;
    }
    //if you touch an arc before timer runs out
    if ((pAngle>angle)&&(pAngle<dist) && seconds>0){
      done = true;
      c= color(0,255,0); //change color to green
      coolingTime ++; //start incrementing coolingTime
      timer = timeleft = 2500; //restart the timer
      timestart = millis();
    }
    //this statement is used so that cooling time will still increment if you touch the arc
    //and then move away from it
    else if ( !((pAngle>angle)&&(pAngle<dist)) && coolingTime >= 1) {coolingTime++;}
    //println(coolingTime);
    //once the coolingTime is over
    if(coolingTime> 15)
    {
      score++; //increment score
      //get new random arc
      angle = random (-8*PI/7,PI/6); 
      dist = angle + PI/8;
      done = false;
      c= color(255,0,0); //change color of arc back to red
      coolingTime = 0; //reset coolingTime to 0
    }
  }
}
//restart the game and resee all the needed variables when mouse is clicked at one of the end screens
void mouseClicked()
{
  if (lost || won)
  {
    won = false;
    lost = false;
    timer = timeleft = 5500;
    timestart = millis();
    score=0;
    coolingTime = 0;
    done = false;
    angle = random (-8*PI/7,PI/6);
    dist = angle + PI/8;
    c = color(255,0,0);
  }
}

//getting the value from arduino
void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n'); //to know where it ends
  s=trim(s);
  if (s!=null){
    pAngle= int(s);
    pAngle = - radians(pAngle);
  }
  //println(pAngle);
  myPort.write("0"); //send something back to arduino to ask for next value
}

final outcome

If I had more time, I would have liked to add a buzzer, as well as a button on Arduino to restart the game instead of doing that with the mouse. I would have also liked to add a start-screen.
Something else I would like to fix is when generating a random arc, it shouldn’t be where the previous one was.

Anyway, this is the final outcome.

Keyboard/Theremin – Distance Measuring Sensor

introduction

When we were still working with processing, I made a keyboard simulator for one of my assignments. However, it wasn’t very practical. It was controlled with the arrow keys and space bar, meaning it took some time to travel between notes. So, for this week’s assignment, I decided to redo it with Arduino and a distance measuring sensor. A note will play based on the distance of an object/your hand from the sensor.

The sensor resulted in some noise, so I marked the exact spot where each note would sound decent, using lines on some papers, as shown here:

circuit And functionality

This is how the wiring goes:

As mentioned, I used a distance measuring sensor to (obviously) measure the distance of some object from the sensor. The range of the distance is limited (using constrain and map).
I also used a button. This is used to activate the sound when pressed. This is useful to control the length of a note since the note stops playing when the button is not pressed.
Finally, there is a buzzer which produces the sounds. It is only triggered when the button is pressed and when the object is within the distance range. It plays a specific note based on the distance from the sensor.

difficulties

The wiring for this assignment was quite straightforward. My main struggle this week was with the code. Even though it is brief, it took me a lot of time, trial, and error to figure out how to:

        • initialize the sensor
        • try to eliminate the noise as much as possible with the distance.

Even though I tried my best to eliminate the noise, I wasn’t able to fully do that, that’s why I marked the spot where the note would play clearly with a line for each note.

Code

#include "pitches.h"

int button = 2;
int sensorEcho = 3;
int sensorTrig = 4;
int buzzer = 5;
long distance;
long duration;
int buttonState;

int notes[8] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5};

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

void loop() {
  // put your main code here, to run repeatedly:
  buttonState = digitalRead(button);
  
  //initializing the distance measuring sensor
  digitalWrite(sensorTrig,HIGH);
  delayMicroseconds(1000);
  digitalWrite(sensorTrig, LOW);
  duration=pulseIn(sensorEcho, HIGH); 
  distance =constrain((duration/2)/29.1 ,4,81); //getting the distance value
  delay(10);
   
////  Serial.println(temp);
//
//  Serial.print(duration);
//  Serial.print(" , ");
//  Serial.println(distance);

  //if the button is not pressed or you are outside of the distance range
  if (buttonState == LOW || distance < 3 || distance > 80) 
  {
    noTone(buzzer); //the buzzer will not play anything
  }
  //if the button is pressed and you are within the distance range
  else if (buttonState == HIGH) 
  {
    int note = map(distance, 4, 81, 0, 8); //determine which note to play based on the distance
    tone(buzzer, notes[note]); //play the note
  }
}

//pitches.h
/*************************************************

 * 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

final outcome

This is the final outcome.
In the end, I tried (TRIED) to demonstrate how this works by playing a small snippet of a song. However, I’m pretty sure the song is only clear to me because I already know what it is (haha), let me know if you can tell what song it is.

 

Alarm System – Analog and Digital Sensors

I would like to start off by saying that I’m actually not very proud of my assignment this week. This is because, for the first time, I felt like I didn’t really go beyond what we did in class.
However, the reason for this is that I found out that I have not yet grasped what we did last week. Therefore, I took the time to watch the recorded lectures and read some online resources to fully understand the basics of input and output. This took up most of the time I had for this week’s assignment, so I decided to keep it simple and just use what I have learned to make sure that I now knew how to handle analog and digital input/output.

idea

Trying to incorporate everything that I have learned so far, I came up with this idea. I wanted to create some sort of alarm system, but with a visual signal (represented by a flashing LED) instead of sound.

In the circuit, there are 3 LEDs, 1 push button, and 1 photoresistor.
2 LEDs (green and red) are digital output, and the other LED (yellow) is an analog output.
Initially, the red and yellow LEDs are off. The green LED is on, meaning the alarm system hasn’t been triggered.
The closer you get to the photoresistor, the brighter the yellow LED lights up, signaling that something is getting close.
When you actually touch the photoresistor, the alarm system is triggered: the green LED turns off, the yellow LED is at its brightest, and the red LED starts to flash.
If you want to turn off the “alarm”, you press the button which turns the green LED back on and the red LED off.

NOTe

I also ended up adding a boolean alarmStopped that does the following:

        • If something touches the photoresistor but doesn’t move away and stays still, you can still shut off the alarm.
        • As soon as it moves and touches the photoresistor again, the alarm system is triggered once again.

circuit and code

int green = 2; //green LED
int red = 3; //red LED
bool redState = LOW;
int b = 4; //buton
bool prevBState = LOW; //to check previous button state
int yellow = 5; //yellow LED
int pr = A0; //photoresitor
bool alarm; //to check if alarm is triggered
bool alarmStopped; //to check if alarm has been stopped
//timer to make yellow LED flash
long timer;
int timerLength = 100;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(red,OUTPUT);
  pinMode(green,OUTPUT);
  pinMode(yellow,OUTPUT);
  pinMode(b,INPUT);
  pinMode(pr,INPUT);
  alarm = false;
  alarmStopped = false;
  digitalWrite(red,LOW);
  digitalWrite(green,HIGH);
  timer = millis();
}

void loop() {
  // put your main code here, to run repeatedly:
  //analog read photoresistor values
  int prValue = analogRead(pr); 
  //digitally read current button state
  int bState = digitalRead(b); 
  
  //Serial.println(prValue);
  
  //map the photoresistor value (from min 270 to max 640) to the LED needed value 
  //and constrain it between those values 0 and 255
  int mappedValue = 255- constrain(map(prValue,270,640,0,255),0,255);
  //light the yellow LED according to the photoresistor value
  analogWrite(yellow,mappedValue);
  
  //Serial.println(mappedValue);
  
  //in my case, the photoresistor has been touched when the LED value is around 150
  if (mappedValue > 150 && alarmStopped == false)
  {
    alarm = true;
  }
  //if alarm has been triggered, flash red LED and turn off green LED
  if (alarm == true)
  {
    if (millis()>timer){
      redState = !redState;
      timer = millis() + timerLength;
    }
    digitalWrite(red,redState);
    digitalWrite(green,LOW);
  }
  //if button has been pressed
  if (bState == HIGH && prevBState == LOW)
  {
    alarmStopped = true; //alarm system has been stopped
    alarm = false; //alarm is not currently triggered
    digitalWrite(red,LOW); //turn off red LED
    digitalWrite(green,HIGH); //turn green LED back on
  }
  prevBState = bState; //update the previous button state every frame
  //this is to allow the button to turn off the alarm
  //even if we're still touch the photoresistor
  //however the alarm will be triggered again if you move and touch the photoresistor once more 
  if (alarm == false && mappedValue< 150)
  {
    alarmStopped = false;
  }
}

 

Final outcome

Don’t drop the light – LEDs and Buttons

INTRODUCTION

For this week’s assignment, I decided to make some sort of game where you have to press a button before the light reaches the bottom.
The light goes through three colored LEDs before it does reaches the bottom (which is a common yellow LED).
As I said, you have to press the corresponding colored button to “stop” the light from falling. If the light does reach the bottom/yellow LED, all the other colored LEDs will light up at once signaling you lost.
I only did this with 2 buttons, 6 LEDs (3 for each button) + an extra common LED, because including 3 buttons made it way too messy and it wouldn’t fit. However, I do include a version with 3 buttons made on an online simulator at the end.
This is what the circuit looks like for the 2-button version:

code

I define all the pins before setup, including 2 arrays for the LED pins.
In setup(), I set the pins to OUTPUT for the LEDs, and INPUT for the buttons.
I then have two functions right before loop(). One is RED(), the other is BLUE(). They do the exact same thing, except that RED operates on the array of red LEDs, while BLUE operates on the array of blue LEDs.
Inside loop(), I just call these functions based on an if-statement.

int rowN;
int l1 = 2;
int l2  = 3;
int l3 = 4;
int l4 = 5;
int l5  = 6;
int l6 = 7;
int b1 = 8;
int b2 = 9;
int common = 10;
int checkCommon =0; //to check if the common LED is on
int lost = 0; //to check if you lost
int pressed1 = 0; //to check if red button is pressed
int pressed2 = 0; //to check if blue button is pressed
int row1[4] = {l1,l2,l3,common}; //array of red LEDs
int row2[4] = {l4,l5,l6,common}; //array of blue LEDs
void setup() {
  // put your setup code here, to run once:
  for (int i= 0; i<3; i++)
  {
    pinMode(row1[i],OUTPUT);
  }
  for (int i= 0; i<3; i++)
  {
    pinMode(row2[i],OUTPUT);
  }
  pinMode(b1,INPUT);
  pinMode(b2,INPUT);
  pinMode(common,OUTPUT);
  randomSeed(digitalRead(0));
  rowN = int(random(1,3));
}
void RED()
{
  if(pressed1 == 0)
  {
    for (int i= 0; i<4; i++)
    {
      checkCommon=0;
      if (digitalRead(b1) == HIGH)
      {
        pressed1 = 1;
        digitalWrite(row1[i],LOW);
        delay(500);
        break;
      }
      else
      {
        checkCommon =1;
      }
      delay(200);
      digitalWrite(row1[i],HIGH);
      delay(100);
      digitalWrite(row1[i],LOW);
      if (i==3 && checkCommon ==1)
      {
        delay(200);
        digitalWrite(common, LOW);
        lost = 1;
      } 
    }
  } 
  if (lost == 1)
  {
    for (int i= 0; i<3; i++)
    {
      digitalWrite(row1[i],HIGH);
      digitalWrite(row2[i],HIGH);
    }
    delay(500);
    for (int i= 0; i<3; i++)
    {
      digitalWrite(row1[i],LOW);
      digitalWrite(row2[i],LOW);
    }
    lost = 0;
  }
  if (pressed1 == 1)
  {
    pressed1 = 0; 
  }
}
void BLUE()
{
  if(pressed2 ==0)
  {
    for (int i= 0; i<4; i++)
    {
      checkCommon=0;
      if (digitalRead(b2) == HIGH)
      {
        pressed2 = 1;
        digitalWrite(row2[i],LOW);
        delay(500);
        break;
      }
      else
      {
        checkCommon =1;
      }
      delay(200);
      digitalWrite(row2[i],HIGH);
      delay(100);
      digitalWrite(row2[i],LOW);
      if (i==3 && checkCommon ==1)
      {
        delay(200);
        digitalWrite(common, LOW);
        lost = 1;
      }
    }
  }
  if (lost == 1)
  {
    for (int i= 0; i<3; i++)
    {
      digitalWrite(row1[i],HIGH);
      digitalWrite(row2[i],HIGH);
    }
    delay(500);
    for (int i= 0; i<3; i++)
    {
      digitalWrite(row1[i],LOW);
      digitalWrite(row2[i],LOW);
    }
    lost = 0;
  }
  if (pressed2 == 1)
  {
    pressed2 = 0;
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  //red case
  if (rowN == 1)
  {
    RED();
    rowN = int(random(1,3));
  }
  //blue case
  if (rowN == 2)
  {
    BLUE();
    rowN = int(random(1,3));
  }
}

Steps

First, I started out by making the LED arrays and randomly switching between them.

Then, I added the common yellow LED, and made all the other colored LEDs light up at once if the light reaches this common LED. This will mean you lost.

final outcome

Adding the push-buttons and their functionalities gave this final result:

3 buttons version

The assignment asked for at least 3 buttons and at least 3 LEDs, but since three buttons were too hard to fit on the breadboard in my case, I made a version with three buttons on an online simulator.
This is what the circuit looks like

And this is what the “gameplay” looks like:

 

 

Claustrophobia – Midterm

original game concept

My game is based on a nightmare I used to have when I was a child.
I’m very claustrophobic, so I used to have a recurring nightmare where I would be in a room with a cake. The cake would get progressively bigger until it left me with no space for myself. My only way out was to eat it.
This is what I wanted the gameplay to be.
I would be able to move around, jump, and crouch to avoid the cake’s hits (candles). If it gets me, it gets bigger. If I get close to it and press the spacebar, I can eat a small chunk out of it. The game will end either if the cake takes up the whole room, or if I eat it first.

Changes made/ Updated version

Due to time constraints, and many many details I was not expecting to cause issues within my program, I had to change the concept slightly, and give up one aspect of the game.
So now, this is the concept of my game:

        • It starts off similar. Me. Cake. In room.
        • The cake can still shoot at me and get bigger if it hits me.
        • I can still move around, jump and crouch to avoid hits.

HOWEVER, I gave up the part where I can eat the cake. So now, instead of eating it whole to win the game, another condition needs to be met:

        • I have to last 1 minute and 30 seconds without the cake taking up the whole space.
        • A timer is set up to take care of this.
        • The cake will take up the whole space when it hits me 14 times. So as long as I have lives remaining (initial lives = 14), and 1 minute and 30 seconds have passed, I win.

aesthetic of the game

The game is supposed to be a bit humorous, in the sense that it makes you think that this is going to be a horror game, dealing with claustrophobia and nightmares, while in reality, it is quite light and silly since it is a childhood nightmare of mine.

The vibe of the start screen is very creepy, accompanied by equally creepy music. But when you press play, the whole vibe changes. The images are a lot more colorful, and the music becomes fun and bouncy.



remaining issue

I ran into multiple bugs and issues while working on this assignment. Most were handled. However, there is one I can’t seem to fix.
When the cake size grows bigger, and the image in the code is updated to grow in size, the program begins to run slower the bigger the image gets.
I’ve tried many approaches to fixing this(img.resize(), scale(), …) but none seem to do the trick.
I kept the cake visualized by a rectangle for the game to be able to run smoothly, but I still intend to look for a way around this as I find myself very intrigued by what else could possibly be causing this and how to fix it.

classes and tabs

I have six tabs for this program. One for the main, 4 for classes (Game, Person, Hit, Cake), and one more tab containing the display functions for each screen (Start screen, Instructions, Winning screen, Losing screen).
I have illustrated and explained the main and basic ideas by commenting throughout my code.

This is the Main tab: 

import processing.sound.*;
SoundFile gamemusic,startmusic;
SoundFile pew, hit , scream,sleep;
PImage image1,image2,image3,bg,candle;
PImage creepy1, creepy2,title,title2,instruc,sleeping,screaming;
PImage[][] sprites;
Game g;
Person me;
Cake cake;
ArrayList<Hit> hits;
float ground ;
int timer, timeleft, timestart,seconds,minutes;
int temptime;

//variables to detect which screen we are on
int startMenu =1;
int howTo = 2;
int playGame = 3;
int gameState;

void setup() {
  size(1400, 800);
  gameState = startMenu; //start at start screen
  temptime = 90;
  timer = timeleft = 90000; // set timer for 1 minute 30 seconds
  timestart = 0;
  ground = height - 50;
  g = new Game(); //new instance of our whole game
  me = new Person(); //new person
  cake = new Cake(); //new cake
  hits = new ArrayList<Hit>(); //new hits array
  //load all images
  image1 = loadImage("me.png");
  sprites = new PImage[3][6]; 
  for (int y=0; y < 3; y++) {
    for (int x=0; x< 6; x++) {
      sprites[y][x] = image1.get(x*(image1.width/6), y*(image1.height/3), me.characterW, me.characterH);
    }
  }
  image2 = loadImage("me3.png");
  image3 = loadImage("cakee.png");
  candle = loadImage("candle.png");
  creepy1 = loadImage("creepy1.jpg");
  creepy2 = loadImage("creepy2.jpg");
  title = loadImage("title.png");
  title2 = loadImage("title2.png");
  instruc = loadImage("instruc.png");
  sleeping = loadImage("sleep2.jpg");
  screaming = loadImage("scream.jpg");
  bg = loadImage("bg1.jpg");
  //load all sounds, loop them and set their volumes to 0
  pew = new SoundFile (this,"pew.mp3");
  hit = new SoundFile (this,"gothit2.mp3");
  scream = new SoundFile (this,"scream.mp3");
  scream.loop(); scream.amp(0);
  gamemusic = new SoundFile (this,"gamemusic.mp3");
  gamemusic.loop(); gamemusic.amp(0);
  startmusic = new SoundFile (this,"startmusic.mp3");
  startmusic.loop(); startmusic.amp(0);
  sleep = new SoundFile (this,"sleep.mp3");
  sleep.loop(); sleep.amp(0);
}

void draw() {
  
  if (gameState == startMenu){
    startmusic.amp(1); //play start music
    image(creepy1,0,0);
    image(title, 200,140);
    image(title2, 400,300);
    displayStart(); //display start screen
    //make new instances of everthing to restart everything
    g = new Game(); 
    cake = new Cake();
    me = new Person();
    hits = new ArrayList<Hit>();
    gamemusic.amp(0); //mute other sounds
    scream.amp(0);
    sleep.amp(0);
  }
  else if (gameState == howTo){ 
    image(creepy2,0,0);
    image(instruc,500,100);
    displayHow(); //display instructions screen
    gamemusic.amp(0);
    scream.amp(0);
    sleep.amp(0);
    startmusic.amp(1); //play music for this screen
  }
  else if (gameState == playGame){
    if (g.won == false && g.lost == false) //if the game is still going
    {
      //display the game and play its music
      image(bg, 0, 0);
      gamemusic.amp(0.5);
      startmusic.amp(0);
      scream.amp(0);
      sleep.amp(0);
      temptime = minutes* 60 + seconds;
      g.coolingTime++;
      g.displayGame();
    }
    else if (g.lost == true) //if you lose, display lost screen
    {
      image(screaming,0,0);
      gamemusic.amp(0);
      startmusic.amp(0);
      sleep.amp(0);
      scream.amp(1);
      lostGame();
    } else if (g.won == true) //if you win display winning screen
    {
      image(sleeping,0,0);
      gamemusic.amp(0);
      startmusic.amp(0);
      scream.amp(0);
      sleep.amp(1);
      wonGame();
    }
  }

}
//mouse pressed functions to make the buttons that switch between screen
void mousePressed(){
  if (gameState == startMenu){
       if (475<mouseX && mouseX<475+450 && 620<mouseY && mouseY<620+100){
        gameState = howTo;
      }
  
      if (475<mouseX && mouseX<475+450 && 500<mouseY && mouseY <500+100){
        gameState = playGame;
        timestart = millis();
      }
  }
  else if (gameState == howTo){ 
      if (475<mouseX && mouseX<475+450 && 650<mouseY&& mouseY<650+100){
        gameState = startMenu;
      }
  }
  else if (gameState == playGame && (g.won || g.lost)){
    if (475<mouseX && mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      gameState= startMenu;
    }
  }
}

 

This is the Game Class:
This is where an instance of the actual gameplay is defined using all other instances from other classes. It only has a displayGame() method which combines everything that needs to be done when you press START.

class Game
{
  boolean won ;
  boolean lost;
  int coolingTime, hitsCount, biggerCount, lives;

  Game()
  {
    hitsCount = 0;
    biggerCount = 0;
    coolingTime = 50;
    won = false;
    lost = false;
  }

  void displayGame()
  {
    timeleft =  timer - (millis() - timestart);
    seconds = (timeleft/1000) % 60;
    minutes = (timeleft/1000) / 60;
    //game
    
    me.jump();
    me.move();
    me.drawPerson();
    //for each hit draw it, move it and check it it collides with character
    for (int i=0; i<hitsCount; i++)
    {
      hits.get(i).updateHit();
      hits.get(i).drawHit();
      if ((me.hitPerson(hits.get(i))== true)) //if there is a collision
      {
        hits.get(i).y=900; //make hit disappear from view
        cake.bigger(me); //make cake bigger
        biggerCount++; //add to this counter which gives us how many times you've been hit
        if (hit.isPlaying()) {
          hit.stop();
        }
        hit.play(); //play hit sound if not already playing
      }
      if (hits.get(i).y == 900 || hits.get(i).x<0) 
      {
        hits.remove(i); //to keep hits array small, remove any hit that is no longer needed
        // i.e if it disappeared from screen or if it already hit the character
        hitsCount--;  //and reduce this counter by 1
      }
    }


    if (coolingTime >= 100) { //add a new hit to hit array after some time
      hits.add( new Hit(random(550, ground-20)));
      hitsCount+=1; //increment hit counter
      if (pew.isPlaying()) { 
        pew.stop();
      }
      pew.play(); //play shooting sound "pew pew" if it's not already playing
      coolingTime = int(random(50));
    }
    //draw and move the cake
    cake.drawCake();
    cake.moveCake();
    if (biggerCount == 14) //detect loss
    {
      lost = true;
    }
    if (biggerCount <14 && seconds == 0 && minutes == 0) //detect win
    {
      won = true;
    }
    //display timer and number of remaining lives
    if(seconds <= 5 && minutes == 0) //make timer show up bigger in the center of the screen
    //when you have 5 minutes left
    {
      fill (255,220);
      rect(width/2-250,height/3-70,500,200);
      fill(0);
      textSize(70);
      textAlign(CENTER);
      lives = 14 - biggerCount;
      text("Timer: "+seconds, width/2, height/3+10);
      text("Lives: "+ str(lives), width/2, height/3+100);
    }
    else{
      fill (255,190);
      rect(10,10,220,100);
      fill(0);
      textSize(35);
      textAlign(LEFT);
      lives = 14 - biggerCount;
      if(seconds < 10)
       text("Timer: "+ minutes+":0"+seconds, 20, 50);
      else
        text("Timer: "+ minutes+":"+seconds, 20, 50);
      
      text("Lives: "+ str(lives), 42, 90);
    }
  }

}

 

This is the Cake class:
All methods related to cake are defined here : drawCake(), moveCake(), bigger() (the last one is to make the cake bigger).

class Cake
{
  float cakeW, cakeH;
  float cakeX, cakeY,speed;
  float s;
  Cake(){
    speed = 0.5;
    cakeW = 300;
    cakeH = 200;
    cakeX = width -cakeW - 50;
    cakeY = ground-cakeH;
    s=1;
  }
  
  void drawCake()
  {
    noStroke();
    fill(0,150);
    rect(cakeX,cakeY, cakeW, cakeH);
    //image(image3,cakeX,cakeY,cakeW, cakeH);
  }
  void moveCake() // to slightly move the cake to the left and right
  {
    cakeX -= speed;
    if (cakeX > width -cakeW - 25 || cakeX< width -cakeW - 75)
    {
      speed *= -1;
    } 
  }
  void bigger(Person thisMe) // to make the cake bigger everytime you are hit
  {
    //change size
    cakeH *= 1.1;
    cakeW *= 1.1;
    //push back x and y accordingly
    cakeX = width -cakeW - 50;
    cakeY = ground-cakeH;
    //upadte the x of the character to not let it be displayed afte the border of the cake
    float d =dist(thisMe.characterX,thisMe.characterY,cakeX,thisMe.characterY);
    if ( d< thisMe.characterW)
    {
      thisMe.characterX -= (thisMe.characterW -d) ;
    }

  }

}

 

This is the Hit class:
It has drawHit and updateHit methods.
An ArrayList of hits is defined in the Main Tab to contain all hits.

class Hit
{
  float x,y, speed;
  int w,h;
  
  Hit(float thisY)
  {
    w= 50;
    h = 6;
    //setting speed for every hit depending on timer
    if (90-temptime <= 20) {speed = 8;}
    else if (90-temptime > 20 && 90-temptime<= 30 ) {speed = 9;}
    else if (90-temptime > 30 && 90-temptime <= 40 ) {speed = 10;}
    else if (90-temptime > 40 && 90-temptime <= 50 ) {speed = 11;}
    else if (90-temptime > 50) {speed = 13;}
    x = cake.cakeX;
    y = thisY;
  }
  
  void drawHit()
  {
    fill(0);
    noStroke();
    //rect(x,y,w,h);
    image(candle,x,y,w,h);
  }
  
  void updateHit() //moving the hit
  {
    x-= speed; 
  }
}

 

This is the Person class:
It has 4 methods: jump , move, drawPerson, and hitPerson(this one is to check for collision with hits).

class Person
{
  int characterW;
  int characterH;
  int direction;
  int step;
  float xspeed;
  float yspeed;
  float characterX;
  float characterY ;
  boolean crouching; //boolean to check if player is crouching or not
  boolean jumping; //boolean to check if player is already jumping
  
  Person()
  {
    characterW=100;
    characterH=150;
    direction = 2;
    step = 0;
    xspeed= 5;
    yspeed = 0;
    characterX= 0;
    characterY= ground-characterH; 
    crouching = false;
    jumping = false;
  }
  
  void jump(){ //function to make character jump
    characterY += yspeed;
    
    if (characterY + characterH > ground) { //check if character is back on the ground
      //set variables to initial state
      characterY = ground - characterH; 
      yspeed = 0;
      jumping = false;
    }
    else { //if character is in the air
      yspeed ++; //bring him back to the ground (gravity)
    }
  }
  void move(){ //function to move the character with the arrow keys
    if (keyPressed && keyCode == RIGHT){
        direction = 0;
        if(characterX + characterW < cake.cakeX){
          characterX += xspeed;
        }
    }
    else if (keyPressed && keyCode == LEFT){
        direction=1;
        if (characterX > 0){
          characterX -= xspeed;
        }
    }
    else if (!keyPressed){
      direction = 2;
    }
    else if (keyPressed && keyCode == UP && !me.jumping) { // only jump if you're not already jumping
      me.yspeed = -15;
      me.jumping = true;
    }
     
    if (keyPressed && keyCode == DOWN){ //if crouching, update character height and y
      crouching = true;
      characterW=140;
      characterH = 93;
      characterY= ground-characterH; 
    }
    else {crouching = false; characterW = 100; characterH = 150;} //if not, reset them to original
    
    if (frameCount%xspeed==0) { //iterate through sprites
        step = (step+1) % 6;
    }
  }
  
  void drawPerson()
  {
    if (!crouching){
      //rect(characterX, characterY, characterW, characterH);
      image(sprites[direction][step],characterX,characterY);
    }
    else if(crouching) {
      //rect(characterX, characterY, characterW, characterH);
      image(image2,characterX,characterY);
    }
  }
  boolean hitPerson(Hit h) //checking collision with a hit
  {
    if((h.x>characterX +characterW) && dist(h.x+h.w/2,h.y+h.h/2,characterX+characterW/2,characterY+characterH/2) <= characterW/2+h.w/2)
    {
      return true; // means there is a collision from front or back
    }
    if((h.x<=characterX +characterW) && dist(h.x+h.w/2,h.y+h.h/2,characterX+characterW/2,characterY+characterH/2) <= characterH/2+h.h/2)
    {
      return true; // means there is a collision from top or bottom
    }
    else {return false;} // if not, there is no collision
  }
}

 

And finally! This is the tab for the screen display functions:

//function to display losing screen
void lostGame(){
  fill(255);
  textSize(30);
  textAlign(CENTER);
  text("OH NO! YOU LOST!", width/2,600);
  fill(255);
  stroke(0);
  strokeWeight(4);
  rect(475,650,450,100);
  fill(0);
  text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
  if (475<mouseX&& mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,650,450,100); 
      fill(255);
      text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
   }
}
//function to display winning screen
void wonGame(){
  fill(255);
  textSize(30);
  textAlign(CENTER);
  text("YAY! YOU WON!", width/2,600);
  fill(255);
  stroke(0);
  strokeWeight(4);
  rect(475,650,450,100);
  fill(0);
  text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
  if (475<mouseX&& mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,650,450,100); 
      fill(255);
      text("GO BACK TO MAIN MENU",475+450/2,650+100/2);
   }
}
//function to display start screen
void displayStart(){
  textSize(30);
  textAlign(CENTER);
  fill(64);
  stroke(0);
  strokeWeight(4);
  rect(475,500,450,100);
  rect(475,620,450,100);
  fill(0);
  text("START GAME",475+450/2,500+100/2);
  text("HOW TO PLAY",475+450/2,620+100/2);
  
   if (475<mouseX&& mouseX<475+450 && 500<mouseY&& mouseY<500+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,500,450,100); 
      fill(255);
      text("START GAME",475+450/2,500+100/2);
   }
                
   if (475<mouseX && mouseX<475+450 && 620<mouseY && mouseY<620+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,620,450,100);
      fill(255);
      text("HOW TO PLAY",475+450/2,620+100/2);
   }
}
//function to display instructions screen
void displayHow(){
  fill(255,200);
  noStroke();
  rect(300,200,800,400);
  fill(0);
  textSize(20);
  textAlign(CENTER);
  text("Well hello sleepy head, welcome to your nightmare.", width/2,240);
  text("In this room, you will face a terrible monster.", width/2,270);
  text("Avoid its hits. OR ELSE...", width/2,300);
  text("It'll get bigger and have you face your claustrophobia!", width/2,330);
  text("If you manage to survive for 90seconds, ", width/2,360);
  text("(which, as we know,is hours in the dream world)", width/2,390);
  text("the nightmare will be over, and you'll sleep peacefully.", width/2,420);
  text("If the monster takes over the entire room before the time is over", width/2,450);
  text("(by hitting you 14 TIMES), your sleep will be interrupted.", width/2,480);
  text("Try to win to stay peacefully asleep.", width/2,510);
  text("Be careful!! The hits get progressively faster with time.", width/2,540);
  text("Move, jump and crouch with the arrow keys. Good luck.", width/2,570);
  fill(64);
  stroke(0);
  strokeWeight(4);
  rect(475,650,450,100);
  fill(0);
  text("GO BACK",475+450/2,650+100/2);
  if (475<mouseX&& mouseX<475+450 && 650<mouseY&& mouseY<650+100){
      stroke(255);
      strokeWeight(4);
      fill(0);
      rect(475,650,450,100); 
      fill(255);
      text("GO BACK",475+450/2,650+100/2);
   }
}

Final game
(has a rectangle instead of cake image)

If you want to try the game out, here is the .zip file!
midtermGame

UPDATE

I fixed the image resizing problem with an off-screen buffer using PGraphics.
This is the working version of the game.
fixedGame

Midterm Progress – Oct 18th

It took me a while to come up with an idea for my midterm project. I started out by looking up games online in the hopes of getting inspired by one of them. But that got me nowhere.
So, I started to think about events from my personal life that I could possibly base my game on. That’s when I got the idea to make one of my dreams/nightmares the inspiration for the game.
Unfortunately, I’m not the type of person who remembers their dreams once they wake up. However, I used to have a recurring “nightmare” when I was a kid that I, luckily, still remember.
I’m very claustrophobic. So, I used to have a nightmare where I would be in a room with a cake. The cake would get progressively bigger until it left me with no space for myself. My only way out was to eat it.
This will be the concept of the game: Eat the cake or it will “eat” you. ( it will take up all your space)

game concept

The game would start out with a character (me) and a cake (mean huge cake) in a room.
The cake can shoot the character. Each time it is able to hit it, it will grow a little bit more in size.
The character can move around, jump, and crouch to avoid the cake’s hits.
It can also take a bite out of the cake every time it gets close enough to it, and presses a specific keyboard key.
If the cake fills up the entire room, I lose. If I am able to eat it first, I win.

progress

I haven’t had much time to get a lot done because of my busy schedule this week. However, I decided to start working on some basics individually and make sure they work smoothly, so that I can later easily incorporate them into my game.

So far:

      •  I made a start menu template with buttons allowing me to move between screens (start menu, instructions, and gameplay):

      • I made functions to move my character to the right and left, and to jump:

 

Drowning – Image manipulation

Since October the 10th is World Mental Health Day, I decided to make this week’s assignment somehow related to Depression.
As I was looking for inspiration, I came across this picture: I liked the scattered effect, so I decided to create something similar with Image pixels. After some brainstorming, I got the idea to represent the feeling of depression with a drowning animation and a wave crash effect for the pixels.

The Point class

To do this, I first thought about creating a Point class. Every pixel, of the image  used, would be represented by a point. The class includes a drawPoint() function that draws a point at the x and y positions of the pixel with the color of that pixel, and then updates its y-position to give a “bouncing” effect. There is a gravity variable that pulls down the point. When the point reaches the bottom of the screen(or a certain boundary), we multiply by -1 so that it goes back up. There is also slowDown variable to reduce the distance the point travels every time it goes back up or down.

Here is the code for this class:

class Point{
  color col;
  float x,y,vy, gravity, slowDown;
  
  Point(int x2,int y2,color col2){
    x= x2;
    y= y2;
    col= col2;
    vy = 0;
    
    gravity = 1;
    slowDown = random(0.5,0.7);
  }
  
  void drawPoint(){
    vy += gravity;
    y += vy;
    
    if (y>= height/1.5){
      y = height/1.5;
      vy *= -1;
      vy *= slowDown;
    }
    strokeWeight(2);
    stroke(col);
    point(x,y);
  
  }

}

Failed Attempts

At first, I tried to keep it simple. So, I wanted to iterate through the image’s pixel, and for every pixel, add a new Point to the array of points (pointsArray). Then, I would iterate through the points and draw them.
However, this made the pixels of the transparent background also get drawn, which resulted in there being too many pixels. This made the animation super slow and it didn’t give the effect I was looking for.
Here is how it looked like:

I didn’t know how to approach fixing this, so with a lot of research and help from online forums, I got the idea to create a new image. This image would be the “difference” (difference in the color of the pixels) between my image and a black image (color values are zeros). This gives us a new image that contains my image but with a black background. Then, all I had to do was iterate through the image pixels and only populate the pointsArray if the pixel isn’t black. (For this to work, make sure the image you’re using doesn’t have any black parts that you want to be drawn.)
However, this resulted in the image being still. This is what was displayed when I ran the program:

solution

To solve this, I turned to Google once again. Apparently, this was happening because the for loop (that makes the pixels of our new image) nested inside the draw() loop makes the points continuously draw in their initial spot because the new image is consistently being made.
To solve this, I again made the new image be the difference between my image and a black image, BUT THEN, it is turned into the difference between my image and itself. This means that the new image becomes fully black when the draw() is called for the second time, so it isn’t being considered anymore.

I also added a noise wave at the bottom using the code from an example on processing.org (https://processing.org/examples/noisewave.html)

Here is the code for that:

int nPixels;
int imgWidth = 480;
int imgHeight =240;
int[] toBlack, difference;
PImage img, differenceImage;
Point[] pointsArray;
int index;
float yoff = 0.0;


void setup(){
  size(700,700);
  //initializing all variables
  nPixels = imgWidth*imgHeight;
  pointsArray = new Point [nPixels];
  difference = new int[nPixels];
  toBlack = new int[nPixels];
  differenceImage = createImage (imgWidth,imgHeight,RGB); //create a new empty image to be filled with pixels
  img = loadImage("falling.png");
  img.loadPixels();
}
 
void draw(){
  background(51);
  
  for (int i = 0; i<nPixels; i++){
    color img_color = img.pixels[i];
    color toBlack_color = toBlack[i]; //here this array just has the values: 0
    //so the first time the difference is made, it only gets rid of the transparent background
    
    //setting the color variables for the pixels of the new image created
    int r =int(red(img_color)) - int(red(toBlack_color));
    int g =int(green(img_color)) - int(green(toBlack_color));
    int b =int(blue(img_color)) - int(blue(toBlack_color));

    difference[i] = color(r,g,b);
    toBlack[i] = img_color; //we set this to img_color so that from now on, the difference will always give a black pixel
    //and so these pixels won't be considered from now on
}
  //creating the difference image using the array of colors/pixels
  differenceImage.loadPixels();
  for (int i=0; i<img.pixels.length;i++){
    differenceImage.pixels[i] = difference[i];
  }
  //updating its pixels
  differenceImage.updatePixels();
  
  //populating the Point array with points only when the pixels aren't black!
  int index = 0;
  color col;
  for (int y=0; y<imgHeight;y++){
    for (int x=0; x<imgWidth; x++){
      if(differenceImage.get(x,y)>color(20)){ //needs to be not black(>0) but I put 10 just to be safe
        col = color(img.get(x,y));
        pointsArray[index] = new Point(int(map(x,0,imgWidth,0,width)),int(map(y,0,imgHeight,0,height/2)),col); 
      }
      index++;
    }
  }
  //drawing a point for each pixel and moving it up and down like a wave crash
  for (int i=0; i<pointsArray.length;i++){
    if (pointsArray[i]!=null){ //some of the points are empty because we skipped the index where the pixel is black
      pointsArray[i].drawPoint();
    }
  }
  //waves. code copied from an example on processing.org 
  fill(0,25,35);
  noStroke();
  beginShape();
  float xoff = 0;
  for (float x = 0; x <= width; x += 10) {
    float y = map(noise(xoff, yoff), 0, 1, 400,470);
    vertex(x, y);
    xoff += 0.05;
  }
  yoff += 0.01;
  vertex(width, height);
  vertex(0, height);
  endShape(CLOSE);

}

 

Final outcome

Transforming text – Geomerative

General Information

As I was working on this, I realized that I haven’t understood the Geomerative library as much as I thought I did, as I struggled a lot while using it.
Therefore, I wanted to make this week’s assignment, an opportunity for me to get a better grasp of most things that have to do with Geomerative and implement something quite basic.

I got the idea to make a program that displays words from a string one at a time, with a transition transforming each word into the one following it. (When we reach the final word of the string, it starts again from the first word.)
It turns out that this has been done a lot before, and to be honest, since I was still trying to learn about the Geomerative library, I ended up needing a lot of help and inspiration from the pre-existing codes I found online.
(PS: I didn’t copy any code. I used what I found to understand some of the main concepts of the program, but I made sure to code everything myself.)

The process

The first thing I did was use the geomerative_basic example, that Professor has provided us with, as my base. (It’s on GitHub if anyone was wondering.)
This program displays one string using ellipses.
In order to make it more suitable for what I wanted to create, I made a bunch of changes:

      • I turned everything into arrays (or array of arrays) so that it can be used on multiple strings instead of one.
      • Instead of using RGroup, I used RShape. This is because I needed access to each point individually so I needed to use getPoint() instead of getPoints(), which doesn’t work on RGroup.
      • Instead of drawing ellipses, I drew points. I made the number of points quite big so that it appears as a line which makes the transition effect look a bit cooler.
      • I added another array of arrays of RPoint called “changing_pnts” such that for every word, there are multiple elements:  changing_pnts[…], each containing the positions of all the points making up that word but at a different stage of the transition.
      • I added a function that populates changing_pnts. It has a role in changing each word into the next one and the last word back into the first one.

This is the code in the tab containing the function:

float temp1,temp2;

void populate_changing_pnts()
{
  //for each word
  for (int i=0; i<myStringArr.length; i++) 
  {
      //for the number of positions taken during the transition
      for (int j=0; j<num; j++) 
      {
          //for the number of points in this word (word of index i)
          for (int k=0; k<pnts[i].length; k++) 
          {
              //if we're at the last word, make the transition back to the first word
              if (i == myStringArr.length -1){
                temp1 = lerp( pnts[i][k].x, pnts[0][k].x, j*(1.0/num));
                temp2 = lerp( pnts[i][k].y, pnts[0][k].y, j*(1.0/num));
                changing_pnts[index][k] = new RPoint(temp1, temp2);
              }
              //if we're not at the last word, make the transition to the next word
              else {
                temp1 = lerp( pnts[i][k].x, pnts[i+1][k].x, j*(1.0/num));
                temp2 = lerp( pnts[i][k].y, pnts[i+1][k].y, j*(1.0/num));
                changing_pnts[index][k] = new RPoint(temp1, temp2);
              }  
          }
          index += 1; /*this is here because when the counter j goes back to 0, we want index to keep 
                      incrementing to reach the size of changing_pnts*/
      }
  }
  index = 0; //setting back index to 0
}

 

And this is the code in the main tab:

import geomerative.*;

RFont font;
int diameter = 15;
int pointNum = 2500; //this how many points we want the words to be made up of

String myString = "this wasn't as easy as it seemed in my head"; 
String[] myStringArr; //this is a string of strings that will contain all words from myString 
RPoint[][] pnts; //an array of arrays of points. For each word i, pnts[i] contains all points that form that word

RShape[] words; //an array of groups
float xOffset[]; //an array of xOffsets

int num = 60; //This is the number of positions that each point will go through while it's transitioning
              //This means it will move through 60 positions to get from its start to its destination

RPoint[][] changing_pnts; //an array of arrays of points.
                          //its size will be num * the size of the array of arrays pnts.
                          /*because for each word, there exist many elements changing_pnts[] 
                          each containing the positions of the points at a different stage of the transition*/

int index = 0; //this variable will be the one used to iterate through changing_pnts without a for loop


void setup() {
  size(640, 640);
  stroke(0);
  strokeWeight(4);
  
  RG.init(this);
  font = new RFont("Franklin Goth Ext Condensed.ttf", 200, RFont.CENTER);
  
  myStringArr = split(myString, ' ');
  
  //initializing all of the arrays according to the size of myStringArr
  pnts = new RPoint[myStringArr.length][pointNum];
  words = new RShape[myStringArr.length];
  xOffset = new float[myStringArr.length];
  changing_pnts = new RPoint[(myStringArr.length)*num][pointNum]; //size of this is = nums * size of pnts

  
  // load all strings from the myStringArr into geomerative
  for(int i=0; i<myStringArr.length; i++){
    words[i] = font.toShape(myStringArr[i]);
    //create the same number of points for each word
    for (int j=0; j<pointNum; j++){
      pnts[i][j]= words[i].getPoint(j*(1.0/pointNum));
    }

     //to set each word in the middle of the screen
     //get the size of the string from myStringArr susbtracted from the width of the screen
     //this is the amount left over
    xOffset[i] = width - words[i].getBottomRight().x - words[i].getBottomLeft().x;
    // divide that by two to center it
    xOffset[i] /= 2;
  }
  
  //this function fills changing_pnts with all the required points
  populate_changing_pnts(); 
}

void draw() {
  background(255);
  for (int j = 0; j< pointNum; j++) {
    float x = changing_pnts[index][j].x + xOffset[0];
    float y = changing_pnts[index][j].y + height/2;
    point(x, y);
  }
  index+= 1;
  if (index == (myStringArr.length)*num){
    index=0;
  }
  
}

final outcome

Piano – OOP

Control a “you” and release your “touch” to play some notes.
For this week’s assignment,  I used Object Oriented Programming to create a simple representation of a piano.

I was inspired by a similar concept I saw on Pinterest and I wanted to include the picture for inspiration credits but I couldn’t find it again!

Nevertheless, to do this, I  started off by making 3 classes in three different tabs. One for the piano, one for the person moving around (or “you”), and lastly one for the “touch” released by the person.

General Information

The Piano class is quite simple. It has a constructor and two functions.
The first function is just to draw the piano on the screen. While the second one is used when a piano key is pressed to make it grey and give it the illusion to have actually been pressed.
Here is the code for this:

class Piano{
  float pianoTop, pianoSide, keyWidth;
  
  //constructor
  Piano(){ 
  pianoTop = height/1.7;
  pianoSide = width/7;
  keyWidth = (width-(2*pianoSide))/13; //13 is the number of keys
  }
  
  //displaying the piano
  void drawPiano(){
    for (int i=0; i<13; i++){
      if (i==1 || i==3 || i==6 || i==8 || i==10){
        fill(0);
        stroke(0);
        strokeWeight(2);
        rect(pianoSide+i*keyWidth, pianoTop, keyWidth, (height-pianoTop)/1.4);
      }
      else{
        fill(255);
        stroke(0);
        strokeWeight(2);
        rect(pianoSide+ i*keyWidth, pianoTop, keyWidth, height-pianoTop);
      }
    }
  }
  //function that makes a pressed key grey 
  void pianoKeyPressed(int a){
    if (a==1 || a==3 || a==6 || a==8 || a==10){
      stroke(0);
      strokeWeight(2);
      fill(100);
      rect(pianoSide+a*keyWidth, pianoTop, keyWidth, (height-pianoTop)/1.4);
    }
    else{
      stroke(0);
      strokeWeight(2);
      fill(100);
      rect(pianoSide+ a*keyWidth, pianoTop, keyWidth, height-pianoTop);
    }
  
  }
  
}

Next, there is the You class. Here as well, there is a constructor along with three functions. The first one just displays an image/draws the “you”. The second one is to move it with the Left and Right arrow keys. And the last one is to return the x position of the You at a specific moment. This last function is needed for the touch. Since the touch is represented by a small circle that appears at the position of the person.
Here is the code for this class:

class You{
  float x, y, speed;
  int youWidth;
  float r, g, b;
  PImage img;
  
  //constructor
  You(){
    speed= 5;
    x= width/2;
    y= height/9;
    youWidth = 100;
    img = loadImage("square.png");
  }
  
  void displayYou(){
    image(img, x-youWidth/2, y, youWidth, youWidth);
    //rect(x-youWidth/2,y,youWidth,youWidth);
  }
  
  void controlYou(){
    if (keyPressed && keyCode == RIGHT){
      if(x < width - (piano.pianoSide)){
        x += speed;
      }
    }
    else if (keyPressed && keyCode == LEFT){
      if (x > piano.pianoSide){
        x -= speed;
      }
    }
  }
  
  float getYouPos(){
    return x;
  }


}

The final class is the Touch class. It has a constructor and two functions. The first function is used to display the touch at the position of the person when it is between the bottom of the person and the top border of the piano. And it also makes it disappear into the piano when it reaches the top border along with a small visual effect representing a touch.
(Note: each “touch” gets a random color.)
The second function is used to update the y-position of the touch.
This is the code for this class:

class Touch{
  float y, speed;
  int touchWidth;
  float r, g, b;
  boolean touchArrived;
  
  //constructor
  Touch(){
    speed = 5;
    y = height/9 + you.youWidth + touchWidth/2;
    touchWidth = 15;
    r = random(255);
    g = random(255);
    b = random(255);
    touchArrived = false;
    
  }
  
  void displayTouch(float pos){
    if (y <= piano.pianoTop){
      fill(r,g,b); //fill the touch with a random color
      noStroke();
      ellipse(pos,y,touchWidth,touchWidth);
    }
    //visual effect when the touch reaches the pressed key
    if (touchArrived){
      stroke(0);
      strokeWeight(3);
      line(pos, piano.pianoTop - 5, pos, piano.pianoTop-17);
      line(pos - 10, piano.pianoTop - 5, pos-20, piano.pianoTop-15);
      line(pos + 10, piano.pianoTop - 5, pos+20, piano.pianoTop-15);
    }
  }
  
  void moveTouch(){
    y += speed;
    touchArrived = y>= piano.pianoTop && y<(height-piano.pianoTop/2);
  }
  
}

In the main tab, instances of all of these classes were created. One for the piano, one for “you” and an ArrayList for all the “touches” that will be created throughout the program.
In draw() there is an if statement that will either display the Start Screen or the Piano Screen (the latter will be triggered when the mouse is clicked. This is done by a mouseClicked() function and a boolean variable.)
A keyPressed() function is also found in this main tab and it is responsible for creating a new Touch object every time the spacebar is pressed.
Finally, there is a function that checks no sound files overlap.
Here is the code for that:

//declaring global variables
import processing.sound.*;
SoundFile[] notes; //an array contaning all the sound files
boolean start;

Piano piano;
You you;
int touchCounter; 
ArrayList<Touch> touches; //an array for all the touches created
FloatList touchPositions; //an array for the x-position of each touch
float pos, temp1, temp2;



void setup(){
  size(1000,700);
  start=false;
  notes = new SoundFile[13];
  for(int i=0; i<13; i++){
    notes[i]= new SoundFile(this,"note"+str(i)+".mp3"); //adding all sound files to an array
  }
  piano= new Piano();
  you= new You();
  touches = new ArrayList<Touch>();
  touchPositions = new FloatList();
}


void draw(){
  background(255,243,249);
   
  //start screen
  if (!start){
    fill(0);
    textSize(20);
    textAlign(CENTER);
    text("Use the left and right arrow keys to move.",width/2 ,height/3);
    text("Press space to release your \"touch\" :)",width/2 ,height/2.7);
    text("Click to start playing!", width/2, height/1.8);
    textAlign(LEFT);
    textSize(15);
    text("(the piano.)", width/2, height/1.7);  
  }
  //when the mouse is clicked, the "game"(?) starts
  else{
    //draw piano
    piano.drawPiano();
    
    //drawing "you" and controlling it
    you.displayYou();
    you.controlYou();
    
    //drawing the touch and controlling it when the spacebar is pressed
    for (int i=0; i<touchCounter; i++){
      touches.get(i).displayTouch(touchPositions.get(i));
      touches.get(i).moveTouch();
      //adding the visual and sound effects when a note is played
      for (int j=0; j<13; j++){
          temp1 = piano.pianoSide+j*piano.keyWidth; //temporary value to find the start border of the pressed key
          temp2 = temp1 + piano.keyWidth; //temporary value to find the end border of the pressed key
          if (touchPositions.get(i)>temp1 && touchPositions.get(i)<temp2 && touches.get(i).touchArrived){
            piano.pianoKeyPressed(j);
            checkSilence(j);
            break;
          }
      }
    }
  }
}

//This is to check when the spacebar is pressed
//and add a new touch to the touches-array everytime
void keyPressed(){
  if (key==' '){
    touches.add(new Touch());
    pos = you.getYouPos(); //SAVE the value of the you position at the time the key is pressed
    //and add it to the positions FloatList
    touchPositions.append(pos);
    touchCounter+=1; //increasing the touchCounter to be ready for next value
  }
}

void mouseClicked(){
  start = true;
}

//function that makes sure no sounds are playing before playing the pressed one
void checkSilence(int index){
  for (int i=0; i<13; i++){
    if (notes[i].isPlaying()){
      notes[i].stop();
    }
  }
  notes[index].play();
}

Difficulties

While trying to get the program to work. I ran into three main difficulties.

  • The first one was that every time I press the space bar to create a new Touch, the previous one would disappear. To fix this, I used an ArrayList and stored every created Touch object there.
  • The second one was that, after pressing the spacebar and making a touch appear, moving the person moves the Touch objects along with it (because they use the x-position of the person as the parameter). So to fix this, every time the spacebar is pressed, I stored the x-position of the person in a temporary value and appended it to a FloatList (It ends up containing all Touch objects’ positions that can easily be accessed by indices.)
  • The third and final one was that the sounds made by pressing a piano key were overlapping, thus creating a lot of noise. To stop this, I included a function that lets a note be played only after it has stopped the previous one from playing.

All of these failed attempts can be visualized, respectively, in this video:

Final Outcome

Here is how it turned out in the end 🙂