# Theory and Practice of Tangible User Interfaces

### Announcements

November 24, 2007
Reading for November 27th, are now posted. Enjoy!

October 2, 2007
To upload your thoughtless acts, create a new assignment page like any other lab. You'll see "Thoughtless Acts" listed as one of the assignment options.

May 24, 2008
This site has been archived and is no longer editable. Stay tuned for the next version, coming in the fall!

# Twista

Project Members:
Alana
daniela
Isaac Salier-He...
Ken-ichi Ueda

### Description

Twista is a version of the game Twister except played with the fingers and using musical notes to signal where players should place their fingers. Ideally, the computer will play a note, and the first player must press a colored key that corresponds to that note (3 colors, 3 notes). Once they've successfully done so, they hold their finger on that key, and the computer plays another note for the second player. The process continues until all keys are pressed or a player lifts a finger and loses.

### Processing Code

import ddf.minim.*;

boolean DEBUG = true;

AudioSample c3; // Audio for the note C

AudioSample e3; // Audio for the note E

AudioSample g3; // Audio for the note G

AudioSample cheer; // Audio for cheering

AudioSample woops; // Audio for shaming

AudioSample studioCheer; // Audio for ending well

AudioSample doh; // Audio for ending badly

AudioSample sample; // temp holder for samples

boolean waiting = false; // whether the system is waiting for player response

char tone; // current tone

int rand; // random number holder

int keyInt; // integer version of key pressed

boolean correct = false; // whether the most recent key pressed is correct

// stores whether a number has already been chosen or not

boolean[] chosenNumbers = {false, false, false, false,

false, false, false, false, false, false};

// index key numbers to notes

char[] keyTones = {'x','c','c','c','e','e','e','g','g','g'};

// GUI vars

int winH = 512;

int winW = 512;

int butHeight = winH / 5;

int butWidth = winW / 2;

int butX = (winW/2) - (butWidth/2);

int butY = (winH/2) - (butHeight/2) + 70;

int boxSize = 80;

PFont trebuchet;

boolean startWait = true;

// solves for the key being held sending multiple keyPressed events (or, it should)

boolean onePressed = false;

boolean twoPressed = false;

boolean threePressed = false;

boolean fourPressed = false;

boolean fivePressed = false;

boolean sixPressed = false;

boolean sevenPressed = false;

boolean eightPressed = false;

boolean ninePressed = false;

void setup()

{

size(winW, winH);

// always start Minim before you do anything with it

Minim.start(this);

Minim.debugOn();

// load BD.wav from the data folder, with a 512 sample buffer

// load SD.wav from the data folder

// Load font for title screen and start button.

}

void draw()

{

// GUI

// Draw title screen and start button.

if(startWait) {

background(0,0,0);

fill(255);

textFont(trebuchet);

text("Twista", 180, 140);

textFont(trebuchet, 16);

text("Alana Pechon, Daniela Rosner," , 144, 200);

text("Isaac Salier-Hellendag, Ken-ichi Ueda", 120, 218);

stroke(255);

fill(159,39,39);

rect(butX, butY, butWidth, butHeight);

textFont(trebuchet, 32);

fill(255);

text("Click to start!", 156, 339);

if(mouseX < (butX + butWidth) && mouseX > butX && mouseY < (butY + butHeight) && mouseY > butY) {

cursor(HAND);

} else {

cursor(ARROW);

}

}

// Draw the boxes and waveforms.

if(!startWait) {

cursor(ARROW);

background(0);

// Draw rects to represent the board.

stroke(80);

// Yellow boxes

for(int j = 7 ; j < 10 ; j++) {

if(!chosenNumbers[j])

// fill(203, 100, 138);

fill(0, 255, 0);

else

/* fill(66, 42, 51);*/

fill(0, 105, 0);

rect((winW * (j - 6) / 4) - (boxSize / 2), (winH * 1 / 4) - (boxSize / 2), boxSize, boxSize);

}

// Red boxes

for(int j = 4 ; j < 7 ; j++) {

if(!chosenNumbers[j])

/* fill(76, 89, 212);*/

fill(255, 0, 0);

else

// fill(29, 32, 59);

fill(105, 0, 0);

rect((winW * (j - 3) / 4) - (boxSize / 2), (winH * 2 / 4) - (boxSize / 2), boxSize, boxSize);

}

// Green boxes

for(int j = 1 ; j < 4 ; j++) {

if(!chosenNumbers[j])

// fill(66, 217, 102);

fill(255, 255, 0);

else

// fill(21, 43, 26);

fill(105, 105, 0);

rect((winW * j / 4) - (boxSize / 2), (winH * 3 / 4) - (boxSize / 2), boxSize, boxSize);

}

stroke(180);

// use the mix buffer to draw the waveforms.

// because these are MONO files, we could have used the left or right buffers and got the same data

float[] csamp = c3.mix.toArray();

float[] esamp = e3.mix.toArray();

float[] gsamp = g3.mix.toArray();

for (int i = 0; i < c3.bufferSize() - 1; i++)

{

line(i, (winH * 1 / 4) - gsamp[i]*50, i+1, (winH * 1 / 4) - gsamp[i+1]*50); // draw waveforms

line(i, (winH * 2 / 4) - esamp[i]*50, i+1, (winH * 2 / 4) - esamp[i+1]*50);

line(i, (winH * 3 / 4) - csamp[i]*50, i+1, (winH * 3 / 4) - csamp[i+1]*50);

}

playGame();

}

}

void mouseClicked() {

// If the user clicks the start button, start the game!

if(mouseX < (butX + butWidth) && mouseX > butX && mouseY < (butY + butHeight) && mouseY > butY) {

background(0);

startWait = false;

}

}

void playGame() {

/*

PSEUDOCODE

if waitState -- note, this went in the keyHandler method below

if key corresponds to tone

celebrate

set playState

if playState

if all numbers chosen

end game

else

choose a random number between 1 and 9

play tone corresponding to that number (1-3, 4-6, 7-9)

set wait state

*/

if (!waiting) {

if (DEBUG) println("Playing...");

if (chosenNumbers[1] && // if all #s are chosen, end the game

chosenNumbers[2] &&

chosenNumbers[3] &&

chosenNumbers[4] &&

chosenNumbers[5] &&

chosenNumbers[6] &&

chosenNumbers[7] &&

chosenNumbers[8] &&

chosenNumbers[9]) {

if (DEBUG) println("All numbers chosen, ending the game");

studioCheer.trigger();

stop();

}

else {

// choose a random number

rand = (int) random(1,9);

if (DEBUG) print("Random Index: "); println(rand);

// if rand hasn't already been chosen...

if (chosenNumbers[rand] == false) {

if (DEBUG) { print("keyTones[rand]: "); println(keyTones[rand]);}

sample = getSample(keyTones[rand]); // get the audio matching that number

delay(3000); // delay while the audio for the players key press plays

sample.trigger(); // play the new tone

tone = keyTones[rand];

waiting = true; // wait for input

}

}

}

}

// just maps a note like 'g' to its corresponding AudioSample

AudioSample getSample(char note) {

if (note == 'c') {

return c3;

}

else if (note == 'e') {

return e3;

}

else {

return g3;

}

}

// print the state of the board to the console

void printKeys() {

for (int i = 1; i <= 9; i++) {

if (chosenNumbers[i]) print(i + " ");

else print(". ");

if (i % 3 == 0) println();

}

}

void keyPressed()

{

// play selected tone on initial key press event

if ( key == '1' && !onePressed) {

c3.trigger();

onePressed = true;

keyHandler();

}

else if ( key == '2' && !twoPressed) {

c3.trigger();

twoPressed = true;

keyHandler();

}

else if ( key == '3' && !threePressed) {

c3.trigger();

threePressed = true;

keyHandler();

}

else if ( key == '4' && !fourPressed) {

e3.trigger();

fourPressed = true;

keyHandler();

}

else if ( key == '5' && !fivePressed) {

e3.trigger();

fivePressed = true;

keyHandler();

}

else if ( key == '6' && !sixPressed) {

e3.trigger();

sixPressed = true;

keyHandler();

}

else if ( key == '7' && !sevenPressed) {

g3.trigger();

sevenPressed = true;

keyHandler();

}

else if ( key == '8' && !eightPressed) {

g3.trigger();

eightPressed = true;

keyHandler();

}

else if ( key == '9' && !ninePressed) {

g3.trigger();

ninePressed = true;

keyHandler();

}

}

// Decide whether the right key was pressed or not

void keyHandler() {

keyInt = (int) key - (int) '0'; // stupid java...

if (DEBUG) {

print("keyInt: ");

println(keyInt);

}

// if the user hit a key matching the selected tone...

if (keyTones[keyInt] == tone) {

cheer.trigger(); // celebrate

chosenNumbers[keyInt] = true; // cross this number off the list

correct = true; // variable for allowing them to lift finger or not

printKeys();

}

// if the user hit a key that does not match the selected tone...

else {

woops.trigger(); // shame

correct = false; // variable for allowing them to lift finger or not

printKeys(); // play another tone

}

}

void keyReleased()

{

// end the game if a player releases a key they should be holding down

if ( correct ) {

doh.trigger();

stop();

}

}

void stop()

{

// always close Minim audio classes when you are done with them

delay(7000);

c3.close();

e3.close();

g3.close();

cheer.close();

studioCheer.close();

woops.close();

doh.close();

super.stop();

}

Pictures

Twista Screen

Twista board