Punch-It! – Daniel Woc – Rodolfo Cossovich

Context and Significance

After watching  Michael Reeve’s Laser Baby and analyzing Nuntinee Tansrisakul’s Tiny Movies, my concept of interaction has grown from one of creativity and learning to one of entertainment and unconventionality. To me, interaction now entails the reciprocal action between two parties and the influence they have over each other. This action and influence must result from a conversation that does not necessarily have to be two-sided and can stray from a purely practical purpose. Furthermore, this interaction can stray from the expected and can even break the box of conventionality in a given topic. In my Midterm Project, I showed an interaction that aimed to incorporate art and the user and its environment through the exploration of a conversation based on creativity. For my Final Project, I wanted to incorporate entertainment methods and the user and its experience through the exploration of a conversation based on fun and unconventionality. 

Conception and Design

My partner, Almon, and I wanted to create a project that would take a concept we both like and deliver a unique and fun experience that would bring entertainment to the user interacting with it. Additionally, we wanted to create an experience that breaks the box of conventionality and expectations in entertainment. We cycled through a variety of ideas, such as a Pokemon game, a Visual DJ, and a punching gauntlet. We also cycled through various methods the user would use to interact with our game, such as throwing things, hitting buttons, or shaking things. We eventually settled on creating a game that would integrate the elements of a fighting game with those of a training game. The linear process of production, relative simplicity in logic, and entertainment it would bring to the user drew us to this idea as the base for our final project.

As we began to ideate the physical aspects of our project, we figured that using a mannequin and displaying the visuals above said mannequin would best integrate our game’s physical and digital aspects in order to enhance the user experience. Additionally, we considered using vibration sensors to detect when something is being hit to be the most intuitive and simple approach to accomplish what we wanted. Similarly, we settled on using LEDs to communicate to the user where they needed to hit for simplicity and a clear indication of action.

Now that we had a clear idea of what we wanted to accomplish with our project in regard to the physical aspects, we needed to determine how we wanted our code to function and what we wanted it to do. Figuring out the base logic was complicated as I had never developed a game before. However, after figuring out the base code, elements such as images and sound were easy to add. We also wanted to incorporate elements such as a health bar, different contact points on our mannequin and add more game elements, such as a counter, to better represent the aspects of the game we wished to create and bring more entertainment to the user. 

Here’s a sketch of our project!

Fabrication and Production

For our User Testing session, my partner and I presented the bare bones of our project. We created a base mechanism that would display a random square on the screen of my computer that corresponded to a given sensor that one needed to punch. While it was functional, it was minimal, and we had a lot of room for improvement. During our User Testing session, we were suggested to add a lot of elements that made it into the final iteration of our project. For example, the combination of our project’s digital and physical aspects was suggested here. Additionally, the incorporation of LEDs and the usage of ways to keep track of specific statistics for the motivation of the user were pitched to us in this session.

IMG

When creating our Final Project, it was simply a matter of adding more complex code to our Processing sketch, expanding our Arduino code, and attaching more vibration sensors to our mannequin. While we were initially thinking about switching the material of our hit pads to wood planks that we would laser cut into different sizes, we kept the cardboard as its flexibility, malleability, and relative softness would benefit us more. We laser-cut a cable management box that would allow us to better organize our wiring and circuitry, the extent of which was relatively low as it was still quite a mess.

Here’s a quick list of materials we used for the project!

5 Cardboard Pads

5 220 Ω Resistors

5 Piezo Vibration Sensors

5 LEDs

1 Arduino

Wires

Hot Glue

Double-sided Tape

Rubber bands

My partner focused more on the physical aspects of our project and helped me with the wiring while I focused on the coding parts of our project. Here’s the Processing code for our project!

//libraries
import processing.serial.*;
import processing.sound.*;
import osteele.processing.SerialRecord.*;

//Arduino stuff
Serial serialPort;
SerialRecord serialRecord;
SoundFile sound1;

//sound files
SoundFile music;
SoundFile countdown;
SoundFile battle;
SoundFile end;
SoundFile punch;
SoundFile finish;
SoundFile strike;
SoundFile hype;

//shit ton of booleans
boolean startScreen = true;
boolean endScreen = false;
boolean gameStart = false;
boolean gameState = true;
boolean playing = true;
boolean finishScreen = false;
boolean endSequence = false;
boolean finishState = true;
boolean hypeState = true;
boolean strikeState = true;
boolean endState = true;
boolean countDown = true;
boolean starting = true;

//shit ton of int variables
int randomVal = -1;
int start = -1;
int start2 = -1;
int count = 0;
int crazyCount = 0;
int strong = 0;
int thresh = 50;
int health = 1250;

//time keeping variables
int m = -1;
int s = -1;
int ms = -1;
int mn = 0;
int sn = 0;
int msn = 0;


//fonts
PFont game;
PFont font;

//images
PImage back;
PImage ded;

void setup(){
  fullScreen();
  
  //font
  game = createFont("AwmuSlant.otf", 250);
  font = createFont("font2.ttf", 250);
  
  //image
  back = loadImage("street.jpeg");
  ded = loadImage("ded.png");
  
  //color
  colorMode(HSB, 360, 100, 100);
  
  //sound files
  music = new SoundFile(this, "music2.aiff");
  countdown = new SoundFile(this, "smash.mp3");
  battle = new SoundFile(this, "battle.aiff");
  end = new SoundFile(this, "ending.aiff");
  punch = new SoundFile(this, "punch.mp3");
  finish = new SoundFile(this, "finish2.mp3");
  strike = new SoundFile(this, "strike.mp3");
  hype = new SoundFile(this, "ded.aiff");
  
  //begin music
  music.loop();
  
  //initial background
  drawBack();
  
  //initial text
  fill(360); 
  textFont(game);
  textSize(250);
  textAlign(CENTER);
  text("Punch It!", width/2, height/2);
  
  //Arduino
  String serialPortName = SerialUtils.findArduinoPort();
  serialPort = new Serial(this, serialPortName, 9600);
  serialRecord = new SerialRecord(this, serialPort, 6);
}

void draw(){
  serialRecord.read();
  int value0 = serialRecord.values[0];
  int value1 = serialRecord.values[1];
  int value2 = serialRecord.values[2];
  int value3 = serialRecord.values[3];
  int value4 = serialRecord.values[4];
  
  if(startScreen){
    if(frameCount % 50 > 0 && frameCount % 50 < 20) {
      fill(360);
    }
    else{
      fill(0);
    }
    
    serialRecord.send();
    
    textSize(50);
    text("Press 'S' to begin!", width/2, (height/2) + 300);
  }
  
  /*
  if(starting && (value0 > thresh) || (value1 > thresh) || (value2 > thresh) || (value3 > thresh) || (value4 > thresh)){
    startScreen = false;
    starting = false;
    start = millis();
    drawBack();
    music.stop();
    
    if(countDown){
      countdown.play();
      countDown = false;
    }
   
  }
  */
  
  //Countdown
  if(start > 0 && millis() - start < 1000){
    fill(360); 
    textFont(font);
    textSize(250);
    textAlign(CENTER);
    text("3", width/2, height/2);
  }  
  
  if(start > 0 && millis() - start > 1000 && millis() - start < 2000){
    drawBack();
    fill(360); 
    textFont(font);
    textSize(250);
    textAlign(CENTER);
    text("2", width/2, height/2);
  }
  
  if(start > 0 && millis() - start > 2000 && millis() - start < 3000){
    drawBack();
    fill(360); 
    textFont(font);
    textSize(250);
    textAlign(CENTER);
    text("1", width/2, height/2);
  }
  
  if(start > 0 && millis() - start > 3000 && millis() - start < 4000){
    drawBack();
    fill(360); 
    textFont(font);
    textSize(250);
    textAlign(CENTER);
    text("GO!", width/2, height/2);
  }
  
  //Starts Game
  if(start > 0 && millis() - start > 4000){
    m = minute();
    s = second();
    ms = millis();

    //Plays music if it isnt playing
    if(playing){
      battle.loop();
      playing = false;
    }
        
    //Game thingy
    if(gameState){
      drawBack();
      randomVal = floor(random(1, 6));
      serialRecord.values[5] = randomVal;
      serialRecord.send();
      drawHealthBar();
      gameState = false;
    }
  
    if((value0 > thresh) && (randomVal == 1)){
      count++;
      
      if(value0 >= strong){
        strong = value0;
      }
      
      if(health - (value0 / 10)<= 0){
        endSequence = true;
        gameState = false;
      }
      else{
        health -= (value0 / 10);
      }
      
      punch.play();
      gameState = true;
      delay(1);
    }
    
    if((value1 > thresh) && (randomVal == 2)){
      count++;
      
      if(value1 >= strong){
        strong = value1;
      }
      
      if(health - (value1 / 10) <= 0){
        endSequence = true;
        gameState = false;
      }
      else{
        health -= (value1 / 10);
      }
      
      punch.play();
      gameState = true;
      delay(1);
    }
    
    if((value2 > thresh) && (randomVal == 3)){
      count++;
      
      if(value2 >= strong){
        strong = value2;
      }
      
      if(health - (value2 / 10) <= 0){
        endSequence = true;
        gameState = false;
      }
      else{
        health -= (value2 / 10);
      }
      
      punch.play();
      gameState = true;
      delay(1);
    }
    
    if((value3 > thresh) && (randomVal == 4)){
      count++;
      
      if(value3 >= strong){
        strong = value3;
      }
      
      if(health - (value3 / 10) <= 0){
        endSequence = true;
        gameState = false;
      }
      else{
        health -= (value3 / 10);
      }
      
      punch.play();
      gameState = true;
      delay(1);
    }
    
    if((value4 > thresh) && (randomVal == 5)){
      count++;
      
      if(value4 >= strong){
        strong = value4;
      }
      
      if(health - (value4 / 10) <= 0){
        endSequence = true;
        gameState = false;
      }
      else{
        health -= (value4 / 10);
      }
      
      punch.play();
      gameState = true;
      delay(1);
    }
  }
  
  if(endSequence){
      finishScreen = true;
      battle.stop();
      
      if(finishState){
        finish.play();
        finishState = false;
      }
  }
  
  if(finishScreen){
    drawBack();
    
    if(frameCount % 50 > 0 && frameCount % 50 < 20) {
      fill(360);
    }
    else{
      fill(0);
    }
    
    if(hypeState){
      hype.play();
      start2 = millis();
      hypeState = false;
    }
    
    textFont(game);
    textSize(250);
    textAlign(CENTER);
    text("Finish Him!", width/2, height/2);
    
    serialRecord.values[5] = 6;
    serialRecord.send();
    
    if(start2 > 0 && millis() - start2 < 10000){
      if((value0 > 20) || (value1 > 20) || (value2 > 20) || (value3 > 20) || (value4 > 20)){
        crazyCount++;
      }
    }
    
    if(millis() - start2 > 10000 && millis() - start2 < 12000){
      if(strikeState){
        hype.stop();
        strike.play();
        strikeState = false;
      }
      drawDed();
    }
    
    if(start2 > 0 && millis() - start2 > 12000){
      endScreen = true;
      drawBack();
      hype.stop();
      
      if(endState){
        mn = minute() - m;
        sn = second() - s;
        msn = (millis() - ms) % 1000;
        
        end.loop();
        endState = false;
      }
      
    }
    
  }
  
  if(endScreen){
    drawBack();
    fill(360);
    textAlign(CENTER);
    textSize(100);
    text("Enemy Defeated!", width/2, (height/2) - 200);
    textFont(font);
    textSize(50);
    
    if(frameCount % 50 > 0 && frameCount % 50 < 20) {
      fill(360);
    }
    else{
      fill(0);
    }
    
    serialRecord.send();
    
    int strong2 = floor(map(strong, 0, 1023, 0, 100));
    
    text("Frenzy Mode Punches: " + crazyCount, width/2, (height/2) - 100);
    text("Strongest Punch: " + strong2 + " strong", width/2, (height/2));
    text("Punches thrown: " + count, width/2, (height/2) + 100);
    text("Time Taken: " + mn + ":" + sn + ":" + msn, width/2, (height/2) + 200);
  }
}

Here’s the Arduino code for our project!

#include "SerialRecord.h"

SerialRecord writer(5);
SerialRecord reader(6);

int val0;
int val1;
int val2;
int val3;
int val4;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  reader.read();

  val0 = analogRead(A0);
  val1 = analogRead(A1);
  val2 = analogRead(A2);
  val3 = analogRead(A3);
  val4 = analogRead(A4);

  writer[0] = val0;
  writer[1] = val1;
  writer[2] = val2;
  writer[3] = val3;
  writer[4] = val4;

  writer.send();

  if(reader[5] == 1){
    digitalWrite(13, HIGH);
    }
  else{
    digitalWrite(13, LOW);
  }

  if(reader[5] == 2){
    digitalWrite(12, HIGH);
    }
  else{
   digitalWrite(12, LOW);
   }

  if(reader[5] == 3){
    digitalWrite(11, HIGH);
    }
  else{
    digitalWrite(11, LOW);
  }

  if(reader[5] == 4){
    digitalWrite(10, HIGH);
    }
  else{
    digitalWrite(10, LOW);
  }

  if(reader[5] == 5){
    digitalWrite(9, HIGH);
    }
  else{
    digitalWrite(9, LOW);
  }

  if(reader[5] == 6){
    digitalWrite(13, HIGH);
    digitalWrite(12, HIGH);
    digitalWrite(11, HIGH);
    digitalWrite(10, HIGH);
    digitalWrite(9, HIGH);
  }

  delay(20);
} 

Here’s a diagram of our circuit! Tinkercad doesn’t have vibration sensors, so pretend the temperature sensors are vibration sensors. 

Conclusion

With my Final Project, I wanted to create something fun, entertaining, and a project that breaks the box of conventionality. While it would have been interesting to go down a creative and exploratory path with my Final Project, I wanted to explore a different area of interaction and focus on the methods in which users can interact with a digital environment through their physical environment in order to achieve a form of entertainment. I would say that the manner in which my audience interacted with my project was one I expected and showed that I accomplished the intention with my project successfully. Furthermore, I would say that this interaction effectively demonstrated my definition of interaction as it embodied the idea that interaction can consist of a conversation that does not necessarily have to be practical and can break the boundaries of practicality and conventionality.

(We were having audio issues in this test)

If I had more time to work on my project and develop it further, I would have liked to add more vibration sensors and refine some elements of my code, such as the timer. Additionally, I would have liked to create a better cable management system for our project to look more refined, prevent any issues like the ones demonstrated in the video, and fine-tune details in the physical aspects of our project. Adding brighter LEDs and adjusting the manner in which we attached our cardboard paddles are some aspects that come to mind. Despite all this, however, I am proud of the project my partner and I made. I will be sure to take in all the lessons we learned along the way and the knowledge I have acquired for my future projects.

Here’s a screen recording of the game!

Annex

Leave a Reply

Your email address will not be published. Required fields are marked *