Inklings
Ideenfindung
Während der Ideenfindung haben wir uns für 3 verschiedenen Varianten entschieden, wobei wir die Dritte als Konzept umgesetzt haben und weiter verfolgen werden:1. Spielerisches Lernen:
Ein Spielbrett welcher mit dem Computer verbunden ist dient als Spielbrett. Auf dem Computer wird ein Lerninhalt abgefragt. Der Benutzer hat für jeden Lernmodus ein Set von Spielkarten, welche als Antworten dienen. Durch das Auf-den-Tisch-Legen der Karten, wird ein Visuelles Feedback vom Computer gegeben ob die Antwort richtig oder falsch ist.2. Gehör-Ballspiel
Zwei Pingpongschläger dienen als Spielgerät. Speziell hier ist, das es keinen Ball gibt. Die Ballposition kann lediglich von dem aktuellen Ton der aus einer Box kommt ermitteln, welche mit den Schläger verbunden ist. Je fester der Ball geschlagen wird desto höher fliegt er und desto intensiver wird die Tonkurve.Konzept
Inklings ist eine kollaborative generative Installation. Auf dem Bildschirm wird eine Tintenlandschaft angezeigt, die durch vier verschiedene Controller beeinflusst werden kann:- Stab: Durch Schwingen beeinflusst der Stab die Bewegungsrichtung der Tintenwolken. (Beschleunigungssensor)
- Box: Die Box enthält kleine Kügelchen und kann geschüttelt werden. Sie erzeugt damit einen Zufallswert, der die Menge der Wolken festlegt. (Piezo-Sensor)
- Zylinder: Der Zylinder besteht aus zwei Zylindern, die um eine Mittelachse in zwei Richtungen gedreht werden kann. Er beeinflusst die Drehung der Tinte. (Potentiometer)
- Ball: Der Ball kann gequetscht werden und erzeugt rote Tintenwolken. (Flex-Sensor)
Ziel
Wir wollen fremde Menschen zusammenbringen, indem sie auf einfache Weise kollaborieren und eine gemeinsame visuelle Sprache finden.Komponenten
Inklings besteht aus einer Konsole (Plastikbox), die den Elektrocontroller (ein Arduino UNO) und zwei Breadboards enthält. Vier Anschlüsse führen zu den physischen Controllern (s.o.), die alle mit einem 5-poligen 2m Kabel mit der Konsole verbunden sind. Die Konsole ist wiederum mit dem Ausgabegerät (z.B. ein iMac) verbunden. Auf dem Ausgabegerät läuft ein Processing-Script, mit dem die Benutzer von Inklings über die Controller interagieren können.Bau
Wir haben die einzelnen Komponenten in der Werkstatt gebaut oder angepasst. Dauer: ca. 1 1/2 Tage- Stab: Wir haben ein Plastikrohr zugeschnitten, und einen passenden Plexiglas-Deckel dazu gelasert. Dann haben wir den Beschleunigungssensor im oberen Ende des Rohrs befestigt.
- Box: Die Box hat nur noch ein Loch für das Kabel gebraucht, danach haben wir den Piezo-Sensor eingesetzt.
- Zylinder: Den Zylinder haben wir aus zwei Holzquadern hergestellt. An der Drehbank haben wir sie in Zylinderform gebracht und anschliessend mit dem Bohrer die Löcher für das Kabel, das Potentiometer und eine Plexiglasabdeckung (mit dem Lasercutter geschnitten) gebohrt. Am Ende haben wir den Potentiometer mit Heissleim und Komponentenkleber an den beiden Zylindern befestigt.
- Ball: Den Ball wir nur ein kleines bisschen aufschneiden um den Flex-Sensor einzufügen.
Lackieren
Wir haben die Teile an zwei Tagen in ca. 6 Schritten schwarz lackiert (den Ball rot) und einen "Inklings" Schriftzug mit einer Kartonschablone aufgesprayt.Abschluss
Zum Schluss haben wir ein USB Kabel geteilt, um es durch das Loch in der Box zu bringen.Coding
Um unsere Tintenwolken zu kreieren, sind wir von einer Partikelklasse aus dem Buch "Generative Gestaltung" ausgegangen, die wir erweitert und unseren Bedürfnissen angepasst haben. So nimmt jeder der Controller Einfluss auf die Partikel und deren Muster. Die Partikel haben wir mit einer eine Lebenszeit versehen, die von den Nutzern mitbestimmt werden kann. Am meisten Mühe bereitetes es uns, den Stab so einzusetzen, dass wir die (bei 180° wieder absteigenden) Werte sinnvoll einsetzen konnten. Im Verlaufe des Programmierens haben wir einen zweiten Modus erarbeitet, der durch schnelles Herunterziehen des Stabs aktiviert wird. Es folgen der Arduino- und der Screen-Code:#include <Wire.h> //Include the Wire library #include <MMA_7455.h> //Include the MMA_7455 library #define SHAKE A0 #define TWIST A1 #define SQUEEZE A2 MMA_7455 mySensor = MMA_7455(); //Make an instance of MMA_7455 char xVal, yVal, zVal; //Variables for the values from the sensor void setup() { Serial.begin(9600); // Set the sensitivity you want to use // 2 = 2g, 4 = 4g, 8 = 8g mySensor.initSensitivity(2); // Calibrate the Offset, that values corespond in // flat position to: xVal = -30, yVal = -20, zVal = +20 // !!!Activate this after having the first values read out!!! //mySensor.calibrateOffset(5, 20, -68); } void loop() { int ShakeVal = analogRead(SHAKE); int TwistVal = analogRead(TWIST); int SqueezeVal = analogRead(SQUEEZE); xVal = mySensor.readAxis('x'); //Read out the 'x' Axis yVal = mySensor.readAxis('y'); //Read out the 'y' Axis zVal = mySensor.readAxis('z'); //Read out the 'z' Axis Serial.print(ShakeVal); Serial.print(","); Serial.print(TwistVal); Serial.print(","); Serial.print(SqueezeVal); Serial.print(","); Serial.print(xVal, DEC); Serial.print(","); Serial.print(yVal, DEC); Serial.print(","); Serial.print(zVal, DEC); Serial.println(","); }
import processing.opengl.*; import processing.serial.*; int sizeX = 1680; int sizeY = 1050; int maxAgents = 60000; int mode = 0; // Mode 0: Normal clouds, Mode 1: Hairlines // ------ agents ------ ArrayList agents = new ArrayList(); int agentsCount = 0; float noiseScale = 50, noiseStrength = 10, noiseZRange = 0.4; float overlayAlpha = 10, agentsAlpha = 10, strokeWidth = 0.3; color strokeColor = color(255, 255, 255); Serial myPort; float shakeVal, twistVal, squeezeVal, swingValX, swingValY, swingValZ, swingValXOld, swingValYOld, swingValZOld; int shakeValMapped; float oldValue = 0.995; float newValue = 0.005; int coords[] = new int[6]; float oldSquezeVal = 0; long previousTime; long currentTime; int breakTime = 500; boolean squeezePressed = false; boolean swingActive = false; boolean logo = true; PImage img; void setup () { size(sizeX, sizeY, OPENGL); background(0); smooth(); //for(int i=0; i<agents.size(); i++) agents.add(new Agent()); //println(Serial.list()); myPort = new Serial(this, Serial.list()[0], 9600); //cam = new Capture(this, width, height); img = loadImage("logo.png"); } void draw () { noCursor(); if(logo == true) { background(img); } int newAgents = 0; fill(0, overlayAlpha); noStroke(); rect(0,0,width,height); //agentsCount = shakeVal*15; //println(squeezeVal); //println(oldSquezeVal); //println(shakeVal); // println(agents.size()); //strokeWidth = map(abs(swingValZ), 0, 128, 0.01, 0.5); if(mode == 0) noiseStrength = map(abs(swingValX), 0, 75, 10, 15); else if(mode == 1) noiseStrength = map(twistVal, 0, 1023, 10, 15); //println(noiseStrength); noiseScale = map(twistVal, 0, 1023, 20, 150); currentTime = millis(); int squeezeTrigger = 15; if(squeezeVal > 0 && (squeezeVal < 80 || squeezeVal > 110)) { strokeColor = color(255, 0, 0); squeezePressed = true; if(squeezeVal < 80) newAgents += map(squeezeVal, 0, 100, maxAgents / 200, 0); else if(squeezeVal > 110) newAgents += map(squeezeVal, 200, 100, maxAgents / 200, 0); } else { strokeColor = color(255, 255, 255); squeezePressed = false; } // println(oldSquezeVal + " " + squeezeVal + " " + strokeColor + " " + squeezePressed); if ( currentTime - previousTime > breakTime ) { previousTime = currentTime; } //rotate(radians(swingValX)); newAgents += int(map(shakeVal, 0, 1023, 0, maxAgents / 10)); //newAgents = 100; //println(agents.size()); if(agents.size() < maxAgents) { for(int i = 0; i < newAgents; i++) { agents.add(new Agent(strokeColor, agentsAlpha, agents.size())); } } for(int i=0; i<agents.size(); i++) { Agent currentAgent = (Agent) agents.get(i); if(currentAgent.isAlive()) { currentAgent.update(); }else { agents.remove(i); } } //println(agentsCount); //println(shakeVal); // print("\t"); // print(twistVal); // print("\t"); // print(squeezeVal); // print("\t"); // print(swingValX); // print("\t"); // print(swingValY); // print("\t"); // print(swingValZ); // print("\t"); // print(swingValXOld); // print("\t"); // print(swingValYOld); // print("\t"); // print(swingValZOld); // println(); if(abs(swingValZ) > 110 && swingActive == false) { if(mode == 0) mode = 1; else mode = 0; } else if(swingActive == true && swingValZ < 110) { swingActive = false; } } void keyPressed() { if(key == 's') { /*if (cam.available ()) { cam.read(); cam.save("cam.png"); }*/ save("screen.png"); } else if (key == 'm') { if(mode == 0) mode = 1; else mode = 0; } else if (key == 'b') { if(logo == false) logo = true; else logo = false; } } void serialEvent(Serial myPort) // Is called everytime there is new data to read { if (myPort.available() > 0) { String completeString = myPort.readStringUntil(10); // Read the Serial port until there is a linefeed/carriage return if (completeString != null) // If there is valid data insode the String { trim(completeString); // Remove whitespace characters at the beginning and end of the string String separateValues[] = split(completeString, ","); // Split the string everytime a delimiter is received //println("----" + separateValues.length + "----"); for(int j = 0; j < separateValues.length; j++) { if(j < 6) coords[j] = int(separateValues[j]); //println(j); } if(separateValues.length == 7) { shakeVal = coords[0]; twistVal = coords[1]; squeezeVal = coords[2]; swingValX = coords[3]; swingValY = coords[4]; swingValZ = coords[5]; // if(swingValXOld != 0) swingValX = swingValX * newValue + swingValXOld * oldValue; // if(swingValYOld != 0) swingValY = swingValY * newValue + swingValYOld * oldValue; // // if(swingValZOld != 0) swingValZ = swingValZ * newValue + swingValZOld * oldValue; // swingValXOld = swingValX; // swingValYOld = swingValY; // // swingValZOld = swingValZ; } } } } boolean sketchFullScreen() { return true; }
class Agent { PVector p, pOld; float noiseZ, noiseZVelocity = 0.01; float stepSize, angle; long birthTime; long lifeTime = 2000; color strokeColor; float agentAlpha; Agent (color strokeColorInput, float agentAlphaInput, int agentsSize) { p = new PVector(random(width),random(height)); pOld = new PVector(p.x,p.y); stepSize = random(1,10); // init noiseZ setNoiseZRange(noiseZRange); birthTime = millis(); strokeColor = strokeColorInput; agentAlpha = agentAlphaInput; lifeTime = round(map(agentsSize, 0, maxAgents, 1000, 10000)); // println(lifeTime); } void update () { if(mode == 0) { //angle = noise(p.x/noiseScale + map(abs(swingValX), 0, 75, 2, 6), p.y/noiseScale + map(abs(swingValY), 0, 75, 2, 6), noiseZ) * noiseStrength; //angle = noise(p.x/noiseScale, p.y/noiseScale, noiseZ + map(abs(swingValZ), 0, 128, 2, 12)) * noiseStrength; //stepSize = map(abs(swingValZ), 0, 128, 1, 10); angle = noise(p.x/noiseScale, p.y/noiseScale, noiseZ) * noiseStrength; p.x += cos(angle) * stepSize; p.y += sin(angle) * stepSize; } else if(mode == 1) { float angleX = (p.x/noiseScale + swingValX/noiseScale); float angleY = (p.y/noiseScale + swingValY/noiseScale); p.x += cos(angleX) * stepSize; p.y += sin(angleY) * stepSize; } // offscreen wrap if (p.x<-10) p.x=pOld.x=width+10; if (p.x>width+10) p.x=pOld.x=-10; if (p.y<-10) p.y=pOld.y=height+10; if (p.y>height+10) p.y=pOld.y=-10; stroke(strokeColor, agentAlpha); strokeWeight(strokeWidth*stepSize); line(pOld.x,pOld.y, p.x,p.y); pOld.set(p); noiseZ += noiseZVelocity; } boolean isAlive() { if(millis() - birthTime < lifeTime) { return true; } return false; } void setNoiseZRange ( float theNoiseZRange ) { // small values will increase grouping of the agents noiseZ = random(theNoiseZRange); } }
Nils misst die maximalgrösse eines Arrays