Control a “you” and release your “touch” to play some notes.
For this week’s assignment, I used Object Oriented Programming to create a simple representation of a piano.
I was inspired by a similar concept I saw on Pinterest and I wanted to include the picture for inspiration credits but I couldn’t find it again!
Nevertheless, to do this, I started off by making 3 classes in three different tabs. One for the piano, one for the person moving around (or “you”), and lastly one for the “touch” released by the person.
General Information
The Piano class is quite simple. It has a constructor and two functions.
The first function is just to draw the piano on the screen. While the second one is used when a piano key is pressed to make it grey and give it the illusion to have actually been pressed.
Here is the code for this:
class Piano{ float pianoTop, pianoSide, keyWidth; //constructor Piano(){ pianoTop = height/1.7; pianoSide = width/7; keyWidth = (width-(2*pianoSide))/13; //13 is the number of keys } //displaying the piano void drawPiano(){ for (int i=0; i<13; i++){ if (i==1 || i==3 || i==6 || i==8 || i==10){ fill(0); stroke(0); strokeWeight(2); rect(pianoSide+i*keyWidth, pianoTop, keyWidth, (height-pianoTop)/1.4); } else{ fill(255); stroke(0); strokeWeight(2); rect(pianoSide+ i*keyWidth, pianoTop, keyWidth, height-pianoTop); } } } //function that makes a pressed key grey void pianoKeyPressed(int a){ if (a==1 || a==3 || a==6 || a==8 || a==10){ stroke(0); strokeWeight(2); fill(100); rect(pianoSide+a*keyWidth, pianoTop, keyWidth, (height-pianoTop)/1.4); } else{ stroke(0); strokeWeight(2); fill(100); rect(pianoSide+ a*keyWidth, pianoTop, keyWidth, height-pianoTop); } } }
Next, there is the You class. Here as well, there is a constructor along with three functions. The first one just displays an image/draws the “you”. The second one is to move it with the Left and Right arrow keys. And the last one is to return the x position of the You at a specific moment. This last function is needed for the touch. Since the touch is represented by a small circle that appears at the position of the person.
Here is the code for this class:
class You{ float x, y, speed; int youWidth; float r, g, b; PImage img; //constructor You(){ speed= 5; x= width/2; y= height/9; youWidth = 100; img = loadImage("square.png"); } void displayYou(){ image(img, x-youWidth/2, y, youWidth, youWidth); //rect(x-youWidth/2,y,youWidth,youWidth); } void controlYou(){ if (keyPressed && keyCode == RIGHT){ if(x < width - (piano.pianoSide)){ x += speed; } } else if (keyPressed && keyCode == LEFT){ if (x > piano.pianoSide){ x -= speed; } } } float getYouPos(){ return x; } }
The final class is the Touch class. It has a constructor and two functions. The first function is used to display the touch at the position of the person when it is between the bottom of the person and the top border of the piano. And it also makes it disappear into the piano when it reaches the top border along with a small visual effect representing a touch.
(Note: each “touch” gets a random color.)
The second function is used to update the y-position of the touch.
This is the code for this class:
class Touch{ float y, speed; int touchWidth; float r, g, b; boolean touchArrived; //constructor Touch(){ speed = 5; y = height/9 + you.youWidth + touchWidth/2; touchWidth = 15; r = random(255); g = random(255); b = random(255); touchArrived = false; } void displayTouch(float pos){ if (y <= piano.pianoTop){ fill(r,g,b); //fill the touch with a random color noStroke(); ellipse(pos,y,touchWidth,touchWidth); } //visual effect when the touch reaches the pressed key if (touchArrived){ stroke(0); strokeWeight(3); line(pos, piano.pianoTop - 5, pos, piano.pianoTop-17); line(pos - 10, piano.pianoTop - 5, pos-20, piano.pianoTop-15); line(pos + 10, piano.pianoTop - 5, pos+20, piano.pianoTop-15); } } void moveTouch(){ y += speed; touchArrived = y>= piano.pianoTop && y<(height-piano.pianoTop/2); } }
In the main tab, instances of all of these classes were created. One for the piano, one for “you” and an ArrayList for all the “touches” that will be created throughout the program.
In draw() there is an if statement that will either display the Start Screen or the Piano Screen (the latter will be triggered when the mouse is clicked. This is done by a mouseClicked() function and a boolean variable.)
A keyPressed() function is also found in this main tab and it is responsible for creating a new Touch object every time the spacebar is pressed.
Finally, there is a function that checks no sound files overlap.
Here is the code for that:
//declaring global variables import processing.sound.*; SoundFile[] notes; //an array contaning all the sound files boolean start; Piano piano; You you; int touchCounter; ArrayList<Touch> touches; //an array for all the touches created FloatList touchPositions; //an array for the x-position of each touch float pos, temp1, temp2; void setup(){ size(1000,700); start=false; notes = new SoundFile[13]; for(int i=0; i<13; i++){ notes[i]= new SoundFile(this,"note"+str(i)+".mp3"); //adding all sound files to an array } piano= new Piano(); you= new You(); touches = new ArrayList<Touch>(); touchPositions = new FloatList(); } void draw(){ background(255,243,249); //start screen if (!start){ fill(0); textSize(20); textAlign(CENTER); text("Use the left and right arrow keys to move.",width/2 ,height/3); text("Press space to release your \"touch\" :)",width/2 ,height/2.7); text("Click to start playing!", width/2, height/1.8); textAlign(LEFT); textSize(15); text("(the piano.)", width/2, height/1.7); } //when the mouse is clicked, the "game"(?) starts else{ //draw piano piano.drawPiano(); //drawing "you" and controlling it you.displayYou(); you.controlYou(); //drawing the touch and controlling it when the spacebar is pressed for (int i=0; i<touchCounter; i++){ touches.get(i).displayTouch(touchPositions.get(i)); touches.get(i).moveTouch(); //adding the visual and sound effects when a note is played for (int j=0; j<13; j++){ temp1 = piano.pianoSide+j*piano.keyWidth; //temporary value to find the start border of the pressed key temp2 = temp1 + piano.keyWidth; //temporary value to find the end border of the pressed key if (touchPositions.get(i)>temp1 && touchPositions.get(i)<temp2 && touches.get(i).touchArrived){ piano.pianoKeyPressed(j); checkSilence(j); break; } } } } } //This is to check when the spacebar is pressed //and add a new touch to the touches-array everytime void keyPressed(){ if (key==' '){ touches.add(new Touch()); pos = you.getYouPos(); //SAVE the value of the you position at the time the key is pressed //and add it to the positions FloatList touchPositions.append(pos); touchCounter+=1; //increasing the touchCounter to be ready for next value } } void mouseClicked(){ start = true; } //function that makes sure no sounds are playing before playing the pressed one void checkSilence(int index){ for (int i=0; i<13; i++){ if (notes[i].isPlaying()){ notes[i].stop(); } } notes[index].play(); }
Difficulties
While trying to get the program to work. I ran into three main difficulties.
- The first one was that every time I press the space bar to create a new Touch, the previous one would disappear. To fix this, I used an ArrayList and stored every created Touch object there.
- The second one was that, after pressing the spacebar and making a touch appear, moving the person moves the Touch objects along with it (because they use the x-position of the person as the parameter). So to fix this, every time the spacebar is pressed, I stored the x-position of the person in a temporary value and appended it to a FloatList (It ends up containing all Touch objects’ positions that can easily be accessed by indices.)
- The third and final one was that the sounds made by pressing a piano key were overlapping, thus creating a lot of noise. To stop this, I included a function that lets a note be played only after it has stopped the previous one from playing.
All of these failed attempts can be visualized, respectively, in this video:
Final Outcome
Here is how it turned out in the end 🙂
another hit this week, good job. i especially like the little animated “hits” with the lines when the pellet hits the keys.