🌏 👽 SPACE DEFENSE 🚀 🛸

It’s been quite a journey. I put tremendous time and effort trying to perfect the details and coordinations of everything in the game. Hope you like it. At first I had a bit of trouble deciding which idea to stick to, but I am glad I ended up coming through with the “SPACE DEFENSE” game. As same as other final projects for this course, I brought Arduino and Processing together to make the game. When I first designed the development of the game, I broke it down into three main milestones with the respective order of priority:

  1. Processing game design
  2. Arduino architecture
  3. Serial connection

Accomplishing the first milestone, therefore, took up the most time. I initially made the game without the Arduino part and included the use of the keyboard to make the development more convenient and seamless. This is because it seemed rather easier for me to replace the keyboard use with the button use through serial communication than leaving numerous chunks blank. I still commented out not all but most of the parts necessary to run the keyboard to play the game. So find parts as shown below and simply uncomment them to use the keyboard instead! (apart from the uncommenting you need to do some tweaking into the code of course)

In terms of the code in Processing, I have in total of 4 classes (Player, Laser, Item, and Asteroid). I used polygons instead of images on asteroids for three reasons. First, I resized the images in Processing and that made the whole game slow. After talking with Aaron, I realized I could have cropped or resized images outside of Processing. However, there was another reason I preferred not to use an image for an asteroid. I thought it would be awkward to recreate the specific movement effect on an image, and the way my code generated different shapes, sizes, trajectories made the game more resourceful. Lastly, employing shapes instead of simply importing images seemed like a bigger challenge to me so I went with it. This is because these polygons have different shapes and vertices and so the collision effect on them required more thinking and effort than on uniform images.

The biggest challenge for me was controlling and coordinating the movement of different objects. This is because I had to manage a set of situations where different objects collide with one another. The next challenge was serial communication. I just had to spend some time figuring out the use of data transferred from Arduino within different classes. Also because when an error arose I had to check both Processing and Arduino, so that required extra investigation on a broader scope. After completing the general skeleton code of the game, I initially connected the maneuvering of the spaceship to the potentiometer. So depending on the degree of rotation, the spaceship would either move left or right. But after trying this for a while, I realized how inconvenient and clumsy it gets when using the potentiometer. The rotation of the button was simply too difficult to carry out that it was annoying to invest so much energy when playing the game. Below is a little sneak peak of how the potentiometer was set up in my Arduino board at first.

With this problem and user experience in mind, I decided to use push buttons. I also decided to get rid of UP & DOWN buttons because it made physiologically little sense that LEFT & RIGHT & UP & DOWN buttons are all aligned next to each other. So the spaceship was only allowed to move sideways and this was enough. I then added two push buttons – one for shooting bullets and the other for resetting the game. Below is how my Arduino looked like in the end:

In terms of the design, I tried to make it sort of retro. The song, the font, the color, the spaceship have all been designed in a way that it makes you feel nostalgic (at least for me). The sound track of the game is from a 1996 arcade game called “Out Run”, which is a car racing game but it also suits well in my game in my opinion. Here are the images and sounds I used.

starship (player)

earth (item)

ouch.mp3 (when the player collides with an asteroid)

laser.mp3 (when shooting bullets)

soundtrack.mp3 (sound track from Out Run 1996 arcade game)

life.mp3 (when the player gets an item and health goes up)

crash.mp3 (when an asteroid collides with the land)

 

Of course, a simple set of instructions are displayed before the game is initiated with the game title on top. I did not add any extra effects on this part because it is retro. To talk about how the game is run, the player can maneuver the starship left and right in order to shoot bullets at the falling “asteroids” that are polygons. I have limited the maneuver to left and right for the reason mentioned earlier. If a player collides with an asteroid he/she will lose 2 health points. If an asteroid collides with the earth, the health will also decrease by 10, as it is a fatal and collateral damage to the global community. If a player successfully hits an asteroid with a bullet, the asteroid will vanish and will be deducted from the total number of asteroids in the current level. There will be earth-looking items falling too and you should shoot at it to get 10+ on your health. Your level goes up when you eliminate all the asteroids in the level. Each level you go up, there will be 1 less earth item and 2 more asteroids. Without further ado, here’s a demo video.

// Main

import ddf.minim.*;

/************* Serial **************/
import processing.serial.*;
Serial myPort;
int xPos=0;
int left, right, b, reset;
/************* Serial **************/

Player player; 
Item item;
PFont font, font2;
PImage starship, earth, galaxy;
ArrayList<Asteroid> asteroids;
ArrayList <Laser> laser; 
ArrayList<Item> items;
boolean instruction = true;
boolean [] keys = new boolean[128];
String message;
int[] land;
int health, asteroidCount, itemCount;
int asteroid_ = 0;
int item_ = 0;
int level = 1;
int count = 1;

//sound effect
Minim minim;
AudioPlayer soundtrack, sound, sound2, sound3, sound4;

void setup() {
  size(480, 640);
  
  if (count == 1){
    /************* Serial **************/
    printArray(Serial.list());
    String portname = Serial.list()[5];
    println(portname);
    myPort = new Serial(this,portname,9600);
    myPort.clear();
    myPort.bufferUntil('\n');
    /***********************************/
    }
  
  // Sound effect processing
  minim = new Minim(this);
  soundtrack = minim.loadFile("soundtrack.mp3");
  sound = minim.loadFile("laser.mp3");
  sound2 = minim.loadFile("crash.mp3");
  sound3 = minim.loadFile("ouch.mp3");
  sound4 = minim.loadFile("life.mp3");
  sound2.setGain(-10);

  // Images
  starship = loadImage("starship.png");
  earth = loadImage("earth.png");
  
  // Font
  font2 = createFont("Cambria-bold",60);
  font = createFont("Phosphate-Inline",60);
  
  // asteroid_ ==  0 means when you start the game
  // else meanse after Level 1
  health = 100;
  if (asteroid_ == 0 && count == 1){
    asteroidCount = 10;
    itemCount = 5;
    soundtrack.rewind();
    soundtrack.play();
  }
  else {
    asteroidCount = 10 + asteroid_;
    itemCount = 5 + item_;}

  /********** Initiating Objects ***********/
  // Player
  player = new Player();
  // Meteor
  asteroids = new ArrayList<Asteroid>();
  for (int i=0; i < asteroidCount; i++) { 
    asteroids.add(new Asteroid());}
  // Bullet
  laser = new ArrayList();
  // Earth item
  items = new ArrayList<Item>();
  for (int i=0; i < itemCount; i++) { 
    items.add(new Item());}
 /*******************************************/

  // Background
  land = new int [width/10+1];
  for (int i = 0; i < land.length; i++) { 
    land[i] = int(random(10));}
    
  message = "Level ";
  
}

void draw() {
  
  
  if (instruction){
    galaxy = loadImage("instruction_background.jpg");
    //Using the width and height of the photo for the screen size
    galaxy.resize(width,height); 
    image(galaxy,0,0);
    textAlign(CENTER);
    textFont(font);
    fill(#FFFF00);
    text("SPACE DEFENSE",width/2,height/2-120);
    textFont(font2);
    textSize(20);
    fill(255);
    text("Protect the planet from falling asteroids!\n* * *\nPress YELLOW to move left\nPress BLUE to move right\nPress RED to shoot\nPress GREEN to restart\n* * *\nClick anywhere to proceed",width/2,height/2);
    return;
  }
  
  background(0);
  fill(255);
  noStroke();
  ellipse(random(width), random(height-50), 3, 3);

  textSize(32);
  fill(255);
  text(message+level, width/2, 100);

  drawEarth();

  for (int i= asteroids.size()-1; i >= 0; i--) {
    Asteroid p = asteroids.get(i);
    p.update();
    p.display();
    if (player.checkCollision(p)) {
      if (health > 0) {
        health -= 2;
      }
      sound3.rewind();
      sound3.play();
    }
    for (int j= laser.size()-1; j >= 0; j--) {
      Laser b = laser.get(j);
      // If an asteroid appears on screen and collides with a bullet
      if ((p.pos.y + p.r > 0) && (p.checkCollision(b))) {
        if (asteroids.size() > 0){
          asteroids.remove(i);
          laser.remove(j);
          asteroidCount = asteroids.size();
        }
        sound2.rewind();
        sound2.play();
      }
    }
  }

  for (int i= items.size()-1; i >= 0; i--) {
    Item it = items.get(i);
    it.update();
    it.display();
    for (int j= laser.size()-1; j >= 0; j--) {
      Laser b = laser.get(j);
      // If an item appears on screen and collides with a bullet
      if ((it.po.y + 25) > 0 && (it.checkCollision(b))) {
        if (items.size() > 0){
          laser.remove(j);
          items.remove(i);
          if (health >= 100) {
            continue;
          }
          health += 10;
          sound4.rewind();
          sound4.play();
        }
      }
    }
  }

  if (asteroids.size() <= 0) {
    delay(1000);
    count = 0;
    asteroid_ += 2;
    item_ -= 1;
    setup();
    level += 1;
    
  }

  player.display();
  player.move();

  for (int i= laser.size()-1; i >= 0; i--) {
    Laser b = laser.get(i);
    b.move();
    b.display();
  }
  
  if (b == 1) {
    Laser temp = new Laser(player.pos.x+16, player.posy);
    laser.add(temp);
    sound.rewind();
    sound.play();
  }
  
  if (reset == 0) {
    count = 0;
    asteroid_ = 0;
    item_= 0;
    level = 1;
    setup();
  }

  textSize(32);
  if (health > 0) {
    text(health, width - 100, 50);
  } else {
    text(0, width - 100, 50);
  }

  if (health <= 0) {
    String over = "Game Over";
    textSize(60);
    text(over, width/2, height/2);
  }
  textSize(32);
  fill(255, 0, 0);
  text(asteroidCount, 100, 50);
}


void asteroidShape(float a, float b, float r, int vertices) {
  float x = map(r, 0, 40, 50, 255);
  float degree = TWO_PI / vertices;
  color col = color(x/2, x/3, 100);
  beginShape();
  for (float i = 0; i < TWO_PI; i += degree) {
    float sx = a + cos(i) * r;
    float sy = b + sin(i) * r;
    fill(col);
    noStroke();
    //curveVertex(sx, sy);
    vertex(sx, sy);
  }
  endShape(CLOSE);
}


void drawEarth() {

  fill(250, 150, 0, 60); 
  noStroke();
  beginShape();
  vertex(0, 640);
  for ( int i=0; i < land.length; i++) {
    vertex( i * 11, 640 - 40 - land[i] );
  } 
  vertex(480, 640);
  endShape(CLOSE);
}


void mousePressed() {
  if (instruction) {
    instruction = false;
  }
  //level = 1;
}


void keyPressed() {
  keys[keyCode] = true;
}


void keyReleased() {
  keys[keyCode] = false;
}



/************* Serial **************/
void serialEvent(Serial myPort){
  String s = myPort.readStringUntil('\n');
  s = trim(s);
  if (s != null){
    int values[] = int(split(s,','));
    if (values.length == 4){
      left = int(values[0]);
      right = int(values[1]);
      b = int(values[2]);
      reset = int(values[3]);
    }
  }
  myPort.write('0');
  
  // For key board use:
  //float xPos = myPort.read();
  //player.pos.x = map(xPos, 0.0, 225.0, 0.0, 390.0)
  //myPort.write(0);
}
/************* Serial **************/
// Laser class

class Laser{
  PVector p;
  PVector a;
  PVector v;
  int w, h;
  
  Laser(float tx, float ty){
     p = new PVector(tx, ty);
     a = new PVector(0,-0.2);
     v = new PVector(0, -2.5);
  }
  
  void display(){
     noStroke();
     fill(random(255), random(255), random(255));
     rect(p.x, p.y, 5, 12);
  }
  
  void move(){
    v.add(a);
    p.add(v);
  }
}
// Item class

class Item{
  PVector po;
  PVector ac;
  PVector ve;

  Item(){
    po = new PVector(random(width), random(-height, 0));
    ac = new PVector(0, 0.001);
    ve = new PVector(0, 0);
  }
  
  void update(){
    ve.add(ac);
    ve.limit(1);
    po.add(ve);
 
    // Readjust location if the player goes off screen on the side
    if((po.x < -25) || (po.x > width + 25)){
      po.x = random(width);
      po.y = random(-height+100, 0);
    }
    if (po.y > height - 25){
      po.y = random(-height+100,0);
      po.x = random(width);
      // meteor crash
      sound2.rewind();
      sound2.play();
    }
  }
  
  void display(){
    pushMatrix();
    translate(po.x, po.y);
    image(earth, 0, 0);
    popMatrix();
  }
  
  
  boolean checkCollision(Laser b){
    if((b.p.y + 6) <= po.y + 25 && b.p.x >= po.x - 10 && b.p.x <= po.x + 50){
      sound.rewind();
      sound.play();
      return true;
    } 
    else {
      return false;
    }
  }
}
// Asteroid class

class Asteroid{
  PVector pos;
  PVector acc;
  PVector vel;
  int vertices;
  float r;
  color c;

  Asteroid(){
    pos = new PVector(random(width), random(-height, 0));
    acc = new PVector(random(-0.1, 0.1), random(0.1, 0.7));
    vel = new PVector(0, 0);
    vertices = int(random(3, 15)); // what type of polygon? triangle ~ 15-gon
    r = random(10, 45);
  }
  
  void update(){
    vel.add(acc);
    vel.limit(2);
    pos.add(vel);
    
    // Readjust location if the player goes off screen on the side
    if((pos.x + r < 0) || (pos.x - r > width)){
      pos.x = random(0, width);
      pos.y = random(-height, 0);
    }
    if (pos.y + r > height){
      if (health > 0){
        health -= 10; // reduce health
      }
      pos.y = random(-height,0);
      pos.x = random(0, width);
      // meteor crash
      sound2.rewind();
      sound2.play();
    }
  }
  
  void display(){
    pushMatrix();
    translate(pos.x, pos.y);
    rotate(frameCount / 50.0);
    asteroidShape(0, 0, r, vertices);
    popMatrix();
  }
  
  boolean checkCollision(Laser b){
    if((b.p.y + 6) <= pos.y + r && b.p.x >= pos.x - r && b.p.x <= pos.x + r){
      sound.rewind();
      sound.play();
      return true;
    } 
    else {
      return false;
    }
  }
}
// Player class

class Player{
  PVector acc;
  PVector vel;
  PVector pos;
  float posy = height-100;
  
  Player(){
    pos = new PVector(width, height - 100);
    acc = new PVector(0.2, 0.2);
    vel = new PVector(0,0);
  }
  
  void display(){
    image(starship, pos.x, posy); 
  }
  
  void move(){
    acc.normalize();
    vel.mult(5);
    vel.limit(10); 
    vel = vel.add(acc);
    
    if(left == 1){
      pos.x -= vel.x;}
    if(right == 1){
      pos.x += vel.x;}
    
    /* Un-comment for keyboard use
    
    if(keys[LEFT]){
      pos.x -= vel.x;}
    if(keys[RIGHT]){
      pos.x += vel.x;}
    if(keys[UP]){
      pos.y -= vel.y;}
    if(keys[DOWN]){
      pos.y += vel.y;}
    if(pos.y >= height - 50){
      pos.y = height - 82.5;}
    _____________________________*/
    
    if(pos.x < -19){
      pos.x = width -19; 
    }
    if(pos.x > width - 19){
      pos.x = -19;  
    }

  }
  
  boolean checkCollision(Asteroid p){
    if (dist(pos.x, posy, p.pos.x, p.pos.y) <= p.r) {
      textSize(24);
      text("Collision!", width - 60, height - 12);
      return true;
    } 
    else {
      return false;
    }
  }
}
// Arduino

int turnOn = 1;
int prevButtonState = 0;

void setup(){
  Serial.begin(9600);
  Serial.println("0,0,0,0");
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  }

void loop(){
  if(Serial.available()>0){
    char incoming = Serial.read();
    int a1 = digitalRead(2);
    delay(1);
    int a2 = digitalRead(3);
    delay(1);
    int a3 = digitalRead(4);
    delay(1);
    int a4 = digitalRead(5);
    delay(1);

    Serial.print(a1);
    Serial.print(",");
    Serial.print(a2);
    Serial.print(",");
    if (a3 == 0 && prevButtonState == 1) {
      Serial.print(turnOn);
    }
    prevButtonState = a3;
    Serial.print(",");
    Serial.print(a4);
    Serial.println();
    }
  }

 

Here’s also a zip file of my game. Hope you enjoy!

Main2

[UPDATED] Refined Final Project Idea + Progress

Last time I spoke in class, I was very much set on the idea of making a “big brother” surveillance camera. BUT, I realized that I would require a few equipments that are not in our sparkfun arduino kit. These equipments included a set of female jump wires, a separate breadboard(?) that can be attached to the servo, and another type of converter looking wire that I forgot the name of. Due to these technical difficulties, I thought my surveillance camera would become boring to the audience and so I decided to switch to a different idea.

Therefore, I resorted to my initial idea, which was to make a “spacewar” game. It is a game in which a spaceship (player) defends objects falling towards the earth from the outer-space. This has always been one of my favorite games since childhood and I loved the idea that I could realize it on my own.

Space War X on the App Store

I am currently in the process of developing the processing part first. I will get to the arduino once I have a working version on my computer. The arduino part will be incorporated with a push buttons to shoot shrapnels/bullets, another push button to accelerate, and a potentiometer to maneuver left and right directions.

I have accomplished my first milestone which is to have the starship maneuver and shoot bullets. I am not entirely sure if I am satisfied with the sprite that I am using, so this is subject to change. Below I have attached the video of the working version of my first milestone and the codes.

/*
David Lee
Main Code - version w/o arduino
*/

ArrayList <Bullet> bullets; 
Player player; 
PImage starship;

boolean [] keys = new boolean[128];


void setup(){
  size(480, 640);
  bullets = new ArrayList();
  player = new Player();
  starship = loadImage("starship6.png");
}

void draw() {
  background(0);
  for(Bullet temp : bullets){
    temp.move();
  }
  for(Bullet temp : bullets){
    temp.display(); 
  }
  //removeToLimit(30);
  
  player.display();
  player.move();
}


void keyPressed(){
  keys[keyCode] = true;
  if(keys[32]){
     Bullet temp = new Bullet(player.pos.x+80, player.pos.y);
     bullets.add(temp);
  }
}

void keyReleased(){
  keys[keyCode] = false;
}

void removeToLimit(int maxLength){
    while(bullets.size() > maxLength){
       bullets.remove(0); 
    }
  }
//Bullet Class

class Bullet{
  PVector pos;
  PVector acc;
  PVector vel;
  
  Bullet(float tx, float ty){
     pos = new PVector(tx, ty);
     acc = new PVector(0,-0.2);
     vel = new PVector(0, -2.5);
  }
  
  void display(){
     stroke(255);
     //point(x, y);
     rect(pos.x, pos.y, 3, 10);
  }
  
  void move(){
    vel.add(acc);
    pos.add(vel);
     //pos.y -= 2.5;
    
  }
}
//Player Class

class Player{
  PVector pos;
  PVector acc;
  PVector vel;
  
  Player(){
    pos = new PVector(width/2, height - 100);
    acc = new PVector(0.0, 0);
    vel = new PVector(4,4);
  }
  
  void display(){
    image(starship, pos.x, pos.y); 
  }
  
  
  void move(){
    vel = vel.add(acc);
    if(keys[LEFT])
      pos.x -= vel.x;
    if(keys[RIGHT])
      pos.x += vel.x;
    if(keys[UP])
      pos.y -= vel.y;
    if(keys[DOWN])
      pos.y += vel.y;
      
    if(pos.x < -50){
      pos.x = width; 
    }
    if(pos.x > width){
      pos.x = -50;  
    }
    if(pos.y > height){
      pos.y = -59; 
    }
    if(pos.y < -59){
      pos.y = height; 
    }
  }
}

 

[Updated] My final project idea 💡

I am still torn between a few ideas in my mind… But the idea I am leaning towards the most at the moment is the following:

Idea:

An arcade game. The setting of the game is in the space amidst a space war. ‘You will have to defeat the alien spaceships by using the laser beams on your vessel to protect the earth.’

Arduino-side:

The player will be able to maneuver the player using 2~3 push buttons (for items and miscellaneous skills) and a potentiometer.

Processing-side:

The visual of the game will contain a working sprite of a spaceship, background image of the space, images of the bullets, alien spaceships, items, etc. The game will contain different levels. The screen won’t convert on different levels, but rather there will be a display of level numbers and the difficulty of the game will increase. Might include a level consisting a different theme for fun.

I will update this post if my idea/plan changes overtime.

-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-

After Monday’s class and having listened to other friends’ ideas, I thought to myself, ‘I do not necessarily need to make a game do I…?’.

So I decided to switch to a different topic. I am still not 100% sure about the idea so I expect to have my final project idea more tailored by the end of today’s class.

New idea: A radar turret. I would like to make a rotating turret using arduino, with an ultrasonic sensor attached to a servo, in order to create a scouting(?) surveillance sensor. I would like to use processing to display any objects that are recognized by the sensor to alert the user monitoring the environment.

Arduino + Processing

I was probably most clueless for this week’s assignment when I started planning for it. I initially tried making numerous different games including, aligning moving objects, shooting game, and so on. I resorted to this game because it is the most neatly operating game in my opinion. I am not a huge gamer myself, but I sometimes get fascinated (or close to getting addicted to) by smartphone app games that are very simple yet clear on their objectives.

The objective of this game is to align the circles. To be more specific, for each round there will be a circle randomly generated by variating radius. The circle will be centered in the middle of the scree, but its size will differ every round. You, as a player, have to align your circle by resizing it and adjusting it to the generated circle. The resizing will be carried out by moving the potentiometer. So, when you move the potentiometer to the right, the circle gets bigger, and when you move the potentiometer to the left, the circle gets smaller. You will repeat this until you have successfully overlapped 3 circles in total. You will be timed each round on your performance and the completion time will be posted on the scoreboard.

To talk a bit about the design and the location of different elements, you will see a scoreboard on your left that is updated after every 3 circles, and you will also be able to see how many circles you have aligned so far on the right-hand side. On the top segment, you will see the time. Most importantly, you will see the two circles in the center.  Without further ado, enjoy!

How this works:

Arduino code:

void setup() {
  Serial.begin(9600);
  Serial.println('0');
  pinMode(2, OUTPUT);
}

void loop() {
  if(Serial.available()>0){
    char inByte=Serial.read();
    int sensor = analogRead(A0);
    delay(1);
    Serial.println(sensor);
    digitalWrite(2, inByte);
  }
}

Processing code:

PFont easyfont;
int now_time = 0;
import processing.serial.*;
Serial myPort;
int radius=10;
float initial_rad = 30;
float time = 0;
boolean game_start = false;
int c = 0;
int count = 0;
float[] scoreboard = new float[10];
String[] timestamp = new String[10];

void setup(){
  size(600,400);
  easyfont = createFont("arial", 50);
  now_time = millis();
  smooth();
  //printArray(Serial.list());
  String portname=Serial.list()[4];
  //println(portname);
  myPort = new Serial(this,portname,9600);
  myPort.clear();
  myPort.bufferUntil('\n');
}

void draw(){
  background(255);
  fill(#00cc33);
  textSize(24);
  text("Overlap 3 circles as fast as you can...", width/2, height-20);
  textAlign(CENTER,CENTER);
  textFont(easyfont);
  textSize(24);
  text(nf(time,0,2)+" sec", width/2, 25);
  noFill();
  strokeWeight(10);
  circle(width/2,height/2,radius);
  
  textSize(10);
  fill(0);
  text("Number of circles so far: " + c, width-70, 25);
  
  if(abs(initial_rad - radius) < 3){
    fill(#FFC300);
    textSize(48);

    game_start = false;
    initial_rad = random(1,height);
    c += 1;
    if(c == 3){
      c = 0;
      println(nf(time,0,2));
      now_time = millis();
      scoreboard[count] = time;
      timestamp[count] = str(hour())+":"+str(minute())+":"+str(second());
      printArray(timestamp);
      count += 1;
    }
  }
  else{
    time = (millis()-now_time)/1000f;
  }
  game_start = true;
  noFill();
  circle(width/2, height/2, initial_rad);
  
  for(int i=0; i<10; i++){
    textSize(20);
    text("SCOREBOARD", 80, 25);
    textSize(18);
    text(timestamp[i] + "  " + scoreboard[i], 70, 60+i*20);
  }
}

void serialEvent(Serial myPort){
  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null){
    radius=(int)map(int(s),0,1023,0, height);
  }
  myPort.write('0');
}

 

Musical instrument with an ultrasonic distance sensor

This week’s assignment was my favorite arduino task thus far. I think it’s because the final outcome seems more reusable for other creative tasks. I used an ultrasonic distance sensor, a push button, and a piezo buzzer to emulate the interactive artwork Aaron once introduced to us in the beginning of the course (tried to find the artwork to show what I was inspired by but was never successful…).

The way it works is very simple. The push button initiates the sound, so only when the button is pressed down, you will be able to hear the notes. You can variate the distance between your hand (or any surface) and the ultrasonic distance sensor to create 3 different notes – middle C (~10 cm), middle D (~15 cm), and middle E (~20 cm). The hand should not move away from the sensor for more than 20 cm for the sound to be activated. Once these conditions are met, the buzzer will emit sound that is corresponding to the notes described above.

While making this instrument, it was important to note the formula for distance that sound travels. This was crucial for me to determine the distance range for each note. Another thing I had to be aware is the fact that echo pin takes in the number for total distance of the sound travel. In other words, I had to be aware that this number represented the round-trip of the sound against the surface and that it had to be halved to obtain the actual distance between the sensor and the object.

Without further ado, here is the video of my working instrument. Please bear in mind that I changed the note after making this video and the notes are now slightly lower and more accurate (Do, Re, Mi).

#define NOTE_C4  262
#define NOTE_D4  294
#define NOTE_E4  330

int trig = 13;
int echo = 12;
int piezo = 11;
int buttonPin = 3;
int buttonState = 0;

void setup() {
    Serial.begin (9600);
    pinMode(trig, OUTPUT);
    pinMode(echo, INPUT);
    pinMode(buttonPin, INPUT);
}

void loop() {
    int time;
    int distance;
    
    digitalWrite(trig, HIGH);
    delay(0.01);
    digitalWrite(trig, LOW);
    
    time = pulseIn(echo, HIGH);
    
    //speed of sound = 343 m/s = 0.0343 cm/µs
    // distance = time * speed
    distance = time * 0.0343 / 2; // sound wave travels forward and bounces backward.

    Serial.print(distance);
    Serial.println(" cm");

    buttonState = digitalRead(buttonPin);

    if( buttonState == HIGH ) {
      if (distance > 20 || distance < 0){
        noTone(piezo);
        }
      else if (distance < 10){
        tone(piezo, NOTE_C4);
        }
      else if (distance < 15){
        tone(piezo, NOTE_D4);
        }
      else{
        tone(piezo, NOTE_E4);  
        }
    }
    else {
      noTone(piezo);
    }
    delay(100);
}

 

Playing with multiple LEDs using a light sensor and a push button!

I created a project where you can play with both analog input and digital input to activate different actions on the LED lights. To begin with, I have installed a photoresistor so that different number of lights can be turned on depending on the light intensity. With the least bright light switching on the red LED, it can go all the way up until the brightest light switching on the green LED. Another aspect I have added to this is the usage of a push button to initiate a collective blinking reaction. All four lights will blink four times and will not be interfered with the light during this process. The LEDs will go back to reacting to light after this. I initially wanted to use the potentiometer but I wanted to show something others have not already done so I chose to make use of the light sensor.

Here is my code:

int photoresistor = A0;
int yellow = 2;
int blue = 3;
int red = 4;
int green = 5;
int button = 6;


void setup() {
  pinMode(yellow, OUTPUT);
  pinMode(blue,OUTPUT);
  pinMode(red,OUTPUT);
  pinMode(green,OUTPUT);
  pinMode(button, INPUT);
}


void loop() {
  
  int button1 = digitalRead(button);
  if (button1 == HIGH) {
    digitalWrite(yellow, HIGH);
    digitalWrite(blue, HIGH);
    digitalWrite(red, HIGH);
    digitalWrite(green, HIGH);
    delay(2000);
    
    int count = 0;
    while (count < 5){
      digitalWrite(yellow, HIGH);
      digitalWrite(blue, HIGH);
      digitalWrite(red, HIGH);
      digitalWrite(green, HIGH);
      delay(20);                  
      digitalWrite(yellow, LOW);
      digitalWrite(blue, LOW);
      digitalWrite(red, LOW);
      digitalWrite(green, LOW);
      delay(1000); 
      count += 1;
      }
  }
  
  else{
    
    int analogValue = analogRead(photoresistor);
    if(analogValue < 50){            
      digitalWrite(red, HIGH);
    }
    else if(analogValue >= 50 && analogValue < 100){
      digitalWrite(blue, HIGH);
    }
    else if(analogValue >= 100 && analogValue <= 150){
      digitalWrite(yellow, HIGH);
    }
    else{
      digitalWrite(green, HIGH);  
    }
    
    delay(100);
    
    digitalWrite(yellow, LOW);
    digitalWrite(blue, LOW);
    digitalWrite(red, LOW);
    digitalWrite(green, LOW);
  }
}

 

Here is the video of how it works:

 

Arduino assignment #1: “Align 3 LED lights” – UPDATED

The LED puzzle I am made is a sort of game where 3 different LED lights randomly turn on at different times and speed. There will come a point when all 3 lights are on at the same time, and this is when a player should press the button. If a player has pressed the button at this exact time of alignment, then all the lights will blink 7 times, signifying the win of the game.

I wanted to make this a game of speed and simplicity. I am personally very happy with what I have produced because it looks neat to me. One thing a player would have to be aware of is the pressing of the button because sometimes the button does not come in full contact with the board and so it leads to misses. Would love to learn to incorporate music to this game later in the future.

INITIAL CODE (didn’t work):

int ledPin = 2;

int ledPin2 = 4;
int buttonPin2 = 5;

int ledPin3 = 6;

int buttonState2 = 0;

void setup() {
  // put your setup code here, to run once:
  pinMode(ledPin, OUTPUT);
  
  pinMode(ledPin2, OUTPUT);
  pinMode(buttonPin2, INPUT);

  pinMode(ledPin3, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  int buttonState2 = digitalRead(buttonPin2);
  
  if (buttonState2 == HIGH) {
    Serial.println("button");
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin, HIGH);
    } else {
      // turn LED off:
      while (buttonState2 != HIGH){
        int n = 2*random(1,4);
        int m = 2*random(1,4);
        int p = 2*random(1,4);
        
        //Serial.begin(9600);
        //Serial.println(n);
        
        digitalWrite(n, HIGH);
        delay(random(1,500));
        digitalWrite(m, HIGH);
        delay(random(1,500));
        digitalWrite(p, HIGH);
        delay(random(1,500));
        
        digitalWrite(n, LOW);
        delay(random(1,500));
        digitalWrite(m, LOW);
        delay(random(1,500));
        digitalWrite(p, LOW);
        delay(random(1,500));

        if (buttonState2 == HIGH) {
          Serial.println("BUTTON");
          break;
          }
        }
      digitalWrite(ledPin2, LOW);
      digitalWrite(ledPin, LOW);
    }
    delay(100);
}

You can actually tell from the video below that the lights do turn on, but the buttons do not carry out any functionalities… Not good!

FINAL CODE:

I wanted to make my code as simple and straightforward as possible. Here is what it looks like.

int ledPin1 = 2;
int ledPin2 = 4;
int ledPin3 = 6;
int buttonPin2 = 5;


void setup() {
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(buttonPin2, INPUT);
}

void loop() {
  led1();
  led2();
  led3();
  button();
}

void led1() {
  if (digitalRead (ledPin1) == LOW) {
    delay(random(1, 300));
    digitalWrite (ledPin1, random(0, 2));
  }
  else {
    delay(random(1, 300));
    digitalWrite (ledPin1, random(0, 2));
  }
}

void led2() {
  if (digitalRead (ledPin2) == LOW) {
    delay(random(1, 300));
    digitalWrite (ledPin2, random(0, 2));
  }
  else {
    delay(random(1, 300));
    digitalWrite (ledPin2, random(0, 2));
  }
}

void led3() {
  if (digitalRead (ledPin3) == LOW) {
    delay(random(1, 300));
    digitalWrite (ledPin3, random(0, 2));
  }
  else {
    delay(random(1, 300));
    digitalWrite (ledPin3, random(0, 2));
  }
}

void button() {
  
  int buttonState2 = digitalRead(buttonPin2);
  
  if (buttonState2 == HIGH && digitalRead(ledPin1) == HIGH && digitalRead(ledPin2) == HIGH && digitalRead(ledPin3) == HIGH) {
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, HIGH);
    digitalWrite(ledPin3, HIGH);
    delay(2000);
    
    int count = 0;
    while (count < 7){
      digitalWrite(ledPin1, HIGH);
      digitalWrite(ledPin2, HIGH);
      digitalWrite(ledPin3, HIGH);
      delay(20);                  
      digitalWrite(ledPin1, LOW);
      digitalWrite(ledPin2, LOW);
      digitalWrite(ledPin3, LOW);
      delay(1000); 
      count += 1;
      }
  }
}

 

David – Midterm

zip file : Midterm_IM

This surely was the project in which I tried my best to incorporate everything we have learned so far. Initially, I wanted to make a game that resembles an already existing game called, “Flappy Bird”, but I diverted my plan to render something different and original to a certain extent. The game’s goal is to fly out of the mysterious cave by avoiding blocks of flying lava. Prior to the instructions of the game, which is fairly simple, I gave a short dystopian background narrative that the player somehow ended up in the cave because things fell apart ever since 2020.

I made the instructions fairly easy. The player will be floating along the cave and the only way to maneuver is to use UP, DOWN, RIGHT (to speed up) keys to avoid the flying lava bombs. If you successfully get out of the cave, you will have to go back to the society, and that requires MONEY. Other people have also attempted to get out of the cave, only to be buried there. So you will pick up the coins that the previous adventurers have left behind and follow their legacy… If you fail, you will become one of them.

I used a few images of caves and rocks to design the game as follow.

Cave1Cave2Rock1 Rock2

To talk more about the functionalities of the game, a player’s health decreases everytime he hits a block of lava. It will keep decreasing until your lives (initally out of 3) have fully depleted. You will then have to restart from the beginning. The placement of coins and lava bombs will be randomly assigned for every level. A very short song from PacMan will be played once you start the game and during the game a player will be able to hear sound effects when he hits a coin or a lava. You will see that there is a flashlight effect on the instructions page that was employed to create a more eerie and dystopian atmosphere.

Here is what it looks like:

The code is as follows:

import ddf.minim.*;

PFont font, font2;

Player player;
ArrayList<Coin> coins;
ArrayList<Bomb> bombs;
boolean [] keys = new boolean[128];
boolean instruction = true;
boolean gameover = true;
PImage photo, cave, rock, rock2;

color player_c = color(100, 200, 200);

int myPlayer = 3;
int myCoins = 0;
float myHealth = 100;

//sound effect
Minim minim;
AudioPlayer sound, sound2, sound3;

void setup(){
  size(800, 400);
  background(0);
  smooth();
  rectMode(CENTER);
  font = createFont("Trattatello",60);
  font2 = createFont("Arial",16);
  
  player = new Player();

  coins = new ArrayList<Coin>();
  for(int i=0; i < 10; i++){
    coins.add(new Coin());
  }

  bombs = new ArrayList<Bomb>();
  for(int i=0; i < 15; i++){
    bombs.add(new Bomb());
  }
  minim = new Minim(this);
  sound = minim.loadFile("1.aif");
  sound2 = minim.loadFile("2.mp3");
  sound3 = minim.loadFile("3.mp3");
  sound.setGain(-5);
  sound2.setGain(-5);
  sound3.setGain(-3);
  sound3.play();
  sound3.rewind();
  
}

void draw(){
  if (instruction){
    photo = loadImage("cave.jpg");
    //Using the width and height of the photo for the screen size
    photo.resize(width,height); 
    image(photo,0,0);
    loadPixels();
    photo.loadPixels();
    for(int x= 0; x<width; x++){
      for(int y=0; y<height; y++){
        //location in the pixel array
        int loc = x+y*width;
        float r = red(photo.pixels[loc]);
        float g = green(photo.pixels[loc]);
        float b = blue(photo.pixels[loc]);
        //get distance between the cursor and the pixel
        float d = dist(mouseX,mouseY,x,y);
        float mult = map(d,0,300,1.8,0);
        //the closer the distance, the brighter.
        pixels[loc] = color(r*mult,g*mult,b*mult);
      }
    }
    updatePixels();
    textFont(font);
    fill(#FFFF00);
    text("Cave Escape",width/2-200,height/2-70);
    textFont(font2);
    fill(255);
    text("It's year 2020... Things went south...\nIn fact, so south that you got stuck in a mysterious cave...\nSurvive the cave of HELL with full of GOLD COINS.\nMake your way out through flying lava bombs.\n\nTo maneuver, press → ↑ ↓\nPress ANY KEY to start",width/2-200,height/2-20);
    return;
  }
  
  background(0);
  cave = loadImage("cave2.jpg");
  //Using the width and height of the photo for the screen size
  cave.resize(width,235); 
  image(cave,0,67);
  caveStone();
  
  for (int i = coins.size() -1; i >= 0; i--){
    Coin c = coins.get(i);
    c.display();
    
    if(player.checkCollisionCoins(c)){
      sound2.rewind();
      sound2.play();
      
      myCoins += 10;

      coins.remove(i);
    }
  }
  
  for (int i = bombs.size() -1; i >= 0; i--){
    Bomb b = bombs.get(i);
    b.display();
   
    if(player.checkCollisionBombs(b)){
      sound.rewind();
      sound.play();
      player_c = color(255, 0, 0);
      myHealth -= 1.3;
      if(myHealth <= 0){
        myPlayer -= 1;
        myHealth = 100;
      }
    } 
  }

  player.display();
  player.move();
 
  //score
  textSize(15);
  fill(255);
  text("Player life = " + myPlayer, 60, 30, 255); // show life
  textAlign(LEFT);
  text("Coins = " + myCoins, 10, 55, 255); // show coins gathered 
  textAlign(LEFT);
  text("Health = " + myHealth, 110, 55, 255); 
  
  if(myPlayer == 0){
    setup();
    myCoins = 0;
    myHealth = 100;
    myPlayer = 3;
  } else {
    player_c = color(100, 200, 200);
  }

}


//Player
class Player{
  PVector pos;
  PVector acc;
  PVector vel;
  PVector speed;
  
  Player(){
    pos = new PVector(-5, height/2);
    acc = new PVector(0.8, 0.8);
    vel = new PVector(0,50);
    speed = new PVector(0.9,0);
  }
  
  void display() {
    fill(player_c);
    rect(pos.x, pos.y,30,30, 7);
  }
  
  void move(){
    vel.mult(5);
    vel.limit(10);
    vel = vel.add(acc);
    pos.add(speed);
    
    if(pos.x < -50){
      pos.x = width; 
    }
    if(pos.x > width){
      pos.x = -50;  
      bombs = new ArrayList<Bomb>();
      coins = new ArrayList<Coin>();
      for(int i=0; i < 10; i++){
        coins.add(new Coin());
      }
      for(int i=0; i < 15; i++){
        bombs.add(new Bomb());
      }
    }
    if(pos.y > height - 120 ){
      pos.y = height - 120; 
    }
    if(pos.y < 120){
      pos.y = 120; 
    }
    
    if(keys[RIGHT])
      pos.x += vel.x;
    if(keys[UP])
      pos.y -= vel.y;
    if(keys[DOWN])
      pos.y += vel.y;
  }
  
  boolean checkCollisionBombs(Bomb b){
     if(dist(player.pos.x, player.pos.y, b.pos.x, b.pos.y) <= b.b_size){
        return true; 
      } else {
        return false;
      }
  }

  boolean checkCollisionCoins(Coin c){
    if(dist(player.pos.x, player.pos.y, c.pos.x, c.pos.y) < c.radius){
        return true;
      } else {
        return false;
      }
  }
}

class Coin{
  
  PVector pos;
  PVector acc;
  PVector vel;
  color c;
  float radius;
  
  Coin(){
    pos = new PVector(random(50, width), random(110, height-110));
    acc = new PVector(0,0);
    vel = new PVector(0,0);
    radius = 20;
    c = color(255, 204, 0);
  }
  
  void display(){
    pushMatrix();
    noStroke();
    translate(pos.x, pos.y);
    fill(80);
    ellipse(2,2, radius, radius);
    fill(c);
    ellipse(0,0,radius, radius);
    fill(0);
    textSize(15);
    textAlign(CENTER);
    text("$",0,4);
    popMatrix();
  }
}


class Bomb {
  PVector pos;
  PVector acc;
  PVector vel;
  float b_size = 25;
  color c;
  float direction = random(2,8);
  
  Bomb (){
    pos = new PVector(random(100, width), random(110, height- 110));
    acc = new PVector(0,0);
    vel = new PVector(0,0);
    c = color(255, 0, 0);
  }
  
  void display(){
    pushMatrix();
    translate(pos.x, pos.y);
    fill(c);
    rect(0, 0, b_size, b_size);
    pos.y += direction;
    if (pos.y < 110 || pos.y > height-110){
      direction *= -1;
    }
    popMatrix();
  }
  
}

void keyPressed(){
  if (instruction) {
    instruction = false;
  }
  else if (gameover){
    gameover = false;
  }
  else {
    keys[keyCode] = true;
  }
}

void keyReleased(){
  keys[keyCode] = false;
}



void caveStone(){
  rock = loadImage("base.jpg");
  rock2 = loadImage("base2.jpg");
  rock.resize(30,30);
  rock2.resize(30,30);
  
  for(int i = 0; i <= width; i += 30) {
    image(rock,i,67);
    image(rock,i,302);
  }
  for(int j = 0; j <= width; j += 90) {
    image(rock2,j,67);
    image(rock2,j,302);
  }
}

 

Mona Lisa manipulation – David

Mona Lisa is one of my favorite paintings. So I decided to use it for this week’s image manipulation assignment. The product of my image manipulation was not entirely my initial intention, because I arrived at the current outcome upon various trials and failures. First, I manipulated the painting in a way that the image is transformed to a black&white image and disintegrated into small ellipses when the cursor is moved. Following the X and Y axis, the gap and the number of ellipses change, creating a sort of 3D-animated effect (?) from my perspective. Another aspect I decided to add was a certain change when the mouse is pressed. When the mouse is pressed, the cursor turns into a spotlight. I feel that this adds to the somewhat eerie and mysterious atmosphere of my creation. You can find my attempts and addition of different aspect in the videos below.

 

Here is the code:

//image manipulation assignment

PImage photo;

void setup(){
  size(800,650);
  photo = loadImage("/Users/sangjinlee/Desktop/monalisa.jpg");
  //Using the width and height of the photo for the screen size
  photo.resize(width,height); 
}

void draw(){
  if (mousePressed){
    loadPixels();
    photo.loadPixels();
    for(int x= 0; x<width; x++){
      for(int y=0; y<height; y++){
        //location in the pixel array
        int loc = x+y*width;
        float r = red(photo.pixels[loc]);
        float g = green(photo.pixels[loc]);
        float b = blue(photo.pixels[loc]);
        //get distance between the cursor and the pixel
        float d = dist(mouseX,mouseY,x,y);
        float mult = map(d,0,120,1.5,0);
        //the closer the distance, the brighter.
        pixels[loc] = color(r*mult,g*mult,b*mult);
      }
    }
    updatePixels();
  } else{
    background(255);
    fill(0);
    noStroke();
    
    //Max val for mouseX is 800. So reduce it.
    float ellipseNumber = mouseX / 5;
    //Distance between the ellipses
    float ellipseGap = width / ellipseNumber;
    
    translate(ellipseGap/2, ellipseGap/2);
    
    for (int i=0; i<ellipseNumber; i++){
      for (int j=0; j<ellipseNumber; j++){
        //get pixel color corresponding to the changing x and y-axis
        color c = photo.get(int(i*ellipseGap), int(j*ellipseGap));
        //Extract size corresponding to the brightness (0~255) of c we got.
        //Because size is too big, we use map to set the darkest 0 to size 10
        //and to set the brightest 255 to size 0.5.
        float size = map(brightness(c), 0, 255, 10, 0.5);
        
        ellipse(i*ellipseGap, j*ellipseGap, size, size);
      }
    }
  }
}

 

Assignment #4: Generative Text

For this week’s assignment, I attempted to make an interactive art utilizing text. My art piece for this week is pretty straightforward. A text is displayed initially and the user can maneuver their cursor onto the test. Then, once the cursor is within the range of the text, the text starts to move away like it is trying to avoid you. I also created a sort of shivering effect to really make it seem like the text is hating the cursor.

The text keeps moving as it tries to avoid the cursor, and if the text moves away from the width and the height of the screen, then the text is called back into the screen.

I am eager to improve this piece and I am working on creating numerous more identical texts within the screen so that it creates a more mass-movement kind of effect when a cursor is placed anywhere on the screen.

Below is the video of my creation in action:

String message = "Go Away~"; // to calculate the string's length
float x,y,hr,vr;

void setup(){
  size(320,320);
  textFont(createFont("Monaco",36));
  textAlign(CENTER, CENTER);
  hr = textWidth(message);
  vr = (textAscent() + textDescent())/2;
  noStroke();
  x = random(hr, width - hr);
  y = random(vr, height - vr);
}

void draw() {
  fill(255, 120);
  rect(0, 0, width, height);

  // Once the mouse is on the text
  if (abs(mouseX-x) < hr && abs(mouseY - y) < vr){
    x += random(-10,10);
    y += random(-10,10);
    if (x+hr/2 > width || x-hr/2 < 0 || y+vr > height || y-vr < 0){
      x = random(hr, width-hr);
      y = random(vr, height-vr);
    }
  }
  fill(0);
  text("Go Away~",x,y);
}