Remind me to never create a sound-based game again.
This project was a roller coaster, there were some days where I would take the entire day just to debug one tiny thing, and some days where I could make a huge advancement with little resistance. Thankfully, I am happy to where I got.
Since I already had a python based code, starting the game was easy. Myfirst few elements, the image, the map, the player were easy to achieve, but it changed fast. When I got to the borders, I had to redo a large aspect of what it used to be. I refined them, which was time-consuming because of how I had to keep track of X and Y values of the borders since I individually drew them. Then figuring out the movement of the player in relation to the borders was a nightmare. It was nothing related to my previous code, and after spending the whole day on it, I resorted to pencil and paper, trying to figure out how to code the movement. Here’s my sad sketch:
After that celebratory dance, I was on to other elements, like the cats. This proved to be relatively easy, since I had coded this kind of thing before. You hit a cat, you die. It was simple. It was so easy, as a matter of fact, I thought it would be great if I would add another element, humans. I thought “they’re so simple, and I could just make it so that if I hit them, I lose points and time, I don’t die.” After spending a few hours sketching 5 illustrations of humans, and adding it to my code, I realised I had misjudged how ‘easy’ it was. My issue was that I had my code so that whenthe distance between the player and the object is less than an amount, the score decreases. The issue with this is that it would continuously decrease until the object is out of bounds when I just wanted it to decrease once. Eventually, I did it in a way where once it hits the object, a boolean is triggered to true, you lose the points, and then after 1 second, the boolean goes back to false, and you can hit it again. (you probably shouldn’t though).
Other elements I was excited to add were things like adding a score, where it was calculated based on your time (the faster the better) and each sound you find, and it tells you your score at the end. (A very small thing I added was changing the score to green when it’s positive, red when it’s negative). Another thing was also adding a tally on the top left of the screen, to see how many sounds left to find, as playing it I realized I was always wondering how many I had left.
Another element I added (although it doesn’t look as nice as I wanted it to), was having the location you are at glow in gold for a second to indicate that you found the sound in that spot. I also made a ‘ding’ sound to be heard when you get a point, so that you know it happened. I changed the main players’ face to my off-brand illustration of the NYUAD falcon, which I’m not a fan of, but someone pointed out to me that with the addition of humans, the player cannot be sure which one they are controlling.
The sound was tricky, because there were so many bugs that kept showing up. Java only allows sound in the main program, not in classes, so I had to keep experimenting and trying to see how to fix these issues. For example, having the sounds in an array didn’t work. Often times the sounds would overlap because of the draw loop triggered over and over again, especially when mixed with restarting, the sounds would layer over each other. My main solution was to make sure that when the game runs, all the sounds are playing but on mute, so when a location is triggered, the sound unmutes, instead of just playing at a random tempo. Also made sure for the song to keep playing when you win, so you can hear your music, but to stop playing when you die.
Moving on to the restart, instruction, and game over screens. These where also a struggle to figure out, because instead of having most of my initiations in a class like I would normally do, I had it in the main page to correlate with the sounds. Eventually, I figured it out, having functions for each screen, and then debugging for a while. I also had to redesign a few elements for the screens.
int gameScreen = 0; PImage dead, won, instructions; boolean trigger = false; Game game; Creature creature; Cats[] cats; Humans[] humans; Borders[] horizentalBorders; Borders[] verticalBorders; Locations[] locations; //SoundFile[] sounds; SoundFile backgroundSound, meow, ding, baraha, alarm, dining, library, knock, palms, theatre; void setup() { //This trigger is just fir the cat meow trigger = false; creature = new Creature (50, 450, 10); game = new Game (1024, 786, 585); cats = new Cats[3]; humans = new Humans[3]; //for displaying the cats and humans, in random places and speeds for ( int g = 0; g< 3; g++) { cats[g] = new Cats (int(random(200, 900)), int(random(200, 600)), 15, 70, 70, 200, 800); humans[g] = new Humans (int(random(200, 900)), int(random(200, 900)), 15, 70, 70, 200, 800); } //Creating the locations for the sound to br triggered. These dont change locations = new Locations[7]; locations[0] = new Locations (250, 270, 100); // library locations[1] = new Locations ( 250, 470, 100); // Palms locations[2] = new Locations (400, 310, 50); //baraha locations[3] = new Locations (790, 250, 100); //theatre locations[4] = new Locations (950, 400, 100); //dining locations[5] = new Locations (800, 620, 200); // knock locations[6] = new Locations (570, 570, 200); // alarm size(1024, 768); background(255, 255, 255); //borders of the map, where you cannot enter from horizentalBorders = new Borders[14]; verticalBorders = new Borders[13]; horizentalBorders[1] = new Borders (444, 286, 575, 286); //h horizentalBorders[2] = new Borders (442, 346, 520, 346); //h horizentalBorders[3] = new Borders (402, 470, 502, 470); //h horizentalBorders[4] = new Borders (590, 472, 712, 472); //h horizentalBorders[5] = new Borders (754, 493, 812, 493); //h horizentalBorders[6] = new Borders (70, 175, 440, 175); // h horizentalBorders[7] = new Borders (70, 375, 422, 375); //h horizentalBorders[8] = new Borders (310, 725, 970, 725); //h horizentalBorders[9] = new Borders (530, 527, 586, 527); //h horizentalBorders[10] = new Borders (536, 595, 586, 595); // h horizentalBorders[11] = new Borders (350, 614, 396, 614); //h horizentalBorders[12] = new Borders (754, 602, 808, 602); // h horizentalBorders[0] = new Borders (756, 640, 808, 640); // h verticalBorders[12] = new Borders (440, 175, 440, 270); //h verticalBorders[1] = new Borders (540, 346, 540, 455); //v verticalBorders[2] = new Borders (582, 286, 582, 456);// v verticalBorders[3] = new Borders (70, 175, 70, 375); //v verticalBorders[4] = new Borders (310, 572, 310, 725); //v verticalBorders[5] = new Borders (970, 525, 970, 725); // v verticalBorders[6] = new Borders (775, 675, 775, 720); // v verticalBorders[7] = new Borders (800, 675, 800, 720); //v verticalBorders[8] = new Borders (586, 527, 586, 595); // v verticalBorders[9] = new Borders (536, 529, 536, 595); //v verticalBorders[10] = new Borders (396, 614, 396, 664); //v verticalBorders[11] = new Borders (808, 602, 808, 640); // v verticalBorders[0] = new Borders (756, 602, 756, 640); // v //if you die- page dead = loadImage("sorry.png"); //if you win - page won = loadImage("finalpage.png"); //instructional page instructions = loadImage("instructions.png"); //loading the sounds of the song backgroundSound = new SoundFile(this, "OriginalBacktrack.mp3"); baraha = new SoundFile(this, "CardSwipe.mp3"); knock= new SoundFile(this, "Knock_RA.mp3"); theatre = new SoundFile(this, "SneezeCough.mp3"); library = new SoundFile(this, "Typing_clicking.mp3"); palms = new SoundFile(this, "Birds.mp3"); dining = new SoundFile(this, "Didyoudothereading.mp3"); alarm = new SoundFile(this, "iPhoneAlarm.mp3"); meow = new SoundFile(this, "meow.mp3"); ding = new SoundFile(this, "Ding.mp3"); //playing all the sounds in loop for easier restart (less bugs), all on mute except background, // this is so that whenever they are triggered they are still on tempo for the song backgroundSound.play(); baraha.loop(); baraha.amp(0.0); knock.loop(); knock.amp(0.0); theatre.loop(); theatre.amp(0.0); library.loop(); library.amp(0.0); dining.loop(); dining.amp(0.0); alarm.loop(); alarm.amp(0.0); meow.amp(0.5); //restart(); } void sound() { // locating each location for their respective sound, because an array of sounds did not work if ( locations[0].trigger == true) { library.amp(1.0); println("library"); } if ( locations[1].trigger == true) { palms.amp(1.0); } if ( locations[2].trigger == true) { baraha.amp(1.0); } if ( locations[3].trigger == true) { theatre.amp(1.0); } if ( locations[4].trigger == true) { dining.amp(1.0); } if ( locations[5].trigger == true) { knock.amp(1.0); } if ( locations[6].trigger == true) { alarm.amp(1.0); } } void initScreen() { //initial screen, instruction page image(instructions, 0, 0); text("Click anywhere to begin ! ", 300, 700); } void restart() { // upon restart // this was a response to a cat bug trigger = false; creature = new Creature (50, 450, 10); game = new Game (1024, 786, 585); cats = new Cats[3]; humans = new Humans[3]; for ( int g = 0; g< 3; g++) { cats[g] = new Cats (int(random(200, 900)), int(random(200, 600)), 15, 70, 70, 200, 800); humans[g] = new Humans (int(random(200, 900)), int(random(200, 900)), 15, 70, 70, 200, 900); } locations = new Locations[7]; locations[0] = new Locations (250, 270, 100); // library locations[1] = new Locations ( 250, 470, 100); // Palms locations[2] = new Locations (400, 310, 50); //baraha locations[3] = new Locations (790, 250, 100); //theatre locations[4] = new Locations (950, 400, 100); //dining locations[5] = new Locations (800, 620, 200); // knock locations[6] = new Locations (570, 570, 200); // alarm backgroundSound.loop(); baraha.loop(); baraha.amp(0.0); knock.loop(); knock.amp(0.0); theatre.loop(); theatre.amp(0.0); library.loop(); library.amp(0.0); dining.loop(); dining.amp(0.0); alarm.loop(); alarm.amp(0.0); //meow.loop(); meow.amp(0.5); } void draw () { // to make it easier which game scteen we are on if (gameScreen == 0) { // instructions page initScreen(); } else if (gameScreen == 1) { // the game gameScreen(); } else if (gameScreen == 2) { // if you lose gameOverScreen(); } else if (gameScreen == 3) { // if you win gameWinScreen(); } } void gameScreen() { game.display(); creature.display(); for ( int g = 0; g< 3; g++) { cats[g].display(); humans[g].display(); } for ( int n = 0; n < 12; n ++) { horizentalBorders[n].displayBorders(); } for ( int m = 0; m < 13; m ++) { verticalBorders[m].displayBorders(); } for ( int l = 0; l <7; l ++) { // checking if the locations are triggered locations[l].display(); locations[l].trigger(); } if (game.count <= 0) { // if the time runs our, you lose creature.alive = false; creature.win = false; } if (creature. alive == false && creature.win == true) { // if you win gameScreen=3 ; gameWinScreen(); } if (creature. alive == false && creature.win == false) { // if you lose gameScreen=2 ; gameOverScreen(); } } void gameOverScreen() { // lost screen image (dead, 187, 59); } void gameWinScreen() { // won screen, dsplaying score as well image (won, 187, 59); text("Your Score: " + int(game.scorecount), 420, 420); } void mouseClicked() { if ( gameScreen == 0) { gameScreen= 1; } if (gameScreen == 2 || gameScreen == 3) { restart(); gameScreen= 1; println("restart"); } }
// class borders for the map class Borders{ float x, y, x2, y2; Borders (int _x, int _y, int _x2, int _y2 ){ x = _x; y = _y; x2 = _x2; y2 = _y2; } void displayBorders(){ //stroke(200); noStroke(); line(x,y,x2,y2); } }
// creature cats class Cats { float x, y, r, w, h, x1, x2; float vy = 0; float vx = 0; boolean alive = true; PImage img; Cats (float _x, float _y, int _r, int _w, int _h, int _x1, int _x2) { x = _x; y = _y; r = _r; vy = 0; vx = random(1, 3); w = _w; h = _h; x1 = _x1; x2 = _x2; // picking a random image of the 3 cat versions img = loadImage("cat" + str(int(random(1, 4))) + ".png"); } void display() { update(); noFill(); noStroke(); image( img, x-r, y-r, 50, 50); circle(x, y, r*2); } void update() { // movement of the cats y += vy; x += vx; // for the cats to move back and forth if (x < x1) { vx *= -1; } else if ( x > x2) { vx *= -1; } } float distance(int tarX, int tarY) { // to calculate the distance between the cat and another object return (pow( (pow(( x - tarX), 2) + pow((y - tarY), 2)), 0.5)); } boolean trigger() { return false; } }
//Clas for player class Creature { int x, y, r; float vy = 0; float vx = 0; int directionH = 0; int numloc = 0; boolean alive = true; boolean win = false; boolean borderCheckY = true; boolean borderCheckX = true; PImage img, green; boolean leftKey = false; boolean rightKey = false; boolean upKey = false; boolean downKey = false; int previousTime = 0; float timePassed = 1000; Creature (int _x, int _y, int _r) { x = _x; y = _y; r = _r; vy = 0; vx = 0; img = loadImage("player.png"); } void sound() { // function to equate each sound to its respective location, play the sound upon trigger if ( locations[0].trigger == true) { library.amp(1.0); } if ( locations[1].trigger == true) { palms.amp(1.0); } if ( locations[2].trigger == true) { baraha.amp(1.0); } if ( locations[3].trigger == true) { theatre.amp(1.0); } if ( locations[4].trigger == true) { dining.amp(1.0); } if ( locations[5].trigger == true) { knock.amp(1.0); } if ( locations[6].trigger == true) { alarm.amp(1.0); } } void display() { update(); noFill(); noStroke(); image( img, x-r, y-r, 50, 50); circle(x, y, r*2); } void update() { keyPressed(); y += vy; x += vx; // for movement of the player and keys + game borders if ( leftKey == true && x>=0) { directionH = 1; vx = -1; } else if ( rightKey == true && x + r <= 1024) { vx = 1; directionH = 0; } else if ( downKey == true && y +r <= 784) { vy = 1; } else if ( upKey == true && y >= 0 ) { vy = -1; } else { vx = 0; vy = 0; } // checking for collision of map borders collisionY(); collisionX(); for ( int g = 0; g< 3; g++) { if (cats[g].distance(x, y) <= r +cats[g].r ) { // when player hits a cat if (trigger == false) { // mouse sound effect + you die, and the sounds stop meow.play(); palms.amp(0.0); baraha.amp(0.0); library.amp(0.0); theatre.amp(0.0); dining.amp(0.0); knock.amp(0.0); alarm.amp(0.0); println("meow"); trigger = true; alive = false; win = false; } } // if you hit a human, your score goes down, and you lose 10 seconds off your timer if (humans[g].distance(x, y) <= r +humans[g].r) { if (humans[g].trigger == false) { game.count -=10; game.scorecount-=20; previousTime = millis(); humans[g].trigger = true; game.display(); } if ( millis() > previousTime + timePassed) { previousTime= millis(); humans[g].trigger = false; } } } // probably one of the most important parts. Code to see if you hit a location for ( int o = 0; o< 7; o++) { if (locations[o].trigger == false) { if (locations[o].distance(x, y) <= 20) { locations[o].trigger = true; if (tally.size() >0) { // adds to your score numloc +=1; game.scorecount += ((120 - (millis()/1000))*creature.numloc); println(game.scorecount); // removes a string on top left to see how many you have left tally.remove(0); locations[o].display2(); // ding as an indicator that you got one ding.play(); sound(); //image (green, height, width); println(tally); } } // check for a win. If the string length == 0, you won } else if (tally.size() == 0) { win = true; alive = false; } } } // checking to see if you hit the horizontal borders. makes you stop moving void collisionY() { for (int i = 0; i <13; i++ ) { if ( vy + y + r >= horizentalBorders[i].y && vx + x + r >= horizentalBorders[i].x && x + vx +r <= horizentalBorders[i].x2 && vy + y + r <= horizentalBorders[i].y2) { vy = 0; } } } // collision with vertical borders. void collisionX() { for (int j = 0; j < 11; j++) { if ( vx + x + (r*2) <= verticalBorders[j].x && vx + x + (r*2) >= verticalBorders[j].x2 && y + vy + r >= verticalBorders[j].y && vy + y + r <= verticalBorders[j].y2) { //println("stop3"); vx = 0; } } } // equating key pressed with players movements void keyPressed() { if (keyPressed && keyCode == LEFT ) { leftKey = true; } else { leftKey = false; } if (keyPressed && keyCode == RIGHT) { //&& x+r <= game.w rightKey = true; } else { rightKey = false; } if (keyPressed && keyCode == UP) { upKey = true; } else { upKey = false; } if (keyPressed && keyCode == DOWN) { // && y+r <= game.h downKey = true; } else { downKey = false; } } float distanceCreature(int tarX, int tarY) { return (pow( (pow(( x - tarX), 2) + pow((y - tarY), 2)), 0.5)); } }
import processing.sound.*; PImage img; StringList tally; class Game { float w, h, g; int count = 120; float scorecount = 0; Game(int _w, int _h, int _g) { w = _w; h = _h; g = _g; img = loadImage("map.jpg"); tally = new StringList("I", "I", "I", "I", "I", "I", "I" ); } void display () { // map image image (img, 0, 0); fill (0, 0, 0); textSize(25); // 3d effect on timer text("Time Remaining: " + count, 365, 50); fill(255, 255, 255); textSize(25); text("Time Remaining: " + count, 363, 48 ); fill (255, 255, 255); // displaying the tally on the left for (int i = 0; i <tally.size(); i++) { text (tally.get(i), 50+(i*10), 50); } fill(150, 17, 17); for (int i = 0; i <tally.size(); i++) { text (tally.get(i), 50+(i*10), 50); } fill (255, 255, 255); text ("Score: " + int(scorecount), 850, 50); // displaying score, with 3d effect and also green if positive, red if negative if (scorecount <=0) { fill(150, 17, 17); text ("Score: " + int(scorecount), 849, 49); } else if (scorecount >=0) { fill(17, 150, 17); text ("Score: " + int(scorecount), 849, 49); } // for decreasing time if (creature.alive == true) { if (frameCount % 60 == 0) { //println (frameCount); count -=1; } if (count <= 0) { creature.win=false; creature.alive = false; } } } }
// creature cats class Humans { float x, y, r, w, h, y1, y2; float vy = 0; float vx = 0; boolean alive = true; PImage img; boolean trigger = false; Humans (float _x, float _y, int _r, int _w, int _h, int _y1, int _y2) { x = _x; y = _y; r = _r; vx = 0; vy = random(1, 3); w = _w; h = _h; y1 = _y1; y2 = _y2; img = loadImage("human" + str(int(random(1, 6))) + ".png"); } void display() { update(); noFill(); noStroke(); image( img, x-r, y-r, 50, 50); circle(x, y, r*2); } void update() { y += vy; x += vx; if (y < y1) { vy *= -1; } else if ( y > y2) { vy *= -1; } } float distance(int tarX, int tarY) { return (pow( (pow(( x - tarX), 2) + pow((y - tarY), 2)), 0.5)); } boolean trigger() { return false; } }
Finally, here is the final game (That i am very bad at playing, sorry its so frustrating to watch):