- A to P
- A
- input
- distance: 3 ultrasonic distance sensors
- output
- LED brightness: red, yellow, blues LEDs
- communication
- input
- P
- input
- communication
- output
- videos
- texts
- input
Interactive Media Arts @ NYUSH
Project Title: Won Color el Stage
Descriptions of the project can be found in my last article. No big deviation from the initial configuration. https://wp.nyu.edu/shanghai-ima-documentation/foundations/interaction-lab/cx592/fnl-prjct-essay-by-changzhen-from-inmis-session/
1. The Strategy
Three players must cooperate as importantly as compete to win the game. After all, mixed color indicates it’s at least two colors.
If only one player inputs too much, the score would go to one of her opponent. E.g. If it’s solely red who inputs, mixed color will be judged purple instead of orange that’s counted to be her score.
If they three input too much, the score decreases for all. The larger input for each, the worse decrease.
2. Digital Design
The video parts will be displayed on 1920*1080 (16:9) TV screen. The video image is made of the MikuMikuDance video in the center, three primary colors on the left with each party’s score, mixed color on the right, and the match on the top.
3. Physical Design
The circuit is hidden in a triangular prism. There are holes laser cut on each side face to fix an ultrasonic distance sensor and an LED indicating her input color. The sensor senses the distance on each side 0~30cm, and the brightness of the LED changes in response. The prism is made of shiny reflexible materials, because it’s designed to be a stage. Around each angle of its top is pasted a small paper with the anime idol printed on.
To my surprise, the ultrasonic distance sensor requires a library and is connected to digital pin on arduino. This sketch is symmetrical for each player.
4. Objections and Suggestions to This Project
Marcella firstly suggests I get rid of the video and let the event just happen on the physical stage. The idol who holds stage will be identified by color stage lights. Secondly, she puts forward the reason that if three players watch the screen in the same direction, the triangular prism design loses its meaning. Thirdly, the input method may be better if it’s to sense the movements of the player dancing. As for the first, I regret not to have taken this advise, because I had limited time to make adjustment. As for the second, I think VR glasses instead of a TV on one side will also make players play face to face forming a triangle. Her third idea is awesome.
Christina Bowllan and her friend objected to the video content. By negligence, I didn’t take feminists like them into account. If I had known that who knew little about anime, comics, and games will not accept the artistic appearance of such anime female characters dancing, I would change the content like I didn’t adopt the idea of mixing color to draw flags, which is political. I might choose landscape videos or so.
Professors suggested I reveal each party’s score so that players know it’s a match and knows where they’re more clearly. I adjusted that on the IMA festival day. And I dismantle the paper cover tent above the stage by their suggestion to make the stage easier on the eyes.
5. Conclusions
Marcella and other professors’ suggestions remind me that each component of the project shall have a focused purpose to account for what the project is about and how it’s played.
The feminists’ objections remind me that I shall consider when and where a project is allowed. It’s intended for all in the context of this inter lab class, so I shall make it acceptable to all.
The core of this project is its strategy. It suggests the real life situations. Victory doesn’t come to a single person; it comes to groups that corporate and compete. So the strategy can be applied to more than this idol dancing thing to create other colorful intellectual projects.
Arduino Code
//include distance sensor and hook up to digital pin 2, 4, 7
#include “Ultrasonic.h”
Ultrasonic s1(2);
Ultrasonic s2(4);
Ultrasonic s3(7);
void setup() {
Serial.begin(9600);
}
void loop() {
//read the distance in cm
int d1 = s1.MeasureInCentimeters();
int d2 = s2.MeasureInCentimeters();
int d3 = s3.MeasureInCentimeters();
//restrict the range within 30cm
if(d1 > 30) {
d1 = 30;
}
if(d2 > 30) {
d2 = 30;
}
if(d3 > 30) {
d3 = 30;
}
//control the brightness of color LED according to the distance
analogWrite(9,255-d1*255/30);
analogWrite(10,255-d2*255/30);
analogWrite(11,255-d3*255/30);
//serial communicate to processing
Serial.print(d1);
Serial.print(“,”);
Serial.print(d2);
Serial.print(“,”);
Serial.print(d3);
Serial.println();
}
Processing Code
import processing.serial.*;
import processing.video.*;
import processing.sound.*;
//create instance; movie is the video of the dancing idol of that color; BGM is background music
Movie orange;
Movie green;
Movie purple;
SoundFile BGM;
String myString = null;
Serial myPort;
//3, because there are three datas for each idol in one serial line
int NUM_OF_VALUES = 3;
int[] sensorValues;
//mixed color
color c;
//r, y, b are the incremental score counted by millis(); O, G, P are the accumulative scores of each player
long red = 0;
long yel = 0;
long blu = 0;
long Ora = 0;
long Gre = 0;
long Pur = 0;
// play BGM only once
boolean play = true;
void setup() {
//TV screen size
size(1920, 1080);
noStroke();
background(0);
setupSerial();
//get the files
orange = new Movie(this, “orange.mp4”);
green = new Movie(this, “green.mp4”);
purple = new Movie(this, “purple.mp4”);
BGM = new SoundFile(this, “BGM.mp3”);
textSize(128);
fill(255);
text(“mix”, 1655, 450);
}
void draw() {
updateSerial();
printArray(sensorValues);
//m1, 2, 3 are multipliers, the input amount, derived from distance
float m1 = 1-float(sensorValues[0])/30;
float m2 = 1-float(sensorValues[1])/30;
float m3 = 1-float(sensorValues[2])/30;
//play BGM only once
if (play == true) {
BGM.play();
play = false;
}
//display each’s color and the mixed on screen as rectangles
fill(255, 255-255*m1, 255-245*m1);
rect(0, 0, 288, 360);
fill(255, 255-10*m2, 255-255*m2);
rect(0, 360, 288, 360);
fill(255-255*m3, 255-105*m3, 255);
rect(0, 720, 288, 360);
c = color(255-255*m3, 255-(255*m1+10*m2+105*m3)*255/370, 255-(245*m1+255*m2)*255/500);
fill(c);
rect(1632, 540, 288, 360);
//judge the mixed color and who earns score at the moment; whose accumulative score is highest, whose idol’s video shall be played
if (m1+m2+m3 < 2.4) {
if (m1+m2 > 3*m3 && m2 > 0.1) {
red = millis()-Ora-Gre-Pur;
Ora += red;
}
if (m2+m3 > 3*m1 && m3 > 0.1) {
yel = millis()-Ora-Gre-Pur;
Gre += yel;
}
if (m3+m1 > 3*m2 && m1 > 0.1) {
blu = millis()-Ora-Gre-Pur;
Pur += blu;
}
}
if (m1+m2+m3 > 2.4) {
Ora -= 100*(m1)/(m1+m2+m3);
Gre -= 100*(m2)/(m1+m2+m3);
Pur -= 100*(m3)/(m1+m2+m3);
orange.stop();
green.stop();
purple.stop();
}
println(Ora);
println(Gre);
println(Pur);
textSize(128);
if (Ora > Gre && Ora > Pur) {
if (orange.available()) {
orange.read();
}
fill(0);
rect(288, 0, 1632, 324);
fill(255);
text(“Orange Girl on Stage”, 350, 200);
orange.play();
green.stop();
purple.stop();
image(orange, 288, 324, 1344, 756);
}
if (Gre > Ora && Gre > Pur) {
if (green.available()) {
green.read();
}
fill(0);
rect(288, 0, 1632, 324);
fill(255);
text(“Green Girl on Stage”, 350, 200);
green.play();
orange.stop();
purple.stop();
image(green, 288, 324, 1344, 756);
}
if (Pur > Gre && Pur > Ora) {
if (purple.available()) {
purple.read();
}
fill(0);
rect(288, 0, 1632, 324);
fill(255);
text(“Purple Girl on Stage”, 350, 200);
purple.play();
orange.stop();
green.stop();
image(purple, 288, 324, 1344, 756);
}
//end of game and show who wins
if(millis() >= BGM.duration()*1000) {
background(0);
fill(255);
if (Ora > Gre && Ora > Pur) {
text(“Orange Girl Wins Stage!”, 240, 500);
}
if (Gre > Ora && Gre > Pur) {
text(“Green Girl Wins Stage!”, 240, 500);
}
if (Pur > Ora && Pur > Gre) {
text(“Purple Girl Wins Stage!”, 240, 500);
}
}
fill(0);
textSize(32);
text(int(Ora), 20, 180);
text(int(Gre), 20, 540);
text(in(Pur), 20, 900);
}
void setupSerial() {
printArray(Serial.list());
myPort = new Serial(this, Serial.list()[3], 9600);
myPort.clear();
myString = myPort.readStringUntil(10);
myString = null;
sensorValues = new int[NUM_OF_VALUES];
}
void updateSerial() {
while (myPort.available() > 0) {
myString = myPort.readStringUntil(10);
if (myString != null) {
String[] serialInArray = split(trim(myString), “,”);
if (serialInArray.length == NUM_OF_VALUES) {
for (int i=0; i<serialInArray.length; i++) {
sensorValues[i] = int(serialInArray[i]);
}
}
}
}
}
Won Color el Stage
1. Project Description
It’s both a video game and a three-player strategic game. Each player acts as a supporting fan respectively of one of the three anime girl idols. Each player inputs his representative primary color and tries to summon his own idol.
2. Project Details
Player 1 inputs red trying to summon the tangerine girl.
Player 2 inputs yellow trying to summon the emerald girl.
Player 3 inputs blue trying to summon the violet girl.
How does a player inputs his color? A distance sensor from Arduino measures the distance of his hand from it. The closer, the higher the input.
How are the girls summoned? Taking those primary color inputs and computing through some lousy maths, three players get a mixed color. Then the new color is judged onto hue, to turn out to be closest to the color tangerine, emerald, or violet, and the corresponding girl switches the girl previously on stage, if there’s been, to appear on the stage to sing and dance. In practice, the animation is the girl’s video played on Processing.
Why these specific colors, and why not RGB? One idea is that I was taught by art teachers from primary school the primary colors were red, yellow, and blue. Now, I’ve acknowledged that RGB are the correct three primary colors, and CMY aren’t equal to blue, red, and yellow. Even so, I try to recover the good old memories of mixing colors in art classes. Another idea is that RGB is just to saturated, and their invert colors, CMY feels like sex and hormone, therefore, there’re not proper to represent the girl idols. By contrast, my RYB and their mixed colors are more moderate:
R+Y = orange (tangerine)
Y+B = green (emerald)
B+R = purple (violet)
How to judge victory of the game? Over the time of an idol pop song being played on the stage on Processing, the total on-stage reign of each idol is counted. Here’s a penalizing mechanism if three players are inputing so harsh that the mixed color is too dark, all girl idols black out for a moment since “there’s too much dark energy”, and each player’s time account gets reduced by the color’s darkness times the proportion of his contribution to the color. Who has the longest time wins.
How strategic can this game be? Highly. Because to earn a high score, the players have to not only explore the theoretic rules for color mixing, but also balance with the other two players to output the color of his idol, which is never easy, plus the penalizing mechanism restriction.
3. Context & Significance
It’s a supreme gift for ACG (anime, comic, and game) lovers. They can watch the cute anime girls perform as they compete, and how they input the color is intended to be like intimately touching the girl since the sensor will be decorated with the girl’s portrait. Plus, the strategic game is highly interactive.
Inspirations from midterm suggests that a multi-player game satisfies a high-ordered interaction. And players are also interacting with the virtual characters. The color idea was triggered by Click Canvas on creative applications.
4. Explanation for the Project Title
“Won” means “to win” in English and “primary” in Korean. “El” means “from” as an affix and is short for “Elsword” an ACG action RPG made by KOG Korea where the three girl idols are from. It basically means winning a game from selecting primary colors to control stage performances.
Preview of project:
Player draws a picture by controlling potential meters. Arduino analogReads the two potentials and serially communicates them to Processing to be used as X and Y coordinates. Processing loads image, JuHunKim.jpg, gets color with random coordinate variations from and around (X, Y) of the image, and draws circle faces of random size with the color it just got respectively onto the canvas my project newly sets at the corresponding coordinates. Moreover, player can only draw the left half of the canvas since the right half will be mirrored from the image.
Arduino:
void setup() {
Serial.begin(9600);
pinMode(A0,INPUT);
pinMode(A1,INPUT);
}
void loop() {
int sensor1 = analogRead(A0);
int sensor2 = analogRead(A1);
Serial.print(sensor1);
Serial.print(“,”);
Serial.println(sensor2);
}
Processing:
import processing.serial.*;
String myString = null;
Serial myPort;
int[] sensorVal = new int[2];
int x;
int y;
PImage img;
void setup() {
printArray(Serial.list());
size(870, 674);
noStroke();
img = loadImage(“JuHunKim.jpg”);
setupSerial();
}
void draw() {
updateSerial();
printArray(sensorVal);
x = sensorVal[0]*435/1023;
y = sensorVal[1]*674/1023;
for(int i=0; i<50; i++) {
int var = int(random(-50, -1));
int dev = int(random(-50, 50));
color c = img.get(x+var,y+dev);
fill(c);
int diam = int(random(1,10));
circle(x+var, y+dev, diam);
circle(870-x-var, y+dev, diam);
}
}
void mousePressed() {
saveFrame(“JuHunna.png”);
}
void updateSerial() {
while (myPort.available() > 0) {
myString = myPort.readStringUntil( 10 );
if (myString != null) {
String[] serialInArray = split(trim(myString), “,”);
if (serialInArray.length == 2) {
for (int i=0; i<serialInArray.length; i++) {
sensorVal[i] = int(serialInArray[i]);
}
}
}
}
}
void setupSerial() {
printArray(Serial.list());
myPort = new Serial(this, Serial.list()[ 3 ], 9600);
myPort.clear();
myString = myPort.readStringUntil( 10 );
myString = null;
}
Electrical Diagram:
Final Effect:
Player may mousePress to save the drawn image as JuHunna.png, a higher ordered art piece derived from 김주훈’s model photo.
What’s directly sent is always string. Copy and paste the example code, fill numbers of pin, port, and such in it, and add the task we want. Never try to understand what each line means.
Ex 1
Input from Arduino and output to Processing. Draw ellipse according to Arduino potential meter readings.
Arduino:
void setup() {
Serial.begin(9600);
}
void loop() {
int sensor1 = analogRead(A0);
int sensor2 = analogRead(A1);
Serial.print(sensor1);
Serial.print(“,”);
Serial.println(sensor2);
}
Processing:
import processing.serial.*;
String myString = null;
Serial myPort; // utilize the library onto myPort
int NUM_OF_VALUES = 2;
int[] sensorValues;
void setup() {
size(512, 512);
background(255);
setupSerial();
}
void draw() {
updateSerial();
printArray(sensorValues);
background(255);
ellipse(width/2,height/2,sensorValues[0]/2,sensorValues[1]/2);
}
void setupSerial() {
printArray(Serial.list());
myPort = new Serial(this, Serial.list()[3], 9600); // myPort is specified to be linked to port 3 USB, but what does “this” mean?
/*myPort.clear(); // mean?
myString = myPort.readStringUntil( 10 ); // 10 = ‘\n’ in ASCII meaning new line
myString = null;*/
sensorValues = new int[NUM_OF_VALUES]; // sensorValues[] contains two num
}
void updateSerial() {
while (myPort.available() > 0) { // mean by “available”?
myString = myPort.readStringUntil(10);
if (myString != null) { // why possibly null?
String[] serialInArray = split(trim(myString), “,”); // split string “trim(myString)” to element in array serialInArray[] when it meets “,”; trim(myString)removes whitespace characters from the beginning and end of a string
if (serialInArray.length == NUM_OF_VALUES) { // length is num of elements
for (int i=0; i<serialInArray.length; i++) {
sensorValues[i] = int(serialInArray[i]); // store value from string as int
}
}
}
}
}
Ex 2
Input from Arduino and output to Processing. Etch A Sketch.
Arduino: the same.
Processing:
import processing.serial.*;
String myString = null;
Serial myPort;
int NUM_OF_VALUES = 2;
int[] sensorValues;
void setup() {
size(512, 512);
background(255);
setupSerial();
updateSerial();
}
void draw() {
int pX = sensorValues[0]; // put before updateSerial to store previous
int pY = sensorValues[1];
updateSerial();
printArray(sensorValues);
line(pX/2,pY/2,sensorValues[0]/2,sensorValues[1]/2);
if(mousePressed) {background(255);}
}
void setupSerial() {
printArray(Serial.list());
myPort = new Serial(this, Serial.list()[3], 9600);
myPort.clear();
myString = myPort.readStringUntil( 10 ); // 10 = ‘\n’ Linefeed in ASCII
myString = null;
sensorValues = new int[NUM_OF_VALUES];
}
void updateSerial() {
while (myPort.available() > 0) {
myString = myPort.readStringUntil(10); // ASCII 10 meaning new line
if (myString != null) {
String[] serialInArray = split(trim(myString), “,”);
if (serialInArray.length == NUM_OF_VALUES) {
for (int i=0; i<serialInArray.length; i++) {
sensorValues[i] = int(serialInArray[i]);
}
}
}
}
}
Ex 3
Input from Processing and output to Arduino. Arduino makes sound whose pitch depends on the coordinates in Processing. Only green texts are changed from the example code.
Arduino:
#define NUM_OF_VALUES 2 /** YOU MUST CHANGE THIS ACCORDING TO YOUR PROJECT **/
int tempValue = 0;
int valueIndex = 0;
/* This is the array of values storing the data from Processing. */
int values[NUM_OF_VALUES];
void setup() {
Serial.begin(9600);
pinMode(A2, OUTPUT);
}
void loop() {
getSerialData();
int pitch = values[0]+values[1];
tone(A2,pitch);
}
//recieve serial data from Processing
void getSerialData() {
if (Serial.available()) {
char c = Serial.read();
//switch – case checks the value of the variable in the switch function
//in this case, the char c, then runs one of the cases that fit the value of the variable
//for more information, visit the reference page: https://www.arduino.cc/en/Reference/SwitchCase
switch (c) {
//if the char c from Processing is a number between 0 and 9
case ‘0’…’9′:
//save the value of char c to tempValue
//but simultaneously rearrange the existing values saved in tempValue
//for the digits received through char c to remain coherent
//if this does not make sense and would like to know more, send an email to me!
tempValue = tempValue * 10 + c – ‘0’;
break;
//if the char c from Processing is a comma
//indicating that the following values of char c is for the next element in the values array
case ‘,’:
values[valueIndex] = tempValue;
//reset tempValue value
tempValue = 0;
//increment valuesIndex by 1
valueIndex++;
break;
//if the char c from Processing is character ‘n’
//which signals that it is the end of data
case ‘n’:
//save the tempValue
//this will b the last element in the values array
values[valueIndex] = tempValue;
//reset tempValue and valueIndex values
//to clear out the values array for the next round of readings from Processing
tempValue = 0;
valueIndex = 0;
break;
//if the char c from Processing is character ‘e’
//it is signalling for the Arduino to send Processing the elements saved in the values array
//this case is triggered and processed by the echoSerialData function in the Processing sketch
case ‘e’: // to echo
for (int i = 0; i < NUM_OF_VALUES; i++) {
Serial.print(values[i]);
if (i < NUM_OF_VALUES – 1) {
Serial.print(‘,’);
}
else {
Serial.println();
}
}
break;
}
}
}
Processing:
import processing.serial.*;
int NUM_OF_VALUES = 2;
Serial myPort;
String myString;
// This is the array of values you might want to send to Arduino.
int values[] = new int[NUM_OF_VALUES];
void setup() {
size(500, 500);
background(0);
printArray(Serial.list());
myPort = new Serial(this, Serial.list()[3], 9600);
myPort.clear(); // throw out the first reading, in case start reading in the middle of a string from the sender
myString = myPort.readStringUntil(10); // 10 = ‘\n’ Linefeed in ASCII
myString = null;
}
void draw() {
noStroke();
fill(0, 30);
rect(0, 0, 500, 500);
strokeWeight(5);
stroke(255);
line(pmouseX, pmouseY, mouseX, mouseY);
values[0] = mouseX;
values[1] = mouseY;
sendSerialData();
echoSerialData(200);
}
void sendSerialData() {
String data = “”;
for (int i=0; i<values.length; i++) {
data += values[i];
//if i is less than the index number of the last element in the values array
if (i < values.length-1) {
data += “,”; // add splitter character “,” between each values element
}
//if it is the last element in the values array
else {
data += “n”; // add the end of data character “n”
}
}
//write to Arduino
myPort.write(data);
}
void echoSerialData(int frequency) {
//write character ‘e’ at the given frequency
//to request Arduino to send back the values array
if (frameCount % frequency == 0) myPort.write(‘e’);
String incomingBytes = “”;
while (myPort.available() > 0) {
//add on all the characters received from the Arduino to the incomingBytes string
incomingBytes += char(myPort.read());
}
//print what Arduino sent back to Processing
print(incomingBytes);
}