[pcomp week 6] Allergic to Love, Joystick Version (serial communication)

2015-10-14 00.00.59

I ran into a lot of problems this week!

While doing this week’s labs on serial communication, lots of weird things kept happening — it would work sometimes and not other times, my computer crashed, Arduino couldn’t find the right port…

A few hours in, I realized that 95% of my problems were because I kept forgetting to turn on and off the serial monitor in P5. Even when I realized this I would forget to do it, or do it in the wrong order. This was very irritating.

Eventually, I got it sorted out. In the labs, it was suggested that we wire up one button and two potentiometers. Luckily for me, the joystick that I got for this assignment was literally those three things in one, so once I figured out the code on the Arduino side, I could easily move it into my p5 sketch.IMG_0500

The sketch I decided to add a sensor to was a game I made the other week in ICM, Allergic to Love. In the original version, you use your mouse to move your character along the x-axis, and then clicked to shoot your laser. Using a joystick to replace the mouse was fairly straightforward. The pot in the joystick that controls x-axis movement would replace mouseX, and the button on the joystick would replace mousePressed.

(This joystick also allows you to control movement on the y-axis, and even though this particular game doesn’t require it, I decided to keep that code in just in case in the future I decide to add some kind of movement up and down.)

Like in the lab, I made an array of data in P5 for the 3 sensor inputs: x, y, and button press. The P5 code using the sensor data ended up looking like this:

function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  var inString = serial.readStringUntil('rn');
 
  //check to see that there's actually a string there:
  if (inString.length > 0 ) {
    var sensors = split(inString, ',');            // split the string on the commas
    if (sensors.length > 2) {                      // if there are three elements
      if (sensors[0] == 539) { 
          cannon.dir = 0
      } else if (sensors[0] < 200) {
          cannon.dir = -1
      } else if (sensors[0] > 900) {
          cannon.dir = 1
      }
      buttonPress = sensors[2];      // element 2 is the button
    }
  }
}

You can see how I made sensors[0], the x-axis, change direction depending on input, and how I set sensors[2], the button, to a simple variable.

It’s pretty fun to play the game with a joystick. It definitely makes it feel more arcade-y. Even though I had to make it a bit easier to accommodate the joystick, it’s still pretty hard!

My P5 code and my Arduino code are below.

var serial; // variable to hold an instance of the serialport library
var portName = '/dev/cu.usbmodem1411'; // fill in your serial port name here
var buttonPress = 1;
var c, intro;

var hearts = [];
var stars = [];


//sound variables
var laserSound;
var popSound;
var loseSound;
var winSound;
var backgroundSound;
var thudSound;

//booleans
var losePlayed = false;
var winPlayed = false;
var winning = false;

var count = { //counting various hearts in different states
  total: 15,
  broken: 0,
  onGround: 0
}

var laser = {
  y: 500,
  speed: 50
}

var cannon = {
  speed: 10,
  x: 500,
  dir: 0,
  left: -1,
  right: 1
}

var skin = {
  r: 235,
  g: 179,
  b: 169
}

var girl = {
  y: 500,
  dir: 1,
  speed: 0.3
}

var moon = {
  x: 800,
  y: 100,
  speed: 0.3
}

var face = {
  x: 800,
  y: 100,
  speed: 0.3,
}

function lose(size, clr, font) {
  fill(clr);
  strokeWeight(4);
  textSize(size);
  textFont(font);
  text('you only broke ' + count.broken + '/' + count.total + ' hearts. try harder', 100, height / 2);
}

function win(size, clr, font) {
  fill(clr);
  strokeWeight(4);
  textSize(size);
  textFont(font);
  text('congrats, you broke ' + count.broken + '/' + count.total + ' hearts!', 100, height / 2);
  text('love is dead!', 100, height / 2 + 50);
}

function explosion(x, y, w, h) {
  fill(255);
  ellipse(x, y, w, h);
}

function gradiant(g, b) {
  strokeWeight(1);
  for (var i = 0; i < height; i++) {
    stroke(i, g, b);
    line(0, i, width, i);
  }
}

function drawHills(x, y, w, h, clr) {
  noStroke();
  fill(clr);
  ellipse(x, y, w, h);
}

function drawMoon(x, y, w, h, clr) {
  strokeWeight(4);
  fill(clr);
  ellipse(x, y, w, h);
}

function drawFace(x, y, clr, angle1, angle2) {
  strokeWeight(4);
  stroke(clr);
  noFill();
  line(x - 25, y - 25, x - 5, y - 5);
  line(x - 25, y - 5, x - 5, y - 25);
  line(x + 25, y - 25, x + 5, y - 5);
  line(x + 25, y - 5, x + 5, y - 25);
  arc(x, y + 20, 50, 30, angle1, angle2, OPEN);
}

function shoot(x, y) {
  strokeWeight(2);
  stroke(random(0, 255), random(0, 255), random(0, 255));
  line(cannon.x, 522, cannon.x, laser.y);
  laser.y = laser.y - laser.speed;
}

function drawLaser(x, y) {
  noStroke();
  fill(36, 32, 56);
  ellipse(x, y, 70, 50);
  rect(x - 10, y - 50, 20, 70);
  fill(255);
  strokeWeight(1);
  stroke(255);
  push();
  translate(0, -18);
  bezier(x, y, x - 12, y - 12, x - 11, y + 8, x, y + 14);
  bezier(x, y, x + 12, y - 12, x + 13, y + 8, x, y + 14);
  pop();
  strokeWeight(4);
  stroke(255, 0, 0);
  line(x - 10, y - 20, x + 10, y - 3);
  line(x - 10, y - 3, x + 10, y - 20);
}

function drawGirl(x, y, skinColor) {
  //right arm
  stroke(skinColor);
  strokeWeight(10);
  line(x - 60, y - 40, x - 20, y - 20);

  //shirt
  noStroke();
  fill(133, 217, 242);
  triangle(x - 60, y - 70, x - 95, y, x - 25, y); //shirt

  //hair bottom
  fill(0);
  ellipse(x - 60, y - 70, 52, 60);
  quad(x - 80, y - 70, x - 95, y - 50, x - 25, y - 50, x - 40, y - 70);

  //face
  fill(skinColor);
  ellipse(x - 60, y - 65, 50, 50);

  //bangs
  fill(0);
  arc(x - 60, y - 72, 52, 60, 180, 360, CHORD);

  //eyes
  ellipse(x - 70, y - 65, 5, 5); //left
  ellipse(x - 50, y - 65, 5, 5); //right

  //left arm
  stroke(skinColor);
  strokeWeight(10);
  line(x - 80, y - 35, x - 90, y - 30);
  line(x - 90, y - 30, x - 82, y - 23);
}

function Twinkle() {
  this.x = random(width);
  this.y = random(height);
  this.diameter = random(3, 5);
  this.speed = 0.5;

  this.move = function() {
    this.x += random(-this.speed, this.speed);
    this.y += random(-this.speed, this.speed);
  };

  this.display = function() {
    noStroke();
    fill(255);
    ellipse(this.x, this.y, this.diameter, this.diameter);
  };
}

function Falling() {
  this.x = random(width);
  this.y = random(-750, 0);
  this.broken = 0;
  this.hitByLaser = false;
  this.hitTheGround = false;
  this.speed = random(0.5, 2);
  this.clr = color(255, random(0, 255), random(0, 255));

  this.move = function() {
    this.y += this.speed;
  };

  this.display = function() {
    strokeWeight(7);
    stroke(this.clr);
    fill(this.clr);
    bezier(this.x, this.y, this.x - 12, this.y - 12, this.x - 11, this.y + 8, this.x, this.y + 14); //left 
    bezier(this.x, this.y, this.x + 12, this.y - 12, this.x + 13, this.y + 8, this.x, this.y + 14); //right 
  };

}

function preload() {
  popSound = loadSound('assets/pop.wav'); //from http://www.freesound.org/people/muel2002/sounds/266965/
  laserSound = loadSound('assets/laser.mp3'); //from http://www.freesound.org/people/peepholecircus/sounds/169991/
  loseSound = loadSound('assets/lose.wav'); //from http://www.freesound.org/people/AdamWeeden/sounds/157218/
  winSound = loadSound('assets/win.wav'); //from http://www.freesound.org/people/pyzaist/sounds/118655/
  backgroundSound = loadSound('assets/Beatoven_-_02_-_Wanna_See_My_Spaceship.mp3'); // from http://freemusicarchive.org/music/Beatoven/projekt168_v30/
  thudSound = loadSound('assets/thud.wav'); // from https://freesound.org/people/NenadSimic/sounds/149966/

}

function setup() {
  c = createCanvas(1000, 500);
  c.position(200, 100);
  intro = createP("You're allergic to love. Shoot the hearts before they fall down and make you sick.");
  intro.position(200, 50);
  intro.id("text");
  
  // create hearts
  for (var i = 0; i < count.total; i++) {
    hearts.push(new Falling());
    if (backgroundSound.isPlaying()) {
      backgroundSound.stop();
    } else {
      backgroundSound.play();
    }
  }

  // create stars
  for (var s = 0; s < 100; s++) {
    stars.push(new Twinkle());
  }

  serial = new p5.SerialPort(); // make a new instance of the serialport library
  serial.on('list', printList); // set a callback function for the serialport list event
  serial.on('connected', serverConnected); // callback for connecting to the server
  serial.on('open', portOpen); // callback for the port opening
  serial.on('data', serialEvent); // callback for when new data arrives
  serial.on('error', serialError); // callback for errors
  serial.on('close', portClose); // callback for the port closing

  serial.list(); // list the serial ports
  serial.open(portName); // open a serial port
}

function draw() {
  angleMode(DEGREES);
  print(buttonPress);

  //background
  gradiant(30, 160);

  //draw stars
  for (var s = 0; s < stars.length; s++) {
    stars[s].move();
    stars[s].display();
  }

  //draw hills
  drawHills(160, height, 550, 325, color(154, 39, 242));
  drawHills(700, height, 580, 300, color(179, 52, 187));
  drawHills(370, height, 600, 350, color(205, 65, 131));
  drawHills(830, height + 50, 500, 300, color(230, 78, 76));
  drawHills(240, height + 50, 750, 300, color(255, 91, 20));

  //draw face
  drawMoon(moon.x, moon.y, 100, 100, color(248, 166, 255, 140));
  moon.x += random(-moon.speed, moon.speed);
  moon.y += random(-moon.speed, moon.speed);

  //shoots laser when mouse is pressed
  if (buttonPress == 0) {
    shoot(cannon.x, 522);
    if (laserSound.isPlaying() === false) {
      laserSound.rate(0.5);
      laserSound.play()
    }
  } else {
    laser.y = 522;
    laserSound.stop();
  }

  //draw laser 
  drawLaser(cannon.x, height);
  cannon.x += cannon.dir * cannon.speed;
  
  drawGirl(cannon.x, girl.y, color(skin.r, skin.g, skin.b));
  girl.y += girl.dir * girl.speed;
  if (girl.y > 500 || girl.y < 490) {
    girl.dir = -girl.dir;
  }

  //if you're not winning, make her frown. if win, make her smile
  if (winning === false) {
    noFill();
    strokeWeight(1);
    stroke(1);
    arc(cannon.x - 60, girl.y - 50, 15, 12, 180, 360, OPEN);
  } else {
    noFill();
    strokeWeight(1);
    stroke(1);
    arc(cannon.x - 60, girl.y - 55, 15, 12, 360, 180, OPEN);
  }

  //draw falling hearts
  for (var i = 0; i < hearts.length; i++) {
    // if the laser hits the heart..
    if (hearts[i].y <= laser.y + 20 &&
      hearts[i].y >= laser.y - 20 &&
      cannon.x <= hearts[i].x + 25 &&
      cannon.x >= hearts[i].x - 25 &&
      hearts[i].hitByLaser === false) {
      hearts[i].hitByLaser = true; //...make hitByLaser true 
      count.broken++; //...add to the number of broken hearts
      popSound.play();
      explosion(cannon.x, hearts[i].y, 50, 50);
    }

    //if the laser is not hitting the heart...
    if (hearts[i].hitByLaser === false) {
      hearts[i].move(); //...make the hearts fall
      if (hearts[i].y > height) {
        hearts[i].y = height
      }
      hearts[i].display(); //...and display them only until the bottom of the screen
    }

    //if the heart hits the ground...
    if (hearts[i].y == height) {
      if (hearts[i].hitTheGround === false) {
        count.onGround++; //...count number of hearts on the ground
        skin.r = skin.r - 10; //...skin changes color
        skin.g = skin.g + 5;
        skin.b = skin.b + 5;
        hearts[i].clr = (0); //...hearts turn black
        thudSound.play();
      }
      hearts[i].hitTheGround = true;
    }

    //if you don't win...
    if (count.onGround + count.broken == count.total && count.onGround >= 1) {
      lose(50, 255, "Helvetica")
      if (loseSound.isPlaying() === false && losePlayed === false) {
        backgroundSound.stop();
        loseSound.play();
      }
      losePlayed = true;
      drawFace(face.x, face.y, 0, 180, 360);
    }

    //if you do win... 
    if (count.total == count.broken) {
      win(50, color(random(0, 255), random(0, 255), random(0, 255)), "Helvetica");
      if (winSound.isPlaying() === false && winPlayed === false) {
        backgroundSound.stop();
        winSound.rate(2);
        winSound.play();
      }
      winPlayed = true;
      winning = true;
      drawFace(face.x, face.y, 0, 360, 180);
    } else {
      winning = false;
    }
  }
  
  //keep the girl on the screen
  if (cannon.x < 0) {
    cannon.x = 0
  }
  if (cannon.x > width) {
    cannon.x = width
  }
}

// get the list of ports:
function printList(portList) {
 // portList is an array of serial port names
 //for (var i = 0; i < portList.length; i++) {
 // Display the list the console:
 //println(i + " " + portList[i]);
 //}
}
 
function serverConnected() {
 println('connected to server.');
}
 
function portOpen() {
 println('the serial port opened.')
}
 
function serialError(err) {
 println('Something went wrong with the serial port. ' + err);
}
 
function portClose() {
 println('The serial port closed.');
}

function serialEvent() {
  // read a string from the serial port
  // until you get carriage return and newline:
  var inString = serial.readStringUntil('rn');
 
  //check to see that there's actually a string there:
  if (inString.length > 0 ) {
    var sensors = split(inString, ',');            // split the string on the commas
    if (sensors.length > 2) {                      // if there are three elements
      if (sensors[0] == 539) { 
          cannon.dir = 0
      } else if (sensors[0] < 200) {
          cannon.dir = -1
      } else if (sensors[0] > 900) {
          cannon.dir = 1
      }
      buttonPress = sensors[2];      // element 2 is the button
    }

  }
}
const int buttonPin = 8;      // digital input 
 
 void setup() {
   // configure the serial connection:
   Serial.begin(9600);
   // configure the digital input:
   pinMode(buttonPin, INPUT);
   digitalWrite(buttonPin, HIGH);
 }

 void loop() {
   // read the sensor:
   int sensorValue = analogRead(A0);
   // print the results:
   Serial.print(sensorValue);
   Serial.print(",");
 
   // read the sensor:
   sensorValue = analogRead(A1);
   // print the results:
   Serial.print(sensorValue);
   Serial.print(",");
 
   // read the sensor:
   sensorValue = digitalRead(buttonPin);
   // print the results:
   Serial.println(sensorValue);
}

 

One thought on “[pcomp week 6] Allergic to Love, Joystick Version (serial communication)”

  1. I’m glad this worked out well in the end. I think the hearts really need to split in half when they get shot, before they disappear.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: