Final: Night at the AB

An Interactive Story: Night at the AB

Idea:

Vera and I created an interactive story call Night at the AB. We chose the Academic Building as the setting of our story since we wanted to cater to our audience, the NYUSH community. The final idea we settled on came a long way. We first started out wanting to make a candy machine, where you would have to perform different sorts of exercises to complete the process. But then Professor Rudi challenged us to make idea even more interactive. Changes after changes, Vera and I landed on the idea of an interactive story. We decided the user will have to perform various movements connected to the storyline to continue through the story. This particularly reminded me of how when I was younger, when my mother told me bedtime stories she would often fall asleep before the story was finished since she was so exhausted from work. I would then have to shake her to wake her up.

 

General Process:

The story narrative, is you’re waking up in the practice room, and you’re not exactly sure what’s going on. You try opening the door but it’s lock. One of surprises we came upon during filming was the amount of special effects we were able to do. We started filming after 10:00 since at that time most students had already left. We then realized with the hallway/room lights off it added a lot onto the ambiance of the story. We were also able to turn on and off the lights to create a scary story/light flashing effect. The filming proved difficult since we ultimately wanted to film everything in one go to make it for editing later on, and this took several tries but then we finally succeeded.

We filmed everything with a borrowed GoPro. The idea to film everything from a first person’s perspective was to make the story more immersive for the user. We looked at how the 2012 Thriller/Drama film Chroniclewas filmed, which was from a first-person perspective, filmed by one of characters.

We knew the video alone would be too confusing for the users, but we also didn’t want to take away the integrity of the mystery with too many instructions. So then we came up with the idea of having an unknown person walking you through the story, telling you what you have to do to make it through.

The general outline of the story is you will wake up in the room and then the first movement will require you to “break the lock” which then allows you to move into the hallway. After moving into the hallway, the user will have to stay still (the yoga stance). After about five seconds of this you can then continue onto the elevator. At the elevator scene Vera and I wanted to implement a real-life problem we all face every day. So, while in the elevator, you know the person on the phone is trying to tell you something, but you’re unable to understand due to the poor signal. Then after you arrive on the second floor you finally find out the thing you’re trying to run away from is actually ON the second floor. The person then tells you to run down to the first floor through the stairs. The next movement is when we edited the “it” into the clip, which then requires the user perform a punching motion, as if they’re actually punching “it.” If the user successfully do so then they will proceed to the stairs scene where the user will have to simulate running motions with their arms to continue down the stairs.

We weren’t exactly sure if the users would’ve still had trouble understanding what they are supposed to do or not. To make sure this isn’t an issue, Vera and I also created a little stick figure side animation for when the movements were required so the users can look at the corner of the screen to see what they’re supposed to do!

 

 

The ending, personally, I think is the scariest part, often receiving the best reactions. Since it’s an interactive story, and most stories have a laid-out storyline, this one ends with the user dying since they trip at the very end. Then presumably from the video, the user can infer “it” got to them through the scream.

 

Hardware:

The hardware is relatively simple; however, we ended up running into a lot of issues. The hardware is essentially a “fitness/movement/acceleration” tracker, which is an accelerometer instead of an enclosed box that goes on your wrist like a watch. The accelerometer tracks the user’s movements through acceleration, which is read by Arduino and then the information is relaying to Processing.

 

Hardware Issues:

Initially, Vera and I wanted to make everything Bluetooth, so the wires won’t restrict the user’s movements. However, after hours and hours of just simply trying to connect the Bluetooth, we figured out it wasn’t feasible with our project given time constraint and our knowledge. We also had a lot of hardware issues, in the end we actually went through six Arduino boards. And it took a long time for us to find an Arduino board that worked with the accelerometer and read and print the right values in Arduino. It was A LOT of trial and error, but in the end, we succeeded!

Code:

The Arduino code we referenced a sample code, reading more than one values from the accelerometer. The trickier part came when we had to send the values over to Processing. Personally, before the final project I had only dealt with sending one value from Arduino to Processing. The main skeleton for the Processing code is a collection of “if” statements. Therefore, we had the video pause and if the user performs the movement accordingly, the if statement will be true and then they can continue on in the story.

 

Coding Issues:

We had several coding issues, such as translating the right values from Arduino to Processing. Luckily for Vera and I, Jung Hyun and Tristen were there to help us through all of the issues we had. One of the main things I learned with this project in particular to Processing code is how to receive and interpret several values.

 

Reflection:

Personally, I think Vera and I achieved most of the things we set out to do. And seeing everyone’s reactions throughout journey of the story was extremely rewarding! One thing I think we could’ve done better was to have a better balance of movements in the story and the parts where the user only has to listen to the story. If we would’ve had more movements throughout the storyline, even just small ones here and there, I think we could’ve kept the users a lot more engaged with the story.

 

User Testing:

During the IMA show we received a lot of helpful feedbacks from everyone. One of the biggest thing was everyone wishing there were more movements required for the user as the story went on. Someone also suggested the possibility of a story tree. A story tree being that the user will have choices. If this one specific action is performed then the story will continue one way, and if another is performed or not performed then the story will go another way. I thought this was extremely interesting and definitely something to do if we weren’t restricted by time. The availability of choices would’ve also been a great way to make sure the user is immersed in the story.

import processing.sound.*;
import processing.video.*;
import processing.serial.*;
SoundFile bgMusic;
Movie scene1;
Movie lockHit1;
Movie lockHit2;
Movie lockHit3;
Movie lockHit4;
Movie lockHit5;
Movie stayStill;
Movie elevator;
Movie punch;
Movie runStairs;
int THRESHOLD = -150; 

String myString = null;
Serial myPort;

int NUM_OF_VALUES = 3;   /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
// This is the number of values you are receiving. 
int sensorValues[] = {0, 0, 0};
int stayStillCount = 0;

int xPos;
int yPos;
int zPos;

int stageScene = 0;

void setup() {
  //size(1280, 720);
  fullScreen();
  frameRate(24);
  setupSerial();
  bgMusic = new SoundFile(this, "Ambient BG Music.mp3");
  bgMusic.play();
  scene1 = new Movie(this, "Scene 1 w anim.mp4");
  lockHit1 = new Movie(this, "lock hit 1.mp4");
  lockHit2 = new Movie(this, "lock hit 2.mp4");
  lockHit3 = new Movie(this, "lock hit 3.mp4");
  lockHit4 = new Movie(this, "lock hit 4.mp4");
  lockHit5 = new Movie(this, "lock hit 5.mp4");
  stayStill = new Movie(this, "stay still.mp4");
  elevator = new Movie(this, "elevator final.mp4");
  punch = new Movie(this, "punch w anim.mp4");
  runStairs = new Movie(this, "run stairs.mp4");
  scene1.play();
}

void draw() {
  println(stageScene);
  updateSerial();
  printArray(sensorValues);

  xPos = int(map(sensorValues[0], -256, 256, 0, 100));
  yPos = int(map(sensorValues[1], -256, 256, 0, 100));
  zPos = int(map(sensorValues[2], -256, 256, 0, 100));

  if (stageScene == 0) {
    image(scene1, 0, 0);
    if (scene1.time() >= 74.5) {
      stageScene ++;
      scene1.pause();
    }
  }
  if (stageScene == 1) {
    image(scene1, 0, 0);
    if (sensorValues[0] < -THRESHOLD || sensorValues[1] <-THRESHOLD) {
      stageScene ++;
      scene1.stop();
      lockHit1.play();
    }
  }
  if (stageScene == 2) {
    image(lockHit1, 0, 0);
    if (lockHit1.time()>=0.5) {
      stageScene ++;
      lockHit1.pause();
    }
  }
  if (stageScene == 3) {
    image(lockHit1, 0, 0);
    if (sensorValues[0] < -THRESHOLD || sensorValues[1] <-THRESHOLD) {
      stageScene ++;
      lockHit1.stop();
      lockHit2.play();
      println("ok");
    } else {
      println("nope");
    }
  }
  if (stageScene == 4) {
    image(lockHit2, 0, 0);
    if (lockHit2.time()>=1.5) {
      stageScene ++;
      lockHit2.pause();
    }
  }
  if (stageScene == 5) {
    image(lockHit2, 0, 0);
    if (sensorValues[0] < -THRESHOLD || sensorValues[1] <-THRESHOLD) {
      stageScene ++;
      lockHit2.stop();
      lockHit3.play();
    }
  }
  if (stageScene == 6) {
    image(lockHit3, 0, 0);
    if (lockHit3.time()>=1.2) {
      stageScene ++;
      lockHit3.pause();
    }
  }
  if (stageScene == 7) {
    image(lockHit3, 0, 0);
    if (sensorValues[0] < -THRESHOLD || sensorValues[1] <-THRESHOLD) {
      stageScene ++;
      lockHit3.stop();
      lockHit4.play();
    }
  }
  if (stageScene == 8) {
    image(lockHit4, 0, 0);
    if (lockHit4.time()>=0.5) {
      stageScene ++;
      lockHit4.pause();
    }
  }
  if (stageScene == 9) {
    image(lockHit4, 0, 0);
    if (sensorValues[0] < -THRESHOLD || sensorValues[1] <-THRESHOLD) {
      stageScene ++;
      lockHit4.stop();
      lockHit5.play();
    }
  }
  if (stageScene == 10) {
    image(lockHit5, 0, 0);
    if (lockHit5.time()>=0.5) {
      stageScene ++;
      lockHit5.stop();
      stayStill.play();
    }
  }
  if (stageScene == 11) {
    image(stayStill, 0, 0);
    if (stayStill.time()>=8.5) {
      stageScene ++;
      stayStill.pause();
    }
  }
  if (stageScene == 12) {
    image(stayStill, 0, 0);
    int scenethershold = 20;
    if ((sensorValues[0] >= sensorValues[0] - scenethershold & sensorValues[0] <= sensorValues[0] + scenethershold) || (sensorValues[1] >= sensorValues[1] - scenethershold & sensorValues[1] <= sensorValues[1] + scenethershold)
      || (sensorValues[2] >= sensorValues[2] - scenethershold & sensorValues[2] < sensorValues[2] + scenethershold)) {
      stayStillCount += 1;
    }
    if (stayStillCount > 300) {
      stageScene ++;
      stayStill.stop();
      elevator.play();
    }
  }
  if (stageScene == 13) {
    image(elevator, 0, 0);
    if (elevator.time()>=120.5) {
      stageScene ++;
      elevator.pause();
    }
  }
  if (stageScene == 14) {
    image(elevator, 0, 0);
    if (sensorValues[0] < -THRESHOLD || sensorValues[1] <-THRESHOLD) {
      stageScene ++;
      elevator.stop();
      punch.play();
    }
  }
  if (stageScene == 15) {
    image(punch, 0, 0);
    if (punch.time()>=9.5) {
      stageScene ++;
      punch.pause();
    }
  }
  if (stageScene == 16) {
    image(punch, 0, 0);
    if (sensorValues[0] < -THRESHOLD || sensorValues[1] <-THRESHOLD) {
      stageScene ++;
      punch.stop();
      runStairs.play();
    }
  }
  if (stageScene == 17) {
    image(runStairs, 0, 0);
    if (runStairs.time()>=36.5) {
      runStairs.stop();
    }
  }
}

void movieEvent(Movie m) {
  m.read();
}
void setupSerial() {
  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[0], 9600);
  while (myPort.available()>0) {
    int inbyte = myPort.read();
    println(inbyte);
  }
}

void updateSerial() {
  while (myPort.available() > 0) {
    myString = myPort.readStringUntil( 10 ); // 10 = 'n'  Linefeed in ASCII
    if (myString != null) {
      String[] serialInArray = split(trim(myString), ",");
      if (serialInArray.length == NUM_OF_VALUES) {
        for (int i=0; i < NUM_OF_VALUES; i++) {
          sensorValues[i] = int(serialInArray[i]);
        }
      }
    }
  }
}
void movieEven(Movie m) {
  m.read();
}


******ARDUINO CODE******
#include <Wire.h>
#include <ADXL345.h>


ADXL345 adxl; //variable adxl is an instance of the ADXL345 library

void setup(){
  Serial.begin(9600);
  adxl.powerOn();
}

void loop(){ 
	  int x,y,z;  
	  adxl.readXYZ(&x, &y, &z); //read the accelerometer values and store them in variables  x,y,z
	// Output x,y,z values 
	  //Serial.print("values of X , Y , Z: ");
	  Serial.print(x);
	  Serial.print(",");
	  Serial.print(y);
	  Serial.print(",");
	  Serial.println(z);
	  delay(100);
  }

Recitation 10: Media Controller

Media Controller: Music & Accelerometer

Process:

This recitation is closely linked with our final. Vera and I first use Arduino to read the values from the accelerometer. And the form of media we controlled was the music sample code we borrowed from the Processing sound library. This is was a good trial for our final since our goal is to use the accelerometer to control media (video) in Processing.

In processing after looking through the code for the sample sound we placed if statements. So if the accelerometer detects acceleration in a certain direction: [0] for X, [1] for Y, and [2] for Z then the sound along with graphics will change a certain way.

Reflection:

I think this recitation was extremely helpful in preparation for Vera and I’s final! The recitation allowed for us to foresee some potential problems, which then allowed us sometime to potentially find a solution.

Question:

Using physical computation to create interactive art and manipulate media is, in my opinion, extremely thought provoking. The interaction it includes has the potential to be included in every kind of project. With the interaction we’re taking something as simple as clicking a button, twisting a potentiometer, or waving your hand into something incredible in the media/digital world.

import processing.sound.*;
import processing.serial.*;
Serial myPort;
String myString = null;
int NUM_OF_VALUES = 3;
int sensorValues[] = {0, 0, 0};
int NUM = 3;
SoundFile [] soundFile = new SoundFile[NUM];
int mode = 0;
 AudioDevice device;
int [] scale = {15, 15, 3};
int bands = 128;
FFT[] fft = new FFT[NUM];
float r_width;
float[] sum = new float[bands];
float smooth_factor = 0.2;

public void setup() {
  fullScreen();
  setupSerial();
  device = new AudioDevice(this, 44000, bands);
  r_width = width/float(bands);
  soundFile[0] = new SoundFile(this, "beat.aiff");
  soundFile[1]= new SoundFile (this, "Cullah_-_06_-_I_See.mp3");
  soundFile[2] = new SoundFile(this, "ALAMO_-_01_-_Remitente_Detente.mp3");
  for (int i = 0; i < NUM; i++) {
    soundFile[i].loop();
    soundFile[i].amp(0);
    fft[i] = new FFT(this, bands);
    fft[i].input(soundFile[i]);
  }
  soundFile[0].amp(1);
  noStroke();
  fill(255, 0, 150);
}      

private void reMode(int new_mode) {
  soundFile[mode].amp(0);
  mode = new_mode;
  soundFile[mode].amp(1);
}

public void draw() {
  updateSerial();
  if (sensorValues[0] < -150){
    reMode(0);
  }
  else if (sensorValues[1] < -150){
    reMode(1);
  }
  else if (sensorValues[2] < -150){
    reMode(2);
  }


  fft[mode].analyze();
  background(125, 255, 125);
  for (int i = 0; i < bands; i++) {
    sum[i] += (fft[mode].spectrum[i] - sum[i]) * smooth_factor;
    rect(i*r_width, height, r_width, -sum[i] * height * scale[mode]);
  }
}

void setupSerial() {
  printArray(Serial.list());
  myPort = new Serial(this, Serial.list()[25], 9600);
  while (myPort.available()>0) {
    int inbyte = myPort.read();
    println(inbyte);
  }
}

void updateSerial() {
  println(sensorValues);
  while (myPort.available() > 0) {
    myString = myPort.readStringUntil( 10 ); // 10 = 'n'  Linefeed in ASCII
    if (myString != null) {
      String[] serialInArray = split(trim(myString), ",");
      if (serialInArray.length == NUM_OF_VALUES) {
        for (int i=0; i < NUM_OF_VALUES; i++) {
          sensorValues[i] = int(serialInArray[i]);
        }
      }
    }
  }
}

******ARDUINO CODE******
#include <Wire.h>
#include <ADXL345.h>

ADXL345 adxl; //variable adxl is an instance of the ADXL345 library

void setup(){
  Serial.begin(9600);
  adxl.powerOn();
}

void loop(){ 
	  int x,y,z;  
	  adxl.readXYZ(&x, &y, &z); //read the accelerometer values and store them in variables  x,y,z
	// Output x,y,z values 
	  //Serial.print("values of X , Y , Z: ");
	  Serial.print(x);
	  Serial.print(",");
	  Serial.print(y);
	  Serial.print(",");
	  Serial.println(z);
	  delay(100);
  }

Recitation 6: Guest Talk

GUEST TALK

Reflection:

The speakers form Xinchejian presented very thought-provoking projects. After listening to all of the speakers I found Andy Garcia’s farming project to be especially interesting. It really shows how far technology has gone in the past few years. With technology you can now literally farm INDOORS! This could then save a lot of the global issues we all face today. Natural unpolluted land is very hard to come by now, especially in urban areas. But with the technology Garcia explored with, every single one of us can grow food within our homes. Not only does it solve the issue of limited space, we can also consume clean food since, the quality of air, amount of sunlight, water, etc. can all be controlled.

Feedback:

With feedbacks for Zane and I’s midterm, the speakers suggested us to focus on the interaction aspect of our project and to fully make sure the users are able to easily engage with the project.

Recitation 9: Soldering

For this recitation I decided to learn soldering. I soldered for the midterm; however, since that was my first time doing it, I had to do it several times, often times without success. Therefore, I wanted to use this recitation to learn how to properly solder.

The recitation was broken down into three separate soldering exercises. First, we soldered a wire to a little piece of copper tape. I think this was the easiest since everything was placed on a flat surface so there were no movements or adjustments needed to be made.

Then we soldered two wires together. This part proved to be a little tricky since we had to place the wires perfectly together to where the ends connected together just right for the solder to melt onto.

Lastly, we made a circuit by soldering wires to a LED lightbulb and then soldering a resistor to one of the wires. I think the most difficult part of this step was soldering the resistor. Since the diameter of the resistor was so small it was difficult for the helping hands to hold on to it. Overall, this recitation was extremely helpful. The recitation has significantly improved my confidence in my soldering skills.

By properly learning how to solder I can apply this new skill to my final project. For Vera and I’s final project there will be multiple sensors. Some of which might be far away from the Arduino and laptop. Therefore, soldering extra wires will be a valuable skill.

Recitation 8: Digital Fabrication

3D Printer

Inspiration:

For this recitation we experimented with 3D printers, which is something I’ve been wanting to do since seeing a video of the printer working in high school. After Vera and I browsed through the list of inspirations the shoes designed by Iris van Herpen caught our eyes.

This then led us to a discussion about how a lot of the times comfort is sacrificed in the name of fashion. We thought it was an interesting concept, so we wanted to do something fun and create something uncomfortable but considered as fashionable.

 

Process:

The shoe mainly consists of five parts: toe of the shoe, heel of the shoe, ankle strap, small strap, and the sole of the shoe.

Everything worked out well and everything printed correctly on the first try. The only issue we had was the small rectangular strap, since it was too small for the printer to detect, but this was easily fixed since we just had to cut a little piece of the material.

Reflection:

We choose 3D printing since neither of us had used it before, and it’s also something we’d like to incorporate into our final project, so we opted for it during this recitation over laser cutting.

 

Looking back to the stupid pet trick project, digital fabrication, whether it’s laser cutting or 3D printing, both of them would’ve been EXTREMELY helpful with the project. This would’ve saved me a lot of time of finding the exact box I was looking for. Also, an aspect of my stupid pet trick project I had an issue with was the blade of the servo not being the size I had hoped for. This problem would’ve been easily solved with 3D printing since I could’ve printed out an exact size of the blade, which could’ve kept the “poops” in the box.

 

In regard to our choice of digital fabrication, the 3D printer, for our recitation, it allows an idea to come to life in a physical sense. Digital fabrication is revolutionary because individuals are able to design something and then print it as something 3 dimensional “on demand.” It’s easy and can cut down on something which was once time consuming.

 

By the time of 2149 I’d imagine the printing time to be a lot less, also the parts printed would be used for much more advanced technology!

Final Essay – Draft

To me, interaction can be defined as a type of conversation. It’s a dialog of actions between two people, a person and machine, or between two machines. In the context of this course however, it’s more so a conversation between a person and a machine. I think of it as a conversation since often times the interaction occurs when a person provides an input, whether its through something as simple as a button to moving their whole body. Then the input is reciprocated with an output. Not only can the output be something physical, but it can also be something emotional, such as a feeling.

For the final project, Vera and I came up with the idea of zero-calorie candy. We initially started out with a candy making simulator, inspired by the thousands of cooking games. However, we wanted to make it even more interactive. Most the the cooking game apps are simple games where you sit on the couch and all that’s required is a tap of a button. Thus, Vera and I wanted to make our project require more movement. We decided on adding a fitness element to the project. The idea of the project is to go through a series of exercise as the candy is being made. For example, one of the steps of making candy is kneading the candy dough, so instead of pressing a button and letting Processing do all the work it’ll require the user to do air punches (simulating a boxing workout) to knead the dough. And through a series of these short workouts the user will see on the bottom of the screen how many calories they’re burning. Thus, at the end when they complete their workout/candy making they will be rewarded with a piece of candy.

Essentially, the interaction is through the user interacting with Arduino/Processing by performing exercises and the machine will give the user the impression the exercises translate into some part of the candy making process. In the end the user will recieve a reward in the form of a piece of candy. Not only is there physical reward, there’s also the emotional reward of accomplishment.

Recitation 7: Drawing Machine

Drawing Machine

Materials:

  • Arduino kit
  • 1 42STH33-0404AC stepper motor
  • 1 SN75440NE ic chip
  • 1 power jack
  • 12 VDC power supply
  • 1 potentiometer (from your kit)
  • 1 Arduino and USB cable (from your kit)
  • Laser-cut and 3D printed mechanisms
  • Pen that fits the laser-cut mechanisms
  • Paper

 

Part A

Step 1: Build the following circuit to control the stepper. Use the stepper_oneRevolution example in Arduino.

This step was straightforward since the schematic for building the stepper circuit was provided to us. One of the challenges was keeping track of all of the jumper cables since there were so many. Rudi had informed us beforehand due to the use of an external power source it’s highly likely if wires aren’t plugged in correctly, it will damage our laptops. Thus, taking it as a precaution I checked out a MacBook from the IMA office beforehand. Fortunately, everything worked out perfectly, and since there weren’t any modifications needed for the Arduino example, everything went smoothly.

 

Step 2: Use your potentiometer and the MotorKnob example to control your motor.

I had to briefly look up the schematics of a potentiometer to refresh my memory, since we had been focusing more on Processing in the last few weeks. The MotorKnob example was fairly straightforward; however, the map() function required a bit of thinking. But once it was explained we had to translate the range to 200 since it’s a 200-step motor, it all made sense. One little hiccup I had was initially the motor wasn’t turning in the same direction as the potentiometer. Later on it was realized all I had to do was switch the ground and power jumper cables.

 

Step 3: Collect the parts of the laser-cut and 3D printed mechanisms as the pictured below and combine your parts (you and one other person) into a mechanical arm that can hold a marker.

This part was also very straightforward. One of the issues I had was the metal clip kept on popping out from the draw machine arms. This was fairly simple since the solution was to just fasten a different pin onto it. Another issue we had was the marker didn’t fit the way we wanted, and it often got stuck as we attempted to adjust it. And since I had extra makers in my pencil pouch we thought we would give it a try and it ended up being a perfect fit (hence the pink). Ultimately, when it came to drawing, it was difficult coordinating with the other person since each person was in control of one of the arms (half of the drawing machine). The drawing machine was a lot of fun building, and in the end the abstract piece of art created was interesting.

When I was looking through the artworks in the reading Bill Vorn’s Hysterical Machine made in 2006 immediately caught my eyes. The spiders from the ceiling looked immensely intimidating. Vorn collected physical data with heat sensors and then translated it into digital data which then directs the lights and the pneumatic legs. This relates to the drawing machine we made in recitation in that we used a potentiometer to collect physical data and then translated it into digital data for the motor to turn (which had a marker attached to the end to draw).

#include <Stepper.h>

// change this to the number of steps on your motor
#define STEPS 100

// create an instance of the stepper class, specifying
// the number of steps of the motor and the pins it's
// attached to
Stepper stepper(STEPS, 8, 9, 10, 11);

// the previous reading from the analog input
int previous = 0;

void setup() {
  // set the speed of the motor to 30 RPMs
  stepper.setSpeed(30);
}

void loop() {
  // get the sensor value
  int val = map(analogRead(0), 0, 1024, 0, 200);

  // move a number of steps equal to the change in the
  // sensor reading
  stepper.step(val - previous);

  // remember the previous value of the sensor
  previous = val;
}

Midterm: Pong

PONG

Materials

  • Breadboard
  • Jumper cables
  • 220 ohms resistors
  • Infrared distance sensors
  • Buttons
  • Arduino Uno
  • USB cable

Idea

Zane and I first landed on the idea when we were talking about our childhoods one day. He said how much he reminisced about how much he missed playing video games with his brother when he was younger. Unfortunately, growing up I wasn’t allowed to play any video games and so hearing Zane bonding with his older brother over video games was interesting and heartwarming at the same time. We selected Pong since we wanted to do something retro style to keep the theme of childhood memories. Pong is a simple two player game which allows the players to compete with one another. We didn’t want the game to be too easy, so we made the game harder with the way we programmed the distance sensor. Since Zane is a much better coder than I, we decided he would take the lead on coding whereas I’d take up more of the physical aspect of the project.

We wanted to add on difficulty to this classic game, so we programmed the sensors to where if your hand placement is above a certain level then your paddle will continuously move up, thus requiring some strategic thinking. We put our own twists to the game by including special powers specific to each colored paddle.

 

Connecting the Circuit

We created the circuit by connecting jumper cables to create a simple button circuit. Initially we thought we would have a lot troubles with the button since the button included in the Arduino kit were too small for users to press it easily, but after checking with the IMA office we found out there were bigger buttons, which turned out perfect for our project. Since the buttons weren’t connected to wires we had to figure out how to solder on the spot. This was proved to be difficult since neither Zane nor I knew how to solder, we didn’t even know we had to tin our wires. In the end we learned by imitation after watching someone else solder. After getting blisters and endless trials we finally ended up with six buttons.

Constructing the Game Console

One of the biggest challenges we knew going into constructing the game console was the sensitivity of the distance sensor. We were able to solve this by placing the sensor into a tube-like structure, thus constricting the sensor to only sense the player’s hands. We opted for a galaxy print for the cardboard to fit in with the galaxy background in processing. We had to paint each individual cardboard black first to create an even surface then dab on the galaxy print.

Code

At the beginning of the project we decided Zane would take the lead on coding. Through the process of coding I was able to learn a lot from Zane since he was able to teach me a lot of the concepts I hadn’t known before. One of the main sections of the code was the use of boolean, it’s what allows the user to navigate through the main menu. With different cases already stated, boolean navigates through them with true or false statements. For example, on the start screen if the player was to select the how to play option then boolean would state “how to play” as true and go to the screen which has the directions to the game. One of the challenges towards the end of the project was the continuous crashing of Processing. This then required us to meticulously go back through the code and in the end, it turned out to be a misplaced curly bracket.

The special abilities were made through if statements. One of the things we when we started testing the game was rate at which the abilities charged up. We started out with if the ball came in contact with the paddle five times then the special abilities will be fully charged. But when we played the game we realized this would be too time consuming, thus we lowered it to only two hits. The if statements were used so when the special abilities buttons were pressed (boolean) and the ability was charged up then the paddle’s power would activate.

User Testing

Part 1: Prepare

One of the most important thing when the players are playing the game is the easiness of navigation through the game. Another important question for the user would be what they think can improve the project and also if they thought the game was interactive enough. Since this is supposed to be a video game an important thing is to also see if the game truly involves a little bit of strategy and invokes competitiveness between the two players.

Part 2: Reporting

One of the projects I tested during recitation on Friday was a clicking dot game. The goal of the game was to eliminate the moving dots as fast as you could by clicking on it with your mouse. The background of the game and the colors of the circles were similar which added on a layer of difficulty. I thought the game was engaging and fun. One of the suggestions I had was to make the animation screen larger. Since the game required the player to win by clicking on the circle and a lot of the times it turned into frantic clicking a couple of times I ended up clicking on the windows beneath the animation screen and then accidentally exiting out of the screen. Another suggestion for the project was for the creator to add a times so the players can see the rate at which they are going.

One of the most common suggestions we received was for us to use more than one sensors, and this was definitely considered during the creation of the project, but in the end, we wanted to stick to the true essence of Pong and didn’t want to add on extra sensors just to have more than one sensor. Another suggestion that was given was instead of moving your hand up and down to move the paddle we should install a block of some sort, so the player can move the paddle by sliding the block. This could’ve helped with the game since the we didn’t take the user demographic as much into consideration. The issue with the user demographic became evident when some of the players weren’t sure as to how the buttons on the control system work (we initially thought using up, down, A, and B buttons would be pretty clear since it models after a regular game controller). Overall, players who tested the game seemed to really enjoy the game and the competiveness of the game certainly engaged the users more. One of the biggest improvements I think we could’ve make was to add on more ideas so as to make our project even more unique.

/*
 *
 * THE ULTIMATE PONG ARCADE GAME
 * Code by Zane Fadul
 */

import processing.serial.*;
import processing.sound.*;

//******************************************************************Controller Variables
Serial controller;
int valueFromController = 79; //putting a random assignment
int player1Values;
int player2Values;
int down;
int up;
int b;
int a;
int[] controllerValues;

//***********************************************************************Sound Variables
SoundFile intro;
SoundFile menu;
SoundFile buttonClick;
SoundFile battle;
SoundFile transitionSound;
SoundFile ballOut;
SoundFile win;
SoundFile ability;
//*********************************************************************Graphic Variables
PFont infoFont;
PFont titleFont;
int heightPixels;
int widthPixels;
int startTime;

// Star Field Graphic used by:
// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/17WoOqgXsRM
class Star {
  float x;
  float y;
  float z;
  float pz;

  Star() {
    x = random(-width/2, width/2);
    y = random(-height/2, height/2);
    z = random(width/2);
    pz = z;
  }

  void update() {
    z = z - speed;
    if (z < 1) {
      z = width/2;
      x = random(-width/2, width/2);
      y = random(-height/2, height/2);
      pz = z;
    }
  }

  void show() {
    strokeWeight(1);
    fill(255);
    noStroke();
    float sx = map(x / z, 0, 1, 0, width/2);
    float sy = map(y / z, 0, 1, 0, height/2);
    float px = map(x / pz, 0, 1, 0, width/2);
    float py = map(y / pz, 0, 1, 0, height/2);
    pz = z;
    pushMatrix();
    stroke(255);
    translate(width/2, height/2);
    line(px, py, sx, sy);
    popMatrix();
  }
}
int speed = height/30;
Star[] stars = new Star[800];

//Intro Sequence Variables
float dotX(float dotT) {
  return sin(dotT/10) * heightPixels/5;
}
float dotY(float dotT) {
  return cos(dotT/10) * heightPixels/5;
}
float dotT = 0;
float alpha = 0;
boolean beginTitle = false;
float scaleBall = 2;

//Title Screen Variables
class Drop {
  float x = random(0, width);
  float y = random(height);
  float xSpeed = random(4, 10);

  void fall() {
    x = x + xSpeed;

    if (x > width) {
      y = random(height);
      x = random(-500, -100);
    }
  }

  void show() {
    stroke(160);
    strokeWeight(1);
    line(x, y, x + height/10, y);
  }
}
Drop[] drops = new Drop[100];

//Game Variables
class Transition {
  float x = random(0, width);
  float y = random(height);
  float xSpeed = random(width/5, width/2);

  void fall() {
    x = x + xSpeed;

    if (x > width) {
      y = random(height);
      x = random(-500, -100);
    }
  }

  void show() {
    stroke(160);
    strokeWeight(1);
    line(x, y, x + height/10, y);
  }
}
Transition[] particles = new Transition[100];
int transitionCount = 0;

//Ability Bars
void abilityBar1(int charge, float spark) {
  switch(charge) {
  case 0:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    break;

  case 1:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, width/80, height/40);
    break;

  case 2:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 2*width/80, height/40);
    break;

  case 3:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 3*width/80, height/40);
    break;

  case 4:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 4*width/80, height/40);
    break;

  case 5:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 5*width/80, height/40);
    break;

  case 6:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 6*width/80, height/40);
    break;

  case 7:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 7*width/80, height/40);
    break;

  case 8:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 8*width/80, height/40);
    break;

  case 9:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, height/80, 9*width/80, height/40);
    break;

  case 10:
    noStroke();
    rectMode(CORNER);
    spark = random(170, 255);
    fill(#0063FF, spark);
    rect(3.3*width/4, height/80, width/8, height/40);
    break;
  }
}
void abilityBar2(int charge, float spark) {
  switch(charge) {
  case 0:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    break;

  case 1:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, width/80, height/40);
    break;

  case 2:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 2*width/80, height/40);
    break;

  case 3:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 3*width/80, height/40);
    break;

  case 4:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 4*width/80, height/40);
    break;

  case 5:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 5*width/80, height/40);
    break;

  case 6:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 6*width/80, height/40);
    break;

  case 7:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 7*width/80, height/40);
    break;

  case 8:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 8*width/80, height/40);
    break;

  case 9:
    noStroke();
    rectMode(CORNER);
    fill(170);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    fill(#0063FF);
    rect(3.3*width/4, 77*height/80, 9*width/80, height/40);
    break;

  case 10:
    noStroke();
    rectMode(CORNER);
    spark = random(170, 255);
    fill(#0063FF, spark);
    rect(3.3*width/4, 77*height/80, width/8, height/40);
    break;
  }
}
float spark1 = 255;
float spark2 = 255;

//Lives
void printP1Lives(int lives, int colorIndex) {
  switch(colorIndex) {
  case 1:
    colorIndex = #B2D8FF;
    break;
  case 2:
    colorIndex = #FF0000;
    break;
  case 3:
    colorIndex = #0700FF;
    break;
  case 4:
    colorIndex = #B600FF;
    break;
  case 5:
    colorIndex = #1BFF08;
    break;
  case 666:
    colorIndex = 0;
    break;
  }
  switch(lives) {
  case 3:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(255);
    ellipse(3*width/8, height/40, height/40, height/40);
    ellipse(width/2, height/40, height/40, height/40);
    ellipse(5*width/8, height/40, height/40, height/40);
    break;

  case 2:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(255);
    ellipse(3*width/8, height/40, height/40, height/40);
    ellipse(width/2, height/40, height/40, height/40);
    fill(0);
    ellipse(5*width/8, height/40, height/40, height/40);
    break;

  case 1:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(255);
    ellipse(3*width/8, height/40, height/40, height/40);
    fill(0);
    ellipse(width/2, height/40, height/40, height/40);
    ellipse(5*width/8, height/40, height/40, height/40);
    break;

  case 0:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(0);
    ellipse(3*width/8, height/40, height/40, height/40);
    ellipse(width/2, height/40, height/40, height/40);
    ellipse(5*width/8, height/40, height/40, height/40);
    break;
  }
}

void printP2Lives(int lives, int colorIndex) {
  switch(colorIndex) {
  case 1:
    colorIndex = #B2D8FF;
    break;
  case 2:
    colorIndex = #FF0000;
    break;
  case 3:
    colorIndex = #0700FF;
    break;
  case 4:
    colorIndex = #B600FF;
    break;
  case 5:
    colorIndex = #1BFF08;
    break;
  case 666:
    colorIndex = 0;
    break;
  }
  switch(lives) {
  case 3:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(255);
    ellipse(3*width/8, 39*height/40, height/40, height/40);
    ellipse(width/2, 39*height/40, height/40, height/40);
    ellipse(5*width/8, 39*height/40, height/40, height/40);
    break;

  case 2:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(255);
    ellipse(3*width/8, 39*height/40, height/40, height/40);
    ellipse(width/2, 39*height/40, height/40, height/40);
    fill(0);
    ellipse(5*width/8, 39*height/40, height/40, height/40);
    break;

  case 1:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(255);
    ellipse(3*width/8, 39*height/40, height/40, height/40);
    fill(0);
    ellipse(width/2, 39*height/40, height/40, height/40);
    ellipse(5*width/8, 39*height/40, height/40, height/40);
    break;

  case 0:
    strokeWeight(4);
    stroke(colorIndex);
    shapeMode(CENTER);
    fill(0);
    ellipse(3*width/8, 39*height/40, height/40, height/40);
    ellipse(width/2, 39*height/40, height/40, height/40);
    ellipse(5*width/8, 39*height/40, height/40, height/40);
    break;
  }
}

//End Game Variables
int endGameTimer = 0;

//************************************************************************Button Objects
class Button
{
  int buttonIndex;
  String text;
  float xPos;
  float yPos;


  Button(int buttonIndex, String text, float xPos, float yPos)
  {
    this.buttonIndex = buttonIndex;
    this.text = text;
    this.xPos = xPos;
    this.yPos = yPos;
  } 

  int getButtonIndex() {
    return buttonIndex;
  }

  String getButtonName() {
    return text;
  }

  void changeText(String text) {
    this.text = text;
  }

  void display() {
    boolean bool = checkForSelected(valueFromController, buttonIndex);
    if (bool == true) { //IF SELECTED
      rectMode(CENTER);
      strokeWeight(width/100);
      stroke(255);
      fill(0);
      rect(xPos, yPos, width/5, height/12);
      fill(255);
      textAlign(CENTER);
      textFont(infoFont);
      text(text, xPos, yPos  + height/48, width/5, height/12); //ATTEMPTS TO PRINT TEXT ON A BOX
    }
    if (bool == false) { //IF NOT SELECTED
      rectMode(CENTER);
      strokeWeight(width/200);
      stroke(255);
      fill(0);
      rect(xPos, yPos, width/5, height/12);
      fill(255);
      textAlign(CENTER);
      textFont(infoFont);
      text(text, xPos, yPos + height/48, width/5, height/12);
    }
  }
}

boolean checkForSelected(int valueFromController, int buttonIndex) {
  if (valueFromController == buttonIndex) {
    return true;
  } else {
    return false;
  }
}

//Main Menu Buttons
Button startMatchButton;
Button addUserButton;
Button changeUserColorButton;
Button howToPlayButton;
Button selectUser1Button;
Button selectUser2Button;
Button playGameButton;
boolean player1Selected = false;
boolean player2Selected = false;

//************************************************************************Data Variables
PrintWriter userDatabase;
String[] userData;

//**********************************************************************Switch Variables
//Screen Switches
boolean gameLoaded = false;
boolean gameIntro = false;
boolean introSkip = false;
boolean titleScreen = false;
boolean listUsers = false;
boolean selectUserToAdd = false;
boolean createUser = false;
boolean chooseColor = false;
boolean firstTimeAdd = false;
boolean selectUserToColor = false;
boolean selectUserToDelete = false;
boolean playGame = false;
boolean transition = false;
boolean gameOver = false;

//Title Screen Button Switches
boolean startMatch = false;
boolean changeUserColor = false;
boolean deleteUser = false;
boolean howToPlay = false;

//Ability Switches
boolean ability1Used = false;
boolean ability2Used = false;

//**************************************************************************User Objects
class User
{
  //Instance Variables
  String username;
  int rank = 1;
  int preferredColor;
  String data;

  //Constructor Declaration of Class
  User(String username, int rank, int preferredColor)
  {
    this.username = username.toUpperCase();
    this.rank = rank;
    this.preferredColor = preferredColor;
  }

  String getUsername()
  {
    return username;
  }
  int getRank() {
    return rank;
  }
  int getPreferredColor() {
    return preferredColor;
  }
  void addRank() {
    rank += 1;
  }
  void setUsername(String username) {
    this.username = username.toUpperCase();
  }
  void setPreferredColor(int preferredColor) {
    this.preferredColor = preferredColor;
  }
}

/* Type Mismatch when incrementing objects to the list, will redo using simply strings
 Object createNewUserFromData(String data) 
 {
 tempUser = data.split(",");
 return new User(tempUser[0], int(tempUser[1]), int(tempUser[2]));
 }
 
 Object createNewUser(String name, int rank, int preferredColorIndex) 
 {
 return new User(name, rank, preferredColorIndex);
 }*/

char[] alphabet = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
  'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};

String[] newUserFromString(String data) {
  String[] splitData = data.split(",");
  return splitData;
}

//User Object Variables
String[] tempUser;
User[] userList = new User[6];
Button[] userButtons = new Button[6];
String[] userListStrings = {"", "", "", "", "", ""};

//*************************************************************************Game Variables
class Paddle
{
  float speed = 1;
  int direction;
  float xpos;
  float ypos;
  int colorIndex = 1;
  int newColor;

  Paddle(int colorIndex, float speed, float xpos, float ypos)
  {
    this.speed = speed;
    this.xpos = xpos;
    this.ypos = ypos;
    this.colorIndex = colorIndex;
  }

  void setSpeed(float newSpeed) {
    this.speed = newSpeed;
  }
  void setColor(int colorIndex) {
    this.colorIndex = colorIndex;
  }

  int getColor() {
    return this.colorIndex;
  }

  void show(float xpos, float ypos) {
    switch(colorIndex) {
    case 1:
      newColor = 255;
      break;
    case 2:
      newColor = #FF0000;
      break;
    case 3:
      newColor = #0700FF;
      break;
    case 4:
      newColor = #B600FF;
      break;
    case 5:
      newColor = #1BFF08;
      break;
    case 666: //used for abilities
      newColor = 0;
      break;
    }
    fill(newColor);
    noStroke();
    rect(xpos, ypos, height/36, height/8);
    this.ypos = ypos;
  }

  void display(int direction) {
    noStroke();
    switch(colorIndex) {
    case 1:
      newColor = 255;
      break;
    case 2:
      newColor = #FF0000;
      break;
    case 3:
      newColor = #0700FF;
      break;
    case 4:
      newColor = #B600FF;
      break;
    case 5:
      newColor = #1BFF08;
      break;
    case 666: //used for abilities
      newColor = 0;
      break;
    }
    switch(direction) {
    case 2:
      fill(newColor);
      rectMode(CENTER);
      rect(xpos, ypos, height/36, height/8);
      break;

    case 0:
      if (ypos - height/16 > height/20) {
        ypos -= speed*height/250;
      }
      fill(newColor);
      rectMode(CENTER);
      rect(xpos, ypos, height/36, height/8);
      break;

    case 1:
      if (ypos + height/16 < height - height/20) {
        ypos += speed*height/250;
      }
      fill(newColor);
      rectMode(CENTER);
      rect(xpos, ypos, height/36, height/8);
      break;
    }
  }

  float getxPos() {
    return xpos;
  }
  float getyPos() {
    return ypos;
  }
}

class Ball
{
  float speed = 1;
  int direction = int(random(1, 4));
  float xpos = width/2;
  float ypos = height/2;
  int col = 1;

  Ball(float speed)
  {
    this.speed = speed;
  }
  void setSpeed(float newSpeed) {
    this.speed = newSpeed;
  }

  void setColor(int col) {
    this.col = col;
  }

  void show(int col) {
    switch(col) {
    case 1:
      col = 255;
      break;
    case 2:
      col = #0063FF;
      break;
    }

    noStroke();
    fill(col);
    xpos = width/2;
    ypos = height/2;
    rect(width/2, height/2, height/60, height/60);
    direction = int(random(1, 4));
  }

  void hide() {
    noStroke();
    fill(255);
    xpos = width/2;
    ypos = height/2;
    rect(-400, -400, 1, 1);
    direction = int(random(1, 4));
  }

  int display() {
    switch(col) {
    case 1:
      col = 255;
      break;
    case 2:
      col = #0063FF;
      break;
    }
    fill(col);
    switch(direction) {
    case 1:
      noStroke();
      rect(xpos, ypos, height/60, height/60);
      xpos += speed*width/400;
      ypos -= speed*width/400;
      break;

      case(2):
      noStroke();
      rect(xpos, ypos, height/60, height/60);
      xpos -= speed*width/400;
      ypos -= speed*width/400;
      break;

      case(3):
      noStroke();
      rect(xpos, ypos, height/60, height/60);
      xpos -= speed*width/400;
      ypos += speed*width/400;
      break;

      case(4):
      rect(xpos, ypos, height/60, height/60);
      xpos += speed*width/400;
      ypos += speed*width/400;
      break;
    }
    if (ypos <= height/20 + 5 && direction == 2) { //hit top from left
      direction = 3;
      return 0;
    }
    if (ypos <= height/20 + 5 && direction == 1) { //hit top from right
      direction = 4;
      return 0;
    }
    if (ypos + height/100 >= height - height/20 && direction == 3) { //hit bottom from left
      direction = 2;
      return 0;
    }
    if (ypos + height/100 >= height - height/20 && direction == 4) { //hit bottom from right
      direction = 1;
      return 0;
    }
    if (xpos <= paddle1.getxPos() + height/60 && ypos >= paddle1.getyPos() - height/16 && ypos <= paddle1.getyPos() + height/16 && direction == 2) { //hit left going up
      direction = 1;
      return 1;
    }
    if (xpos <= paddle1.getxPos() + height/60 && ypos >= paddle1.getyPos() - height/16 && ypos <= paddle1.getyPos() + height/16  && direction == 3) { //hit left going down
      direction = 4;
      return 1;
    }
    if (xpos + height/100 >= paddle2.getxPos() - height/120 && ypos >= paddle2.getyPos() - height/16 && ypos <= paddle2.getyPos() + height/16 && direction == 1) { //hit right going up
      direction = 2;
      return 2;
    }
    if (xpos + height/100 >= paddle2.getxPos() - height/120 && ypos >= paddle2.getyPos() - height/16 && ypos <= paddle2.getyPos() + height/16 && direction == 4) { //hit hit right going down
      direction = 3;
      return 2;
    }    
    if (xpos <= 0 && direction == 2) { //hit left going up
      return 3;
    }
    if (xpos <= 0 && direction == 3) { //hit left going down
      return 3;
    }
    if (xpos + height/100 >= width && direction == 1) { //hit right going up
      return 4;
    }
    if (xpos + height/100 >= width && direction == 4) { //hit hit right going down
      return 4;
    } else {
      return 0;
    }
  }
}

Paddle paddle1;
Paddle paddle2;
Ball ball;

int paddle1TempColor;
int paddle2TempColor;
int paddle1Charge;
int paddle2Charge;
int player1Lives;
int player2Lives;
int ballValues;

//**********************************************************************************Ability
void incrementCharge(int paddleCharge) {
  switch (paddleCharge) {
  case 0:
    break;
  case 1:
    if (paddle1Charge != 10) {
      paddle1Charge+= 5;
    }
    break;
  case 2:
    if (paddle2Charge != 10) {
      paddle2Charge+= 5;
    }
    break;
  }
}
int player1Ability;
int player2Ability;
int abilityTimer1 = 0;
int abilityTimer2 = 0;
//**********************************************************************************Setup
void setup() {
  //****WINDOW DIMENSIONS
  fullScreen();
  background(0);
  heightPixels = height;
  widthPixels = width;
  //****TEXT OBJECT DECLARATIONS
  infoFont = createFont("OCR A Extended", width/50, true);
  titleFont = createFont("Corbel Bold", width/6.5, true);
  //****CONTROLLER VALUES
  controller = new Serial(this, Serial.list()[0], 115200);
  controllerValues = new int[9];
  //****BUTTON OBJECTS
  startMatchButton = new Button(0, "START MATCH", width/2, 4*height/8);
  addUserButton = new Button(1, "ADD USER", width/2, 5*height/8);
  changeUserColorButton = new Button(2, "CHANGE COLOR", width/2, 6*height/8);
  howToPlayButton = new Button(3, "HOW TO PLAY", width/2, 7*height/8);
  selectUser1Button = new Button(4, "SELECT PLAYER 1", width/2, 3*height/8);
  selectUser2Button = new Button(5, "SELECT PLAYER 2", width/2, 4*height/8);
  playGameButton = new Button(6, "PLAY GAME", width/2, 6*height/8);
  //****GAME OBJECTS
  ball = new Ball(1);
  paddle1 = new Paddle(1, 2, width/36, height/2);
  paddle2 = new Paddle(1, 2, 35*width/36, height/2);
  for (int i = 0; i < particles.length; i++) {
    particles[i] = new Transition();
  }
  //****SOUND OBJECTS
  intro = new SoundFile(this, "intro.mp3");
  menu = new SoundFile(this, "menu.mp3");
  buttonClick = new SoundFile(this, "button.mp3");
  battle = new SoundFile(this, "battle.mp3");
  transitionSound = new SoundFile(this, "transition.mp3");
  win = new SoundFile(this, "win.mp3");
  ballOut = new SoundFile(this, "ballout.mp3");
  ability = new SoundFile(this, "ability.mp3");
  //****MENU BACKGROUND
  for (int i = 0; i < drops.length; i++) {
    drops[i] = new Drop();
  }
  //****GAME BACKGROUND
  for (int i = 0; i < stars.length; i++) {
    stars[i] = new Star();
  }
  startTime = millis();
}

//***********************************************************************************Draw
void draw() {

  //Load Users from Text File, if none, create one
  if (gameLoaded == false) {
    if (intro.isPlaying() != 1) {
      intro.play();
    }
    if (millis() - startTime < 7600) {
      textAlign(CENTER, CENTER);
      textFont(infoFont);
      fill(255);
      text("Loading User Data...", 4*width/5, 7*height/8);
    }
    if (millis() - startTime > 7600) {
      userData = loadStrings("userdata.txt");
      if (userData != null) {
        background(0);
        for (int i =  0; i < userData.length; i++) {
          String tempUserLine = userData[i];
          String[] tempUserData = newUserFromString(tempUserLine);
          try {
            userList[i] = new User(tempUserData[0], int(tempUserData[1]), int(tempUserData[2]));
          }
          catch (Exception e) {
            print("");
          }
        }
        printArray(userList);
        println("User Data loaded."); //Console Print for testing
        text("User Data loaded.", 4*width/5, 7*height/8);
        gameLoaded = true;
      } else {
        background(0);
        println("User Data not found. Creating new file."); //Console Print for testing
        text("User Data not found.", 4*width/5, 7*height/8);
        text("Creating new file.", 4*width/5, 7.25*height/8);
        userDatabase = createWriter("userdata.txt");
        userDatabase.flush();
        //userDatabase.close(); //I HAVE QUESTIONS ABOUT THIS
        userData = loadStrings("userdata.txt");
        gameLoaded = true;
      }
    }
  }

  //Intro Sequence to Game
  if (gameIntro == false && gameLoaded == true) {

    down = controllerValues[0];
    up = controllerValues[1];
    b = controllerValues[2];
    a = controllerValues[3];

    if (a == 1) {
      gameIntro = true;
      titleScreen = true;
      alpha = 255;
      intro.stop();
      valueFromController = 0;
      a = 0;
    }
    if (dotT < 800) {
      stroke(255);
      strokeWeight(5);
      translate(width/2, height/2);
      point(dotX(dotT), dotY(dotT));
      dotT += 4;
      alpha = 0;
    }
    if (alpha < 255 && dotT >= 800) {
      noStroke();
      fill(255, alpha);
      ellipse(width/2, height/2, 2*heightPixels/5, 2*heightPixels/5);
      alpha += 0.3;
    }
    if (alpha > 40 && alpha < 60) {
      background(255);
      fill(0);
      ellipse(width/2, height/2, 2*heightPixels/5, 2*heightPixels/5);
    }
    if (alpha > 50 && alpha < 70) {
      background(0);
      fill(255);
      ellipse(width/2, height/2, 2*heightPixels/5, 2*heightPixels/5);
    }
    if (alpha > 60 && alpha < 80) {
      background(255);
      fill(0);
      ellipse(width/2, height/2, 2*heightPixels/5, 2*heightPixels/5);
    }
    if (alpha > 100 && beginTitle != true) {
      alpha = 255;
      beginTitle = true;
      background(255);
      delay(2000);
    }
    if (beginTitle == true && scaleBall < 25) {
      background(0);
      fill(255);
      ellipse(3.5*width/8, 3*height/10, 3*width/scaleBall, 3*width/scaleBall);
      scaleBall += 0.2;
    }
    if (scaleBall > 25 && scaleBall < 35) {
      textFont(titleFont);
      textAlign(CENTER, CENTER);
      text('P', 2.5*width/8, 2.8*height/10);
      scaleBall ++;
    }
    if (scaleBall > 35 && scaleBall < 45) {
      textAlign(CENTER, CENTER);
      text('N', 4.55*width/8, 2.8*height/10);
      scaleBall ++;
    }
    if (scaleBall > 45 && scaleBall < 55) {
      textAlign(CENTER, CENTER);
      text('G', 5.5*width/8, 2.8*height/10);
      scaleBall ++;
    }
    if (scaleBall > 55) {
      gameIntro = true;
      titleScreen = true;
      valueFromController = 0;
      alpha = 255;
    }
  }

  //Title Screen (will have Start Match, Change User Color, Delete User, How To Play)
  if (titleScreen == true) {
    if (win.isPlaying() == 1) {
      win.stop();
    }
    if (menu.isPlaying() != 1) {
      menu.play();
      menu.amp(0.6);
    }

    down = controllerValues[0];
    up = controllerValues[1];
    b = controllerValues[2];
    a = controllerValues[3];

    background(0);
    for (int i = 0; i < drops.length; i++) {
      drops[i].fall();
      drops[i].show();
    }
    textAlign(CENTER);
    textFont(infoFont);
    text("©2018 Interaction Lab", width/2, 15*height/16);
    translate(0, -height/16);
    //Button Display
    startMatchButton.display();
    addUserButton.display();
    changeUserColorButton.display();
    howToPlayButton.display();

    //vvthese rectangles are over the buttons currently in beta to show they cannot be used
    rectMode(CENTER);
    noStroke();
    fill(255, 70);
    rect(width/2, 5*height/8, width/5, height/12);
    rect(width/2, 6*height/8, width/5, height/12);
    //^^these rectangles are over the buttons currently in beta to show they cannot be used

    if (alpha > 0) {
      noStroke();
      fill(0, alpha);
      rectMode(CORNER);
      rect(0, 0, width, height);
      alpha -= 3;
    } 
    fill(255);
    noStroke();
    ellipse(3.5*width/8, 3*height/10, 3*width/24.8, 3*width/24.8);
    textAlign(CENTER, CENTER);
    textFont(titleFont);
    text('P', 2.5*width/8, 2.8*height/10);
    text('N', 4.55*width/8, 2.8*height/10);
    text('G', 5.5*width/8, 2.8*height/10);

    //Test for arduino
    if (down == 1 && valueFromController != 0) {
      valueFromController --;
      buttonClick.play();
    }
    if (up == 1 && valueFromController != 3) {
      valueFromController ++;
      buttonClick.play();
    }
    //Button Selections to different screens
    if (valueFromController == startMatchButton.getButtonIndex() && a == 1) {
      startMatch = true;
      titleScreen = false;
      valueFromController = 4;
    }
    /*if (valueFromController == addUserButton.getButtonIndex() && a == 1) {
     createUser = true;
     titleScreen = false;
     valueFromController = 0;
     }*/
    /*if (valueFromController == changeUserColorButton.getButtonIndex() && a == 1) {
     changeUserColor = true;
     titleScreen = false;
     valueFromController = 0;
     }*/
    if (valueFromController == howToPlayButton.getButtonIndex() && a == 1) {
      howToPlay = true;
      titleScreen = false;
      valueFromController = 0;
    }
  }

  //************************************************************Start Match Options (SCREEN #4)
  if (startMatch == true) {

    down = controllerValues[0];
    up = controllerValues[1];
    b = controllerValues[2];
    a = controllerValues[3];

    //Test for arduino
    if (down == 1 && valueFromController != 4) {
      valueFromController --;
      buttonClick.play();
    }
    if (up == 1 && valueFromController != 6) {
      valueFromController ++;
      buttonClick.play();
    }
    if (b == 1) {
      startMatch = false;
      titleScreen = true;
      selectUser1Button.changeText("SELECT PLAYER 1");
      selectUser2Button.changeText("SELECT PLAYER 2");
      valueFromController = 0;
    }
    background(0);
    for (int i = 0; i < drops.length; i++) {
      drops[i].fall();
      drops[i].show();
    }
    //Select Users
    selectUser1Button.display();
    selectUser2Button.display();
    playGameButton.display();

    if (a == 1 && valueFromController == selectUser1Button.getButtonIndex()) {
      listUsers = true;
      startMatch = false;
      selectUserToAdd = true;
      player1Selected = true;
      player2Selected = false;
      a = 0;
      valueFromController = 7;
    }
    if (a == 1 && valueFromController == selectUser2Button.getButtonIndex()) {
      listUsers = true;
      startMatch = false;
      selectUserToAdd = true;
      player2Selected = true;
      player1Selected = false;
      a = 0;
      valueFromController = 7;
    }
    if (selectUser1Button.getButtonName() == "SELECT PLAYER 1" || selectUser2Button.getButtonName() == "SELECT PLAYER 2" ) {
      noStroke();
      fill(255, 70);
      rect(width/2, 6*height/8, width/5, height/12);
    }
    //(1st time) Return to Start Match Options showing one slot still open for another user
    //(2nd time) Return to Start Match Options unlocking Play button
    //Play Button (if two users are selected)
    if (selectUser1Button.getButtonName() != "SELECT PLAYER 1" && selectUser2Button.getButtonName() != "SELECT PLAYER 2" ) {
      //Starts Game
      if (a == 1 && valueFromController == playGameButton.getButtonIndex()) {
        transition = true;
        startMatch = false;
        a = 0;
        menu.stop();
        transitionCount = 0;
        alpha = 0;
      }
    }
  }

  //**********************************************List Users and allow player to select with the A button (SCREEN #5)
  if (listUsers == true && selectUserToAdd == true) {
    background(0);

    down = controllerValues[0];
    up = controllerValues[1];
    b = controllerValues[2];
    a = controllerValues[3];

    if (b == 1) {
      startMatch = true;
      listUsers = false;
      selectUserToAdd = false;
      player1Selected = false;
      player2Selected = false;
      valueFromController = 4;
    }
    //Test for arduino
    if (down == 1 && valueFromController > 7) {
      valueFromController --;
      buttonClick.play();
    }
    if (up == 1 && valueFromController < 12) {
      valueFromController ++;
      buttonClick.play();
    }
    for (int i = 0; i < drops.length; i++) {
      drops[i].fall();
      drops[i].show();
    }
    //Create User Button Objects
    for (int i = 0; i < userList.length; i++) {
      try {
        userButtons[i] = new Button(i+7, userList[i].getUsername(), width/4, (i+1)*height/7);
      } 
      catch(Exception e) {
        userButtons[i] = new Button(i+7, "NO USER", width/4, (i+1)*height/7);
      }
    }
    //Display User Button Objects
    for (int i = 0; i < userButtons.length; i++)
    {
      userButtons[i].display();
      try {
        text("WINS: " + userList[i].getRank(), 3*width/4, (i+1)*height/7 + height/49, width/5, height/12);
      }
      catch (Exception e) {
      }
    }
    if (userList[0] != null) {
      if (a == 1 && valueFromController == userButtons[0].getButtonIndex()) {
        if (player1Selected == true) {
          paddle1.setColor(userList[0].getPreferredColor());
          selectUser1Button.changeText(userList[0].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          startMatch = true;
          valueFromController = 4;
        }
        if (player2Selected == true) {
          paddle2.setColor(userList[0].getPreferredColor());
          selectUser2Button.changeText(userList[0].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          startMatch = true;
          valueFromController = 5;
        }
      }
    }
    if (userList[1] != null) {
      if (a == 1 && valueFromController == userButtons[1].getButtonIndex()) {
        if (player1Selected == true) {
          paddle1.setColor(userList[1].getPreferredColor());
          selectUser1Button.changeText(userList[1].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          startMatch = true;
          valueFromController = 4;
        }
        if (player2Selected == true) {
          paddle2.setColor(userList[1].getPreferredColor());
          selectUser2Button.changeText(userList[1].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          startMatch = true;
          valueFromController = 5;
        }
      }
    }
    if (userList[2] != null) {
      if (a == 1 && valueFromController == userButtons[2].getButtonIndex()) {
        if (player1Selected == true) {
          paddle1.setColor(userList[2].getPreferredColor());
          selectUser1Button.changeText(userList[2].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player1Selected = false;
          startMatch = true;
          valueFromController = 4;
        }
        if (player2Selected == true) {
          paddle2.setColor(userList[2].getPreferredColor());
          selectUser2Button.changeText(userList[2].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player2Selected = false;
          startMatch = true;
          valueFromController = 5;
        }
      }
    }
    if (userList[3] != null) {
      if (a == 1 && valueFromController == userButtons[3].getButtonIndex()) {
        if (player1Selected == true) {
          paddle1.setColor(userList[3].getPreferredColor());
          selectUser1Button.changeText(userList[3].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player1Selected = false;
          startMatch = true;
          valueFromController = 4;
        }
        if (player2Selected == true) {
          paddle2.setColor(userList[3].getPreferredColor());
          selectUser2Button.changeText(userList[3].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player2Selected = false;
          startMatch = true;
          valueFromController = 5;
        }
      }
    }
    if (userList[4] != null) {
      if (a == 1 && valueFromController == userButtons[4].getButtonIndex()) {
        if (player1Selected == true) {
          paddle1.setColor(userList[4].getPreferredColor());
          selectUser1Button.changeText(userList[4].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player1Selected = false;
          startMatch = true;
          valueFromController = 4;
        }
        if (player2Selected == true) {
          paddle2.setColor(userList[4].getPreferredColor());
          selectUser2Button.changeText(userList[4].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player2Selected = false;
          startMatch = true;
          valueFromController = 5;
        }
      }
    }
    if (userList[5] != null) {
      if (a == 1 && valueFromController == userButtons[5].getButtonIndex()) {
        if (player1Selected == true) {
          paddle1.setColor(userList[5].getPreferredColor());
          selectUser1Button.changeText(userList[5].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player1Selected = false;
          startMatch = true;
          valueFromController = 4;
        }
        if (player2Selected == true) {
          paddle2.setColor(userList[5].getPreferredColor());
          selectUser2Button.changeText(userList[5].getUsername());
          listUsers = false;
          selectUserToAdd = false;
          player2Selected = false;
          startMatch = true;
          valueFromController = 5;
        }
      }
    }
  }


  //*********************************************************************************Add User
  if (createUser == true) {
    //allow for arcade style username adding) (SCREEN #6)

    if (chooseColor == true && firstTimeAdd == true) {
      //Show different paddle colors, example of paddle,
      //and allow user to select with controller (SCREEN #7)
      //INITIALIZE NEW USER OBJECT AND SAVE TO TEXT FILE
      //RETURN TO SELECT USERS
    }
  }

  //***********************************************************************Change User Color
  if (changeUserColor == true) {
    //Create User Button Objects
    for (int i = 0; i < userList.length; i++) {
      if (userList[i].getUsername() != null) {
        userButtons[i] = new Button(i+6, userList[i].getUsername(), width/4, i*height/8);
      } else {
        userButtons[i] = new Button(i+6, "No User", width/4, i*height/8);
      }
    }
    //Display User Button Objects
    for (int i = 0; i < userButtons.length; i++)
    {
      userButtons[i].display();
    }


    //List Users and allow player to select User
    if (listUsers == true && selectUserToColor == true) {
    }

    if (chooseColor == true && firstTimeAdd != true) {
      //Copy User attributes (username and rank)
      //Delete User, bring to a clone of Color Screen in Add users
      //SAVE USERS
      //Return to Main Menu
    }
  }

  //********************************************************************************Delete User
  if (deleteUser == true) {
    //List Users and allow player to select User
    if (listUsers == true && selectUserToDelete == true) {
      //"are you sure?" let player cycle through yes and no
      //if yes,
      //Delete user from text data, and SAVE TEXT
      //Return to Main Menu
      //if no, 
      //return to Main Menu
    }
  }

  //********************************************************************************How To Play
  if (howToPlay == true) {

    down = controllerValues[0];
    up = controllerValues[1];
    b = controllerValues[2];
    a = controllerValues[3];

    if (b == 1) {
      howToPlay = false;
      titleScreen = true;
      valueFromController = 3;
    }
    background(0);
    for (int i = 0; i < drops.length; i++) {
      drops[i].fall();
      drops[i].show();
    }
    //Bring a small menu with text
    textAlign(CENTER, CENTER);
    textFont(titleFont);
    text("HOW TO PLAY", width/2, 2*height/10);
    textFont(infoFont);
    text("Go to 'START MATCH' and select users!", width/2, 4*height/10, width - width/10, height/12);
    text("Once you have selected two players, press 'PLAY GAME'", width/2, 5*height/10, width - width/10, height/12);
    text("To move the paddle, use the sensors to move up and down.", width/2, 6*height/10, width - width/10, height/12);
    text("Press the ability button to use your ability.", width/2, 7*height/10, width - width/10, height/12);
    text("Most importantly, use the B button to go back to the menu.", width/2, 8*height/10, width - width/10, height/12);
    //prompt player to press B button again to return to main menu
  }

  //**************************************************************************************PLAY
  if (transition == true) {
    //Paddle Charge initialize at 0 in this block instead of Play block to avoid accidental re-assignments to 0
    paddle1Charge = 0;
    paddle2Charge = 0;
    player1Lives = 3;
    player2Lives = 3;
    //Initialize gamestart
    gameOver = false;

    if (transitionCount >= 0 && transitionCount <= 100) {
      if (transitionSound.isPlaying() != 1) {
        transitionSound.play();
        transitionSound.amp(0.4);
      }
      for (int i = 0; i < particles.length; i++) {
        particles[i].fall();
        particles[i].show();
      }
      fill(255, alpha);
      noStroke();
      rectMode(CORNER);
      rect(0, 0, width, height);
      alpha += 2.3;
      transitionCount ++;
    } 
    if (transitionCount == 100) {
      delay(1000);
    }
    if (transitionCount > 100 && transitionCount <= 160) {
      background(0);
      for (int i = 0; i < stars.length; i++) {
        stars[i].show();
        stars[i].update();
      }
      rectMode(CORNER);
      strokeWeight(5);
      stroke(255);
      fill(0);
      rect(0, 19*height/20, width, 1*height/20);
      rect(0, 0, width, 1*height/20);
      stroke(255, 170);
      line(width/2, height/20, width/2, 2.375*height/20);
      line(width/2, 3.5625*height/20, width/2, 4.75*height/20);
      line(width/2, 5.9375*height/20, width/2, 7.125*height/20);
      line(width/2, 8.3125*height/20, width/2, 9.5*height/20);
      line(width/2, 10.6875*height/20, width/2, 11.875*height/20);
      line(width/2, 13.0625*height/20, width/2, 14.25*height/20);
      line(width/2, 15.4375*height/20, width/2, 16.625*height/20);
      line(width/2, 17.8125*height/20, width/2, 19*height/20);
      strokeWeight(1);
      //Print paddles(with color) and ball in initial spots
      //Prints players' profiles
      //Count Down (no one can move & ball doesnt move)
      paddle1.display(2);
      paddle2.display(2);
      ball.show(1);
      fill(255, alpha);
      rectMode(CORNER);
      rect(0, 0, width, height);
      alpha -= 4.5;
      transitionCount ++;
      if (battle.isPlaying() != 1) {
        battle.play();
        battle.amp(0.7);
      }
    }
    if (transitionCount > 160 && transitionCount <= 220) {
      background(0);
      rectMode(CORNER);
      strokeWeight(5);
      stroke(255);
      fill(0);
      //Bars with names and lives
      rect(0, 19*height/20, width, 1*height/20);
      rect(0, 0, width, 1*height/20);
      stroke(255, 170);
      //Court Lines
      line(width/2, height/20, width/2, 2.375*height/20);
      line(width/2, 3.5625*height/20, width/2, 4.75*height/20);
      line(width/2, 5.9375*height/20, width/2, 7.125*height/20);
      line(width/2, 8.3125*height/20, width/2, 9.5*height/20);
      line(width/2, 10.6875*height/20, width/2, 11.875*height/20);
      line(width/2, 13.0625*height/20, width/2, 14.25*height/20);
      line(width/2, 15.4375*height/20, width/2, 16.625*height/20);
      line(width/2, 17.8125*height/20, width/2, 19*height/20);
      strokeWeight(1);
      for (int i = 0; i < stars.length; i++) {
        stars[i].show();
        stars[i].update();
      }
      rectMode(CORNER);
      strokeWeight(5);
      stroke(255);
      fill(0);
      rect(0, 19*height/20, width, 1*height/20);
      rect(0, 0, width, 1*height/20);
      stroke(255, 170);
      line(width/2, height/20, width/2, 2.375*height/20);
      line(width/2, 3.5625*height/20, width/2, 4.75*height/20);
      line(width/2, 5.9375*height/20, width/2, 7.125*height/20);
      line(width/2, 8.3125*height/20, width/2, 9.5*height/20);
      line(width/2, 10.6875*height/20, width/2, 11.875*height/20);
      line(width/2, 13.0625*height/20, width/2, 14.25*height/20);
      line(width/2, 15.4375*height/20, width/2, 16.625*height/20);
      line(width/2, 17.8125*height/20, width/2, 19*height/20);
      strokeWeight(1);
      paddle1.display(2);
      paddle2.display(2);
      ball.show(1);
      textFont(titleFont);
      textAlign(CENTER);
      textFont(titleFont);
      text("3", width/2, height/2);
      print('3');
      transitionCount++;
    }
    if (transitionCount > 220 && transitionCount <= 260) {
      background(0);
      for (int i = 0; i < stars.length; i++) {
        stars[i].show();
        stars[i].update();
      }
      rectMode(CORNER);
      strokeWeight(5);
      stroke(255);
      fill(0);
      rect(0, 19*height/20, width, 1*height/20);
      rect(0, 0, width, 1*height/20);
      stroke(255, 170);
      line(width/2, height/20, width/2, 2.375*height/20);
      line(width/2, 3.5625*height/20, width/2, 4.75*height/20);
      line(width/2, 5.9375*height/20, width/2, 7.125*height/20);
      line(width/2, 8.3125*height/20, width/2, 9.5*height/20);
      line(width/2, 10.6875*height/20, width/2, 11.875*height/20);
      line(width/2, 13.0625*height/20, width/2, 14.25*height/20);
      line(width/2, 15.4375*height/20, width/2, 16.625*height/20);
      line(width/2, 17.8125*height/20, width/2, 19*height/20);
      strokeWeight(1);
      paddle1.display(2);
      paddle2.display(2);
      ball.show(1);
      textFont(titleFont);
      textAlign(CENTER);
      text("2", width/2, height/2);
      print('2');
      transitionCount++;
    }
    if (transitionCount > 260 && transitionCount <= 300) {
      background(0);
      for (int i = 0; i < stars.length; i++) {
        stars[i].show();
        stars[i].update();
      }
      rectMode(CORNER);
      strokeWeight(5);
      stroke(255);
      fill(0);
      rect(0, 19*height/20, width, 1*height/20);
      rect(0, 0, width, 1*height/20);
      stroke(255, 170);
      line(width/2, height/20, width/2, 2.375*height/20);
      line(width/2, 3.5625*height/20, width/2, 4.75*height/20);
      line(width/2, 5.9375*height/20, width/2, 7.125*height/20);
      line(width/2, 8.3125*height/20, width/2, 9.5*height/20);
      line(width/2, 10.6875*height/20, width/2, 11.875*height/20);
      line(width/2, 13.0625*height/20, width/2, 14.25*height/20);
      line(width/2, 15.4375*height/20, width/2, 16.625*height/20);
      line(width/2, 17.8125*height/20, width/2, 19*height/20);
      strokeWeight(1);
      paddle1.display(2);
      paddle2.display(2);
      ball.show(1);
      textFont(titleFont);
      textAlign(CENTER);
      text("1", width/2, height/2);
      print('1');
      transitionCount++;
    }
    if (transitionCount > 300 && transitionCount <= 330) {
      background(0);
      for (int i = 0; i < stars.length; i++) {
        stars[i].show();
        stars[i].update();
      }
      rectMode(CORNER);
      strokeWeight(5);
      stroke(255);
      fill(0);
      rect(0, 19*height/20, width, 1*height/20);
      rect(0, 0, width, 1*height/20);
      stroke(255, 170);
      line(width/2, height/20, width/2, 2.375*height/20);
      line(width/2, 3.5625*height/20, width/2, 4.75*height/20);
      line(width/2, 5.9375*height/20, width/2, 7.125*height/20);
      line(width/2, 8.3125*height/20, width/2, 9.5*height/20);
      line(width/2, 10.6875*height/20, width/2, 11.875*height/20);
      line(width/2, 13.0625*height/20, width/2, 14.25*height/20);
      line(width/2, 15.4375*height/20, width/2, 16.625*height/20);
      line(width/2, 17.8125*height/20, width/2, 19*height/20);
      strokeWeight(1);
      paddle1.display(2);
      paddle2.display(2);
      ball.show(1);
      textFont(titleFont);
      textAlign(CENTER);
      text("GO!", width/2, height/2);
      print('g');
      transitionCount++;
    }
    if (transitionCount > 330) {
      playGame = true;
      transition = false;
    }
  }

  //*******************************************************************************GAME START
  if (playGame == true) {
    if (transitionSound.isPlaying() == 1) {
      transitionSound.stop();
    }
    if (gameOver != true) {
      background(0);
      stroke(255, 170);
      line(width/2, height/20, width/2, 2.375*height/20);
      line(width/2, 3.5625*height/20, width/2, 4.75*height/20);
      line(width/2, 5.9375*height/20, width/2, 7.125*height/20);
      line(width/2, 8.3125*height/20, width/2, 9.5*height/20);
      line(width/2, 10.6875*height/20, width/2, 11.875*height/20);
      line(width/2, 13.0625*height/20, width/2, 14.25*height/20);
      line(width/2, 15.4375*height/20, width/2, 16.625*height/20);
      line(width/2, 17.8125*height/20, width/2, 19*height/20);

      for (int i = 0; i < stars.length; i++) {
        stars[i].show();
        stars[i].update();
      }
      player1Values = controllerValues[6];
      player2Values = controllerValues[7];
      player1Ability = controllerValues[4];
      player2Ability = controllerValues[5];
      //Ball starts with Bouncy" code, random direction but,
      //changes direction only by hitting top and bottom, and paddles
      //triggers a switch when it collides with walls of the screen
      //Paddles can freely move, based on the info that distance sensors will be sending,
      //distance sensors will read either 1, or -1, the corresponding directions up, stop, and down
      paddle1.display(player1Values);
      paddle2.display(player2Values);
      //since ball.display actually returns values, 1 and 2 values are used to increment paddle charge
      ballValues = ball.display();
      incrementCharge(ballValues);

      //with each paddle collision, players will build up an "ability charge"
      //about 10 hits for ability
      //ability will be saved and not increase when full
      //ability will vary depending on paddles color
      //Checks if the paddle is fully charged
      if (paddle1Charge == 10 && player1Ability == 1) {
        abilityTimer1 = 0;
        paddle1Charge = 0;
        ability1Used = true;
      }
      //Checks the types of abilities the paddle can perform based on color
      /*
       -Ball Speed Increase ^
       -Paddle Speed Increase
       -Paddle distance Change
       -Paddle color change
       */
      if (ability1Used == true) {
        if (ability.isPlaying() != 1) {
          ability.play();
          ability.amp(0.4);
        }
        switch(paddle1.getColor()) {
        case 1:
          //White Ability
          if (abilityTimer1 <= 200) {
            ball.setSpeed(2);
            ball.setColor(2);
            battle.rate(3);
            spark1 = 0;
            abilityTimer1++;
          }
          if (abilityTimer1 > 200) {
            ball.setSpeed(1);
            ball.setColor(1);
            battle.rate(1);
            abilityTimer1 = 0;
            paddle1Charge = 0;
            ability.stop();
            ability1Used = false;
          }
          break;

        case 2:
          //Red Ability
          if (abilityTimer1 <= 200) {
            paddle1.setSpeed(3);
            battle.rate(3);
            spark1 = 0;
            abilityTimer1++;
          }
          if (abilityTimer1 > 200) {
            paddle1.setSpeed(2);
            battle.rate(1);
            abilityTimer1 = 0;
            paddle1Charge = 0;
            ability.stop();
            ability1Used = false;
          }
          break;

        case 3:
          //Blue Ability
          if (abilityTimer1 <= 200) {
            ball.setSpeed(0.25);
            ball.setColor(2);
            battle.rate(0.5);
            spark1 = 0;
            abilityTimer1++;
          }
          if (abilityTimer1 > 200) {
            ball.setSpeed(1);
            ball.setColor(1);
            battle.rate(1);
            abilityTimer1 = 0;
            paddle1Charge = 0;
            ability.stop();
            ability1Used = false;
          }
          break;

        case 4:
          //Purple Ability
          if (paddle2.getColor() != 666) {
            paddle2TempColor = paddle2.getColor();
          }
          if (abilityTimer1 <= 100) {
            paddle2.setColor(666);
            battle.rate(1);
            spark1 = 0;
            abilityTimer1++;
          }
          if (abilityTimer1 > 100) {
            paddle2.setColor(paddle2TempColor);
            battle.rate(1);
            abilityTimer1 = 0;
            paddle1Charge = 0;
            ability.stop();
            ability1Used = false;
          }
          break;

        case 5:
          //Green Ability
          if (abilityTimer1 <= 200) {
            paddle2.setSpeed(1);
            battle.rate(0.5);
            spark1 = 0;
            abilityTimer1++;
          }
          if (abilityTimer1 > 200) {
            paddle2.setSpeed(2);
            battle.rate(1);
            abilityTimer1 = 0;
            paddle1Charge = 0;
            ability.stop();
            ability1Used = false;
          }
          break;

        case 666: //if the user uses an ability while corrupted
          spark1 = 0;
          paddle1Charge = 0;
          ability1Used = false;
          ability.stop();
          break;
        }
      }

      if (paddle2Charge == 10 && player2Ability == 1) {
        paddle2Charge = 0;
        abilityTimer2 = 0;
        ability2Used = true;
      }  

      if (ability2Used == true) {
        if (ability.isPlaying() != 1) {
          ability.play();
          ability.amp(0.4);
        }
        switch(paddle2.getColor()) {
        case 1:
          //White Ability
          if (abilityTimer2 <= 200) {
            ball.setSpeed(2);
            ball.setColor(2);
            battle.rate(3);
            spark2 = 0;
            abilityTimer2++;
          }
          if (abilityTimer2 > 200) {
            ball.setSpeed(1);
            ball.setColor(1);
            battle.rate(1);
            abilityTimer2 = 0;
            paddle2Charge = 0;
            ability.stop();
            ability2Used = false;
          }
          break;

        case 2:
          //Red Ability
          if (abilityTimer2 <= 200) {
            paddle2.setSpeed(3);
            battle.rate(3);
            spark2 = 0;
            abilityTimer2++;
          }
          if (abilityTimer2 > 200) {
            paddle2.setSpeed(2);
            battle.rate(1);
            abilityTimer2 = 0;
            paddle2Charge = 0;
            ability.stop();
            ability2Used = false;
          }
          break;

        case 3:
          //Blue Ability
          if (abilityTimer2 <= 200) {
            ball.setSpeed(0.25);
            ball.setColor(2);
            battle.rate(0.5);
            spark2 = 0;
            abilityTimer2++;
          }
          if (abilityTimer2 > 200) {
            ball.setSpeed(1);
            ball.setColor(1);
            battle.rate(1);
            abilityTimer2 = 0;
            paddle2Charge = 0;
            ability.stop();
            ability2Used = false;
          }
          break;

        case 4:
          //Purple Ability
          if (paddle1.getColor() != 666) {
            paddle1TempColor = paddle1.getColor();
          }
          if (abilityTimer2 <= 100) {
            paddle1.setColor(666);
            battle.rate(1);
            spark2 = 0;
            abilityTimer2++;
          }
          if (abilityTimer2 > 100) {
            paddle1.setColor(paddle1TempColor);
            battle.rate(1);
            abilityTimer2 = 0;
            paddle2Charge = 0;
            ability.stop();
            ability2Used = false;
          }
          break;

        case 5:
          //Green Ability
          if (abilityTimer2 <= 200) {
            paddle1.setSpeed(1);
            battle.rate(0.5);
            spark2 = 0;
            abilityTimer2++;
          }
          if (abilityTimer2 > 200) {
            paddle1.setSpeed(2);
            battle.rate(1);
            abilityTimer2 = 0;
            paddle2Charge = 0;
            ability.stop();
            ability2Used = false;
          }
          break;

        case 666: //if the user uses an ability while corrupted
          spark2 = 0;
          paddle2Charge = 0;
          ability.stop();
          ability2Used = false;
          break;
        }
      }

      //each player will have 3 lives
      //when ball hits players wall, 
      //they lose a life, ball starts at the beginning of match,
      //with lives changed accordingly
      //since ball.display also returns a 3 or 4 if the outskirts of the map are hit, this will be used
      //to affect players' lives
      if (ballValues == 3 && player1Lives != 0) {
        player1Lives --;
        ball.hide();
        ballOut.play();
        /*for (int i = 0; i < 3000; i++) {
         translate(random(width/5, width/2), random(height/5, width/2));
         }*/
        delay(2000);
        ballOut.stop();
      } else if (ballValues == 4 && player2Lives != 0) {
        player2Lives --;
        ball.hide();
        ballOut.play();
        /*for (int i = 0; i < 3000; i++) {
         translate(random(width/5, width/2), random(height/5, width/2));
         }*/
        delay(2000);
        ballOut.stop();
      }

      //Print Users (GUI)
      rectMode(CORNER);
      strokeWeight(5);
      stroke(255);
      fill(0);
      rect(0, 19*height/20, width, 1*height/20);
      rect(0, 0, width, 1*height/20);
      strokeWeight(1);
      textAlign(CENTER);
      textFont(infoFont);
      fill(255);
      text(selectUser1Button.getButtonName(), width/40, 0.1*height/20, width/10, height/20);
      text(selectUser2Button.getButtonName(), width/40, 19.1*height/20, width/10, height/20);

      //Print Graphics for Ability and Lives
      abilityBar1(paddle1Charge, spark1);
      abilityBar2(paddle2Charge, spark2);
      printP1Lives(player1Lives, paddle1.getColor());
      printP2Lives(player2Lives, paddle2.getColor());

      //Print Ability Names
      if (paddle1Charge == 10) {
        switch(paddle1.getColor()) {
        case 1:
          textAlign(CORNER);
          fill(255);
          text("BALLBOOST", 3.3*width/4, 3*height/80);
          break;

        case 2:
          textAlign(CORNER);
          fill(255);
          text("PADDLEBOOST", 3.3*width/4, 3*height/80);
          break;

        case 3:
          textAlign(CORNER);
          fill(255);
          text("CHILL OUT", 3.3*width/4, 3*height/80);
          break;

        case 4:
          textAlign(CORNER);
          fill(255);
          text("CORRUPT", 3.3*width/4, 3*height/80);
          break;

        case 5:
          textAlign(CORNER);
          fill(255);
          text("SLOWPADDLE", 3.3*width/4, 3*height/80);
          break;

        case 666:
          textAlign(CORNER);
          fill(255);
          text("CORRUPTED", 3.3*width/4, 3*height/80);
          break;
        }
      }

      if (paddle2Charge == 10) {
        switch(paddle2.getColor()) {
        case 1:
          textAlign(CORNER);
          fill(255);
          text("BALLBOOST", 3.3*width/4, 79*height/80);
          break;

        case 2:
          textAlign(CORNER);
          fill(255);
          text("PADDLEBOOST", 3.3*width/4, 79*height/80);
          break;

        case 3:
          textAlign(CORNER);
          fill(255);
          text("CHILL OUT", 3.3*width/4, 79*height/80);
          break;

        case 4:
          textAlign(CORNER);
          fill(255);
          text("CORRUPT", 3.3*width/4, 79*height/80);
          break;

        case 5:
          textAlign(CORNER);
          fill(255);
          text("SLOWPADDLE", 3.3*width/4, 79*height/80);
          break;

        case 666:
          textAlign(CORNER);
          fill(255);
          text("CORRUPTED", 3.3*width/4, 79*height/80);
          break;
        }
      }


      //when one has 0 lives,
      if (player1Lives == 0) {
        ball.hide();
        endGameTimer = 0;
        gameOver = true;
      }
      if (player2Lives == 0) {
        ball.hide();
        endGameTimer = 0;
        gameOver = true;
      }
    }


    //*****************************************************************************GAME OVER
    if (gameOver == true) {
      battle.stop();
      if (win.isPlaying() != 1) {
        win.play();
        win.amp(0.7);
      }
      if (endGameTimer <= 500) {
        background(0);
        for (int i = 0; i < stars.length; i++) {
          stars[i].show();
          stars[i].update();
        }
        rectMode(CORNER);
        strokeWeight(5);
        stroke(255);
        fill(0);
        rect(0, 19*height/20, width, 1*height/20);
        rect(0, 0, width, 1*height/20);
        textAlign(CENTER);
        textFont(infoFont);
        fill(255);
        text(selectUser1Button.getButtonName(), width/40, 0.1*height/20, width/10, height/20);
        text(selectUser2Button.getButtonName(), width/40, 19.1*height/20, width/10, height/20);
        strokeWeight(1);
        //"Username wins!"
        abilityBar1(paddle1Charge, spark1);
        abilityBar2(paddle2Charge, spark2);
        printP1Lives(player1Lives, paddle1.getColor());
        printP2Lives(player2Lives, paddle2.getColor());
        paddle1.show(width/36, height/2);
        paddle2.show(34*width/36, height/2);

        //Print Ability Names
        if (paddle1Charge == 10) {
          switch(paddle1.getColor()) {
          case 1:
            textAlign(CORNER);
            fill(255);
            text("BALLBOOST", 3.3*width/4, 3*height/80);
            break;

          case 2:
            textAlign(CORNER);
            fill(255);
            text("PADDLEBOOST", 3.3*width/4, 3*height/80);
            break;

          case 3:
            textAlign(CORNER);
            fill(255);
            text("CHILL OUT", 3.3*width/4, 3*height/80);
            break;

          case 4:
            textAlign(CORNER);
            fill(255);
            text("CORRUPT", 3.3*width/4, 3*height/80);
            break;

          case 5:
            textAlign(CORNER);
            fill(255);
            text("SLOWPADDLE", 3.3*width/4, 3*height/80);
            break;

          case 666:
            textAlign(CORNER);
            fill(255);
            text("CORRUPTED", 3.3*width/4, 3*height/80);
            break;
          }
        }

        if (paddle2Charge == 10) {
          switch(paddle2.getColor()) {
          case 1:
            textAlign(CORNER);
            fill(255);
            text("BALLBOOST", 3.3*width/4, 79*height/80);
            break;

          case 2:
            textAlign(CORNER);
            fill(255);
            text("PADDLEBOOST", 3.3*width/4, 79*height/80);
            break;

          case 3:
            textAlign(CORNER);
            fill(255);
            text("CHILL OUT", 3.3*width/4, 79*height/80);
            break;

          case 4:
            textAlign(CORNER);
            fill(255);
            text("CORRUPT", 3.3*width/4, 79*height/80);
            break;

          case 5:
            textAlign(CORNER);
            fill(255);
            text("SLOWPADDLE", 3.3*width/4, 79*height/80);
            break;

          case 666:
            textAlign(CORNER);
            fill(255);
            text("CORRUPTED", 3.3*width/4, 79*height/80);
            break;
          }
        }

        if (player1Lives == 0) {
          textAlign(CENTER);
          textFont(titleFont);
          fill(255);
          text(selectUser2Button.getButtonName() + "  WINS!", width/2, height/2);
        }
        if (player2Lives == 0) {
          textAlign(CENTER);
          textFont(titleFont);
          fill(255);
          text(selectUser1Button.getButtonName() + "  WINS!", width/2, height/2);
          //User's amount of wins goes up
        }
        endGameTimer += 1;
      }

      if (endGameTimer >  500) {
        if (player1Lives == 0) {
          //User's amount of wins goes up
          for (int i = 0; i < userList.length; i++) {
            try {
              if (selectUser2Button.getButtonName() == userList[i].getUsername()) {
                userList[i].addRank();
              }
            }
            catch (Exception e) {
            }
          }
        }
        if (player2Lives == 0) {
          for (int i = 0; i < userList.length; i++) {
            try {
              if (selectUser1Button.getButtonName() == userList[i].getUsername()) {
                userList[i].addRank();
              }
            }
            catch (Exception e) {
            }
          }
        }
        //SAVES USER STATS
        userDatabase = createWriter("userdata.txt");
        for (int i = 0; i < userList.length; i++) {
          try {
            userListStrings[i] = userList[i].getUsername() + ',' + str(userList[i].getRank()) + ',' + str(userList[i].getPreferredColor());
          }
          catch (Exception e) {
            userListStrings[i] = "";
          }
        }
        saveStrings("userdata.txt", userListStrings);

        //Fix any glitches during game
        paddle1.setSpeed(2);
        paddle2.setSpeed(2);
        ball.setSpeed(1);
        ball.setColor(1);
        battle.rate(1);
        abilityTimer1 = 0;
        abilityTimer2 = 0;
        paddle1Charge = 0;
        paddle2Charge = 0;

        //Returns to Main Menu
        paddle1.show(width/36, height/2);
        paddle2.show(34*width/36, height/2);
        delay(1000);
        win.stop();
        playGame = false;
        titleScreen = true;
        valueFromController = 0;
        endGameTimer = 0;
      }
    }
  }
}

void serialEvent(Serial controller) {
  String myString = controller.readStringUntil(10);
  if (myString != null) {
    //println(myString);
    String[] serialInArray = split(trim(myString), ",");
    if (serialInArray.length == controllerValues.length) {
      for (int i = 0; i < serialInArray.length; i++) {
        controllerValues[i] = int(serialInArray[i]);
        //print(controllerValues[i]);
      }
    }
  }
}

//ARDUINO CODE
/*
int pins[10];
int i1;
int i2;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  for (int i = 2; i < 8; i++) {
    pinMode(i, INPUT);
  }
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  for (int i = 2; i < 8; i++) {
    if (i != 6 || i != 7) {
      if (pins[i] == 1) {
        Serial.print(0);
        Serial.print(',');
        pins[i] = digitalRead(i);
      }
      else {
        pins[i] = digitalRead(i);
        Serial.print(pins[i]);
        Serial.print(',');
      }
    }
  }
  //Distance Sensors
  i1 = analogRead(A0);
  i2 = analogRead(A1);
  if (i1 > 320) {
    Serial.print(1);
    Serial.print(',');
  } else if (i1 < 320) {
    Serial.print(0);
    Serial.print(',');
  }
  if (i2 > 320) {
    Serial.print(1);
    Serial.print(',');
  } else if (i2 < 320) {
    Serial.print(0);
    Serial.print(',');
  }


  Serial.println(0);
  delay(10);
}
*/

Recitation 5: Serial Communication

SERIAL COMMUNICATION

Materials:
  • Arduino
  • Breadboard
  • Processing
  • 3 LEDs (red, yellow, green)
  • Jumper cables
  • 220R resistors
  • 10K potentiometer
  • USB cable

Initially, I wasn’t sure how I wanted to incorporate sketches going from Processing to Arduino, so I did step 2 before I did step 1. I also decided instead of making two different sketches and circuit I would make one and have it communicated back and forth between Arudino and Processing.

Step 1:

TASK: Create a Processing sketch that sends data to Arduino. Receive that data and express it using physical components (ie. servo, led, buzzer). 

PROCESSING SENDING: the myPort code right after the line of code for ellipse in Processing is the code for sending information to Arduino Uno. Then I followed the myPort.write with a line of print code so I can visually see on Processing the correct data is being sent. Through this part, I learned the “write” code is simply for data communication and the “print” code is for the coder to visually see what’s being communicated. Along with the first myPort.write and print code I also used “else” statements. This is so if the valueFromArduino didn’t fit within the parameters given then it would send data over to Arduino letting it know of the change.

ARDUINO RECEIVING: Ever since we’ve started Processing I’ve been interested in the codes for different colors, and so I knew wanted to make something involving color changes. I thought it would be interesting for the physical aspect to reflect the colors displayed in Processing so I opted for LEDs as the physical component. For Arduino to receive data from Processing I used the “Serial.read” code which would read the value from processing (from the “myPort.write” code). And using “if” statements I coded my Arduino so when the value from Processing is a certain value then digitalWrite will turn the LED on. Since I had three different LEDs (red, yellow, and green) I coded the ellipses on Processing to display the three colors. To keep track, I set the value from Processing for when the red LED would turn on as capital “R” and a lowercase “r” for when the LED would be off. The combination of “if” and “else if” statements coordinated for the designated colored LED to turn on when the same colored ellipse was drawn on Processing.

ISSUES: When I first coded this, the LEDs refused to turn on, so I thought there was something wrong with the code. I had my friends check, double check, and triple checked the, but no one could find anything wrong with the code. But in the end after checking with Professor Rudi, it was a simple mistake of overcrowding the Arduino breadboard.

 

Step 2:

TASK: Create a circuit using your Arduino to send data from physical inputs (ie. variable resistors, buttons, tilt switch) to Processing. Receive the data and express it visually in your Processing sketch.

ARDUINO SENDING: I decided on a potentiometer since I usually have an inclination to use a button, and I wanted to try something different during this recitation. A potentiometer later turned out to be a better choice since it provides the user with a smoother transition feel. In Arduino the sensor, in this case potentiometer, is being read through analogRead and since the values read goes to to the thousands, I had to divide the value by four (/4). Then with the Serial.write code sends the data collected in Arduino over to Processing.

PROCESSING RECEIVING: the myPort line of code in setup informs Processing to read the information from the Arduino port [1], the USB port, when the it’s connected. Then the first line of code (while) in draw equates the data values being sent from Arduino as “valueFromArduino”. Since now Processing is receiving data from Arduino I can now code various actions based on value changes. I wanted to make something that would change as the user turned the potentiometer, so I tested how the value ranges and found out the value went from 0 to around 255. From the values 0 to 50 only a blank blue background would show up and then as the potentiometer turns, the data value will increase and when the value hits 50 a small circle will be drawn. Then as the value increases the circle will also increase in size along with it. I used “if” statements, so the value from Arduino is a requirement for the ellipse to be drawn.

ISSUES: One of the issues I ran into was setting the parameter for the values, such as coding Processing to perform a certain task when the value sent from Arduino is greater than something but less that something else. I initially attempted to code it like a simple math parameter x<value<y, then one of my friends informed me I had to add the “&&” between the two for a logical and code. This solved the problem immediately.

Step 3:

Task: Write a short reflection about why you would use serial communication in your future projects

Serial communication is a good way to make a project more interactive, since it’s translating a physical action into something virtual on your computer screen. This allows the user to feel like they are manually controlling what is happening virtually, and in a way, they are, but it’s just something like Arduino interpreting the physical action into a code which then communicates this to something like Processing which then displays it virtually to the user.

  1. What are the possibilities for interaction that you can envision?

The possibilities are endless, but one of the interactions that come to my mind is how character movements in video games or movies are sometimes made. The serial communication comes in when sensors are attached to real life human beings and as they move the sensors attached sends information which then turns into code and then it’s virtually shown.

 

  1. What are some of the benefits of controlling physical elements with computational media and of controlling screen-based computational media through physical interaction?

It allows for the project to be more interactive, and thus letting the user feel more engaged. Usually, when something is engaging it’ll draw the user in.

 

  1. Can you think of real world applications that take advantage of communication between the physical and screen-based computational media?

To just name a few video games, movies, computers, and phones ae all examples of communication between the physical and screen-based computational media in the real world.

Recitation 4: Animation in Processing

Spinning Rectangle

Step 1

Step 2

After browsing through all of the artworks, I was inspired by Joseph Albers’s study of squares. I found the soft variation of the colors contrasted with the hard edges of the multiple squares interesting. I wanted to code something similar to it but with more colors. I started playing around with different color gradients. The fill for the first rectangle was (51, 0, 25) which was a dark maroon, and with the dark edge the idea of ombre squares came to me. Initially I only had one big maroon/magenta/pink square but one day my laptop died before I could save my code thus I had to start completely over. When I started on a new code I wanted to try a different color to see how it would turn out. The new color turned out to be a dark turquoise (0, 51, 51). I liked both of the colors, so I decided to make a block of the colors with four squares. The decision to rotate the blocks came to me when I was cleaning my room and I saw the design of the fan blades on the air purifier. Another element I added was the rounded edges. Since the colors of the rectangles I made contrasted less than Joseph Albers, I wanted to make the blocks softer, and therefore I took out the sharp edges by adding parameters around the four corners.

 

Issues:

One of the biggest hurdles I had to overcome was to find a way to keep track of the different rectangles since a lot of the coordinates were similar. This was easy to solve since I just had to write out notes beside the lines of code to keep track of which rectangle was which.

Final Product:

The final product of my java code is a rotating block, consisting of four smaller rectangles made up of nine varying sizes of rectangles, with each being 10 pixels apart. Each of the squares have a rounded corner for a smoother look. The big block is rotating as a whole around the center point. I placed the frameRate at a high value of 60 frames per second, so the block would have a smooth transition as it spins.

 

Reflection

  1. Inspired by Joseph Albers’s work, I coded blocks within each other similar to Albers. However, instead of using contrasting colors I code the fill to create a gradient from the darkest color to a almost white in the middle. Albers’s rectangles all had four 90 degree angles, but to continue with the smooth aesthetic of the gradient I opted for rounded edges.
  2. Drawing by hand and drawing with processing is most definitely different. Drawing by hand is simply translating an idea onto paper without thinking about the coordinates; however, with processing everything is about the coordinates. An upside of processing is that it gives its user the option to make something interactive; whereas, a physical artwork is often non-moving. Hand-drawn work are also more difficult to replicate due to various inconsistent elements, but with processing it’s a lot easier to replicate.
  3. I think if images or artworks were to be recreated then the value would be that it has the potential to be mass produced; however, the original piece would still have its value as it is the original authentic thing.
  4. I think Joseph Albers’s artwork is more rigid; whereas, the code I’ve created for recitation is smoother. It evokes similar feelings since they are both overlapping squares; however, other than that I think it’s largely different.
  5. If I was to create multiple variations with the code I’ve created. I would scale the rectangles to where there are multiple rectangles. And just to make it more interesting I would program for it to glow/pulse; which I think would resemble almost a laptop screen saver.