Final Project – Jumping Man!

Khalas, our game is finally working. Jumping Man is inspired by the hidden game in Chrome. In the Chrome game, the dinosaur is continuously running and jumping to avoid crashing with the obstacles. Similarly, our game involves a lot of jumping in order to survive. Even though computer games are always exciting, we want to add more excitement by creating a physical controller for the game. The physical controller is built on a big trampoline, and we use a pressure sensor to measure the jumping movement.

Screen Shot 2015-12-14 at 5.20.01 PM  IMG_9041

Here is a brief introduction to our game. To start the game, the player is required to stand on the trampoline with both feet on top of the pink area. p.s. We tried to find something with New Media Pink but failed 🙁 Once the game starts, the camera will take a photo of the player. This image is used as jumping man (avatar) in our game. Then the player is ready to jump after he presses ‘s’. To survive, the player has to jump to avoid crashing into our cute obstacles. While jumping, he also has to make sure that he is not shot by the flying Scott on the sky. After the jump, the player is not required to land on the pink areas. However, standing outside the pink area won’t increase the score, so it’s not recommended that the player stands outside the pink area for a long time.

Even though the game seems easy to make, we had a lot of trouble in building the physical controller. Originally, we plan to make a pressure plate switch using cardboard. However, Rong suggested that we use a more elastic material for people to jump on. Therefore, we decided to switch to the trampoline. At first, we put a flex sensor underneath the trampoline, but it’s not getting any data. Therefore, we moved the flex sensor to the surface of the trampoline, but unfortunately, it was broken (see Figure 1). Then we replaced the flex sensor with some conductive fabric. One piece of conductive fabric was taped on the trampoline, and another piece was attached to a shoe. Sadly, the conductive fabric was not giving us an accurate reading, so we switched to pressure sensor in the end. THE PRESSURE SENSOR IS OUR SAVIOR, because it gives an accurate measurement of jumping. We were afraid that people might break the pressure sensor when jumping on it, so we covered the pressure sensor with two pieces of pink foam (see Figure 2). As for the code of our game, we borrowed code from Open CV to capture an image of the head of the player. We have one class called obstacles that controls all the obstacles in the game. Rong drew all the obstacles. She has amazing artistic skills!

IMG_9028 Figure 1

IMG_9032 Figure 2

Even though the game is working, it is not working the same as we expected. If we had more time, we would try to save the game records in an external file. Right now, if the player exits the game, the all-time record will be reset to zero. Saving the records to an external file will help us retrieve the all-time record after we restart the game. Another thing we hope to add is sound. We tried to implement sound effect when people are jumping, but the code for sound was not working properly, so we had to show the silent version.

We would like to thank Scott for finding the trampoline for us. Even though the trampoline did not fit into his car, he decided to carry it on top of his car and drive all the way from the city to Saadiyat (See Figure 3). Without Scott, we could never have built the physical controller. We also want to thank Miha and Luis for providing us with their pressure sensors. The pressure sensor is at the core of our physical controller. Lastly, we would like to thank everyone in our class. It has been an amazing semester learning with and from you guys!

IMG_20151211_182357 Figure 3

 

We used the following pictures in the game:

scott

Mosque

Giraffe

Cloud

Camel

C2

Here’s the code for Arduino

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

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println(analogRead(A0));
  delay(10);
}

Code for Processing:

ArrayList<Obstacle> obstacles;   // arraylist full of sphere called spheres

import processing.serial.*;
Serial myPort;

import processing.sound.*;

//simpleSaveface
import processing.video.*;
import gab.opencv.*;
import java.awt.Rectangle;
OpenCV opencv;
Capture cam;
PImage smaller;
Rectangle[] faces;
int scale = 4;

//Easing
float x;
float y;
float easing = 0.05; //was 0.02

PImage Dinosaur;
PImage cloud;
PImage scott;

float character_xPos;
float character_yPos;
float cloud_xPos;
float cloud_yPos;
float scott_xPos;
float scott_yPos;
float base;
float targetHeight;

int obs_num = -1;
int obs_num2 = 0;
int obs0;
float obstacle_speed = 11;
int gamedistance = 0;
int gamerecord = 0;
int counter = 0;

boolean dead = false; //CHANGE BACK TO 'FALSE'
boolean jump = true;
boolean jump2 = true; 
boolean start = false; //CHANGE BACK TO 'FALSE'
boolean stage1;
boolean stage2;
boolean stage3;
boolean increase;
boolean makeCloud = true;

int randFrame = 90; // the number of frames before the next obstacle
String[] RECORD = new String[5];
String[] readRecord = new String[5];

void setup() {
  size(1366, 768);
  base = height/5*4;

  String portName = "/dev/tty.usbserial-DA00VFL3";
  myPort = new Serial(this, portName, 9600);
  myPort.bufferUntil('\n');

  //simpleSaveface
  Dinosaur = createImage(80, 80, RGB);
  cam = new Capture(this, 1280, 720, 15);
  cam.start();
  opencv = new OpenCV(this, cam.width/scale, cam.height/scale);
  opencv.loadCascade(OpenCV.CASCADE_FRONTALFACE); 
  smaller = createImage(opencv.width, opencv.height, RGB);

  character_xPos = 90;
  character_yPos = base-Dinosaur.height;
  scott_xPos = width;
  scott_yPos = base-Dinosaur.height-350;
  targetHeight = base-Dinosaur.height;

  cloud = loadImage("Cloud.gif");
  cloud.resize(270, 0);

  scott = loadImage("scott.png");
  scott.resize(100, 0);

  obstacles = new ArrayList<Obstacle>();
}


//simpleSaveface
void captureEvent(Capture cam) {
  if (start != true) {
    cam.read();
    smaller.copy(cam, 0, 0, cam.width, cam.height, 0, 0, smaller.width, smaller.height);
    smaller.updatePixels();
  }
}

void draw() {

  //simpleSaveface
  if (start != true) {
    background(0);
    opencv.loadImage(smaller);
    faces = opencv.detect();
    image(cam, 0, 0);
    if (faces != null) {
      for (int i = 0; i < faces.length; i++) {
        strokeWeight(2);
        stroke(255, 0, 0);
        noFill();
        rect(faces[i].x*scale, faces[i].y*scale, faces[i].width*scale, faces[i].height*scale);
      }
    }
    fill(255);
    textSize(24);
    text("Press S to start", 10, height-100);
  } else {
    if (dead != true) {
      //println("Scott yPos: ", scott_yPos);
      //println("WE yPos: ", character_yPos);
      //check stage
      if (gamedistance < 60) {
        stage1 = true;
        stage2 = false;
      }
      if (gamedistance >= 60 && gamedistance <= 100) {
        stage1 = false;
        stage2 = true;
      }
      if (gamedistance >= 100) {
        stage2 = false;
        stage3 = true;
      }

      drawbackground();

      showdistance();
      image(Dinosaur, character_xPos, character_yPos);
      filter(POSTERIZE, 4);
      //filter(THRESHOLD);

      counter += 1;

      for (int i = obstacles.size()-1; i>=0; i--) { // manipulate the objects
        Obstacle s = obstacles.get(i);
        s.update();
        s.render();
        s.checkDeath();
        if (s.obs_death()) {
          obstacles.remove(i); //make it disappear after it reaches the left
        }
      }

      if (counter % 9 == 0) {
        if (increase) {
          gamedistance += 1;
        }
      }

      scottAttack();

      if (counter % randFrame == 0) {
        obs0 = int(random(0, 5));
        obstacles.add(new Obstacle(obs0)); // add a obstacle every 190 counters
        if (stage1) {
          randFrame = 190;
        }
        if (stage2) {
          randFrame = int(random(120, 200));
        }
        if (stage3) {
          randFrame = int(random(100, 120));
        }
      }
    }

    //JUMPPPPPPP!!!
    if (jump) {
      easingJump();
    } else {
      easingFall();
    }

    checkDeath();
  }
}

void keyPressed() {
  if (key == 's') { //start the game
    if (faces != null) {
      for (int i = 0; i < faces.length; i++) {
        PImage cropped = createImage(faces[i].width*scale, faces[i].height*scale, RGB);
        cropped.copy(cam, faces[i].x*scale, faces[i].y*scale, faces[i].width*scale, faces[i].height*scale, 0, 0, faces[i].width*scale, faces[i].height*scale);
        cropped.updatePixels();
        cropped.save("faces/face-"+i+".jpg");
      }
    }

    //simpleSaveface
    Dinosaur.copy(cam, faces[0].x*scale, faces[0].y*scale, faces[0].width*scale, faces[0].height*scale, 0, 0, faces[0].width, faces[0].height);
    Dinosaur.resize(100, 0);

    character_xPos = 90;
    character_yPos = base-Dinosaur.height;
    start = true;
  }

  if (key == 'n') { //restart the game
    dead = false;
    jump = true;
    character_xPos = 90;
    character_yPos = base-Dinosaur.height;
    gamedistance = 0;
    counter = 0;
    start = false; //CHANGE BACK TO 'TRUE'
    scott_xPos = width;
  }
}

void serialEvent(Serial myPort) { //controller
  String input = myPort.readString();
  if (input != null) {
    // trim off any whitespace:
    input = trim(input);
    int value = int(input);

    //  println(value);
    if (value < 100) {
      increase = false;
      if (jump) {
        targetHeight = base-Dinosaur.height-350;
        //easingJump();
      }
      jump = false;
    }
    if (value >= 100) {
      increase = true;
      targetHeight = base-Dinosaur.height;
      jump = true;
      character_yPos = base-Dinosaur.height;
    }
  }
}

void drawbackground() {
  background(255);
  strokeWeight(2);
  stroke(0);
  line(0, base, width, base);
  image(cloud, 910, 0);
}

void scottAttack() {
  image(scott, scott_xPos, scott_yPos);
  if (stage1) {
    scott_xPos -= 5;
  }
  if (stage2) {
    scott_xPos -= 10;
  }
  if (stage3) {
    scott_xPos -= 15;
  }
  if (scott_xPos < 0) {
    scott_xPos = width;
  }
  if (round(character_yPos) <= 190) {
    if (scott_xPos <= character_xPos+Dinosaur.width) {
      dead = true;
      println("Dead");
    }
  }
}

void checkDeath() {
  if (dead) {    

    //gameover screen
    background(255);
    fill(0);
    textSize(100);
    text("Game Over", width/4, height/3);
    textSize(40);
    text("Distance: "+gamedistance, width/4, height/3*2);
    text("All time record: "+gamerecord, width/4, height/3*2+50);

    if (gamedistance > gamerecord) { //update all time record
      gamerecord = gamedistance;
    }

    for (int i = obstacles.size()-1; i>=0; i--) { // remove the previous obstacles
      Obstacle s = obstacles.get(i);
      obstacles.remove(i);
    }
  }
}

void showdistance() {
  fill(0);
  textSize(80);
  text(gamedistance, 1000, 100);
}

void easingJump() {
  float targetY = targetHeight;
  float dy = targetY - character_yPos;
  character_yPos += dy * easing;
}

void easingFall() {
  float targetY = targetHeight;
  float dy = targetY - character_yPos;
  character_yPos += dy * easing;
}

class Obstacle {
  // variables for the object
  PImage arch;
  float arch_xPos;
  float arch_yPos;
  float distance;

  Obstacle(int x_) {
    if (x_ == 0) {
      arch = loadImage("Panda.gif");
      arch.resize(150, 0);
      arch_xPos = width;
      arch_yPos = base-arch.height;
    }
    if (x_ == 1) {
      arch = loadImage("Camel.gif");
      arch.resize(180, 0);
      //println("a1 size:", arch1.width, arch1.height);
      arch_xPos = width;
      arch_yPos = base-arch.height+3;
    }
    if (x_ == 2) {
      arch = loadImage("Giraffe.gif");
      arch.resize(120, 0);
      arch_xPos = width;
      arch_yPos = base-arch.height+3;
    }
    if (x_ == 3) {
      arch = loadImage("Mosque.gif");
      arch.resize(150, 0);
      arch_xPos = width;
      arch_yPos = base-arch.height+5;
    }
    if (x_ == 4) {
      arch = loadImage("C2.gif");
      arch.resize(200, 0);
      arch_xPos = width;
      arch_yPos = base-arch.height;
    }
    //println("Obstacle Created!");
  }

  boolean obs_death() {
    if (arch_xPos + arch.width < 0) {
      return true;
    } else {
      return false;
    }
  }

  // declare all my functions
  void render() {
    image(arch, arch_xPos, arch_yPos);
  }

  void update() {
    arch_xPos -= obstacle_speed;
  }
  void checkDeath() {
    distance = dist(character_xPos, base, arch_xPos, base);
    if (character_yPos == base-Dinosaur.height) {
      if (distance <= Dinosaur.width ) {
        dead = true;
        //println("Dead");
      }
    }
  }
}

 

 

 

Leave a Reply