Overview
Finally, after all the ranting and hours of crying, the midterm game project is complete and it looks and works great, as intended, who am I kidding even. To summarize the game, the theme is around stress struggles as a student. To make it more personalized, the student is an NYUAD falcon. The game design includes free-falling stressful and stress-relieving activities that the player has to avoid and collect respectively while moving across the screen horizontally. It is a race between sanity and stress. The first total to reach 320 points wins. There are four types of free-falling activities that are worth a different number of points and allows the player certain boosted or dulled abilities. The game starts with the main page that allows the player to either start the game directly or navigate to the instructions screen. The game has three different difficulty levels that the player has to choose from. As the game concludes, a game over screen is displayed with results and the option to restart the game with a mouse press.
Production
This production process has been by far the most stressful, ironic right. With the code breaking down so many times, I started the initial game design from scratch with circles as the objects and incorporated the movement. See the initial sketch below.
Once the intended movement was achieved, building upon previous progress, I selected my images to use and incorporated them into the previous sketch. Finally, as shown below, the basic design of the game was complete.
The following steps included working more on the game outlook. Putting my artsy skills to use was real fun while coloring the free-falling objects png files. To have a better outlook, I used two images for the player, flipped left and right, which were chosen based on the movement of the player on the screen.
After completing this, the main landing screen, instructions screen, main gameplay screen, and game over screen were redesigned. For this, I used different background images, fonts, and text placements. Like most sketch screens, I wanted to have a faded image background during the main gameplay screen. However, with png moving down the screen, the background would distort, and also the increased loading time of the screen made the movement of objects weird and much less smooth. Therefore, after trying several different patterns and backgrounds, I just stick to a solid background. This came out looking pretty good design and allowing the moving objects’ color to pop on screen. Below are screenshots of the final designs.
Once all of the design was finalized, the last step was to include sound. There were challenges with different sounds playing at a time, ending up in weird noise. So, I finalized a single background sound looped until the game over screen and whenever a selection is made using mouse press or keypress, a notification sound is played. With this, I finalized my game design, and below is a demo
Code for the game is below (+500 lines) and the entire sketch can be downloaded from here
import processing.sound.*; //Global variables responsible for various states in game //That includes: difficulty select, playing game, game over Game start; Player player; String state = "MAIN"; String difficulty = "EASY"; //array to store the different points Activity[] activityarray; //all the images and fonts for design PImage food; PImage leisure; PImage deadlines; PImage grades; PImage user; PImage userr; PImage bdrop; PImage miss; PImage bg; PImage diff; PImage inst; PImage keys; PImage back; PFont f; PFont g; //global variables for location check and size float speed = 1; float previouspos = 0; //soundfile SoundFile audio; SoundFile sel; //setup the game void setup(){ fullScreen(); //initializing game and player start = new Game(state); player = new Player(); //loading images food =loadImage("data/food.png"); leisure = loadImage("data/leisure.png"); deadlines = loadImage("data/deadline.png"); grades = loadImage("data/test.png"); user = loadImage("data/user.png"); userr = loadImage("data/userr.png"); bg = loadImage("data/bg.jpg"); diff = loadImage("data/diff.jpg"); bdrop = loadImage("data/backdrop.jpg"); inst = loadImage("data/inst.jpg"); keys = loadImage("data/key.png"); miss = loadImage("data/stress.png"); back = loadImage("data/backdrop.jpg"); back.resize(width,height); //loading sound audio = new SoundFile(this, "data/game.mp3"); audio.loop(); sel = new SoundFile(this, "data/select.mp3"); //font f = loadFont("FootlightMTLight-55.vlw"); g = loadFont("JavaneseText-55.vlw"); //diffculty level if(difficulty == "EASY"){ speed = 2; } if(difficulty == "MEDIUM"){ speed = 4; } if(difficulty == "HARD"){ speed = 8; } activityarray = new Activity[250]; //initializing the array of activity object fillarray(); } void draw(){ //image(back,0,0,width,height); start.display(); } //fill activity array void fillarray(){ //choose random x values and drop activities for(int i=0; i<activityarray.length; i++){ float x_pos = random(0,width-30); while(x_pos == previouspos){ x_pos = random(0,width-30); } previouspos = x_pos; float type = random(1,50); String activity_ability = "FOOD"; //randomly choosing ability based on random number if(type < 5){ activity_ability = "LEISURE"; } else if(type > 45){ activity_ability = "GOODGRADES"; } if(type > 10 && type <=25){ activity_ability = "DEADLINE"; } //create new activity object activityarray[i] = new Activity(x_pos,random(-10000,0),speed,activity_ability); } } //game class class Game{ String mode; int score = 0; int lostpoints = 0; float speed = 0; Game(String state){ mode = state; } void display(){ textAlign(CENTER,CENTER); //check for game over if(lostpoints >= 320 || score >= 320){ mode = "OVER"; audio.stop(); } //load the main landing page if(mode=="MAIN"){ //background gradient background(255); loadPixels(); for(int y=0;y<height;y++){ for(int x=0;x<width; x++){ int index = x+y*width; pixels[index] = color(map(y,0,height,155,0),0,map(y,0,height,255,0),50); } } updatePixels(); //displaying text textFont(f,random(90,97)); fill(150,120); text("STRESS FREE NYUAD?",width/2,height/3); fill(255); textFont(f,90); text("STRESS FREE NYUAD?",width/2,height/3); textFont(g,30); fill(255); text("START GAME - G",width/2,2*height/3 - height/14); text("INSTRUCTIONS - I",width/2,2*height/3); textFont(f,20); text("Press corresponding keys to initiate the game",width/2,height-height/14); } //loading the instructions page if(mode=="INSTRUCTIONS"){ fill(0); image(inst,0,0,width,height); textFont(f,60); text("INSTRUCTIONS",width/2,height/10); //display images and text image(keys,width/4-width/7,height/2.5 - height/8,130,60); image(food,width/2-130/2,height/2.5 - height/8,130,60); image(grades,3*width/4+width/16,height/2.5 - height/7,130,80); image(leisure,width/4-width/7,1.75*height/2.5 - height/8,130,60); image(deadlines,width/2-130/2,1.75*height/2.5 - height/8,130,60); image(miss,3*width/4+width/16,1.75*height/2.5 - height/8,130,60); textFont(g,22); text("Use arrow L-R keys to\nmove across the screen",width/4-width/10,height/2.5); text("Eat healthy to gain +05 pts",width/2,height/2.5); text("Perform well on assignment\n to get +10 pts ",3*width/4+width/10,height/2.5); text("Do refresing leisure activities\n to get boosted speed",width/4-width/10,1.75*height/2.5); text("Avoid deadline pressure\nto skip decreased speed",width/2,1.75*height/2.5); text("Missed pts added to stress level\nFirst to reach 320 pts win",3*width/4+width/10,1.75*height/2.5); textFont(g,random(32,34)); fill(random(255),0,0); text("START GAME - G",width/2,9*height/10); } //displaying the difficulty selection screen if(mode=="DIFFICULTY"){ image(diff,0,0,width,height); //turn the text red and create shadow effect when mouse is hovered //over the level selection part if(mouseX>0 && mouseX<width/3){ fill(255,0,0); textFont(g,54); text("EASY - E",width/4-width/10,height/2); } else if(mouseX>width/3 && mouseX<2*width/3){ fill(255,0,0); textFont(g,54); text("MEDIUM - M",width/2,height/2); } else if(mouseX>2*width/3 && mouseX<width){ fill(255,0,0); textFont(g,54); text("HARD - H",3*width/4+width/10,height/2); } fill(255); textFont(g,50); text("EASY - E",width/4-width/10,height/2); text("MEDIUM - M",width/2,height/2); text("HARD - H",3*width/4+width/10,height/2); textFont(f,20); text("Press corresponding keys to initiate the game",width/2,height-height/14); } //game over screen if(mode=="OVER"){ //display the background image fill(255); image(bg,0,0,width,height); //display the text textFont(f,60); text("GAME OVER",width/2, height/3); textFont(f,35); text("Your Score:",width/2 - width/6, height/3 +height/6); text("Stress Level:",width/2 - width/6, height/3 + height/4); textFont(g,45); text(score,width/2, height/3 +height/6); text(lostpoints,width/2, height/3 + height/4); textFont(f,35); text("points",width/2 + width/6, height/3 +height/6); text("points",width/2 +width/6, height/3 + height/4); //display result string based on scores textFont(g,45); if(score>=lostpoints){ if(score==lostpoints){ text("IT'S A TIE", width/2, 2.25*height/3); } else{ text("YOU WON", width/2, 2.25*height/3); } } else{ text("YOU LOST", width/2, 2.25*height/3); } textFont(f,20); text("Please click on screen to restart game",width/2,height-height/14); } //main game screen if(mode=="PLAY"){ //background color background(back); fill(0); //score board rect(width-width/6,height/14.5,width/8,height/13.5); fill(255); textFont(g,18); text("Your:", width-width/7,height/12); text("Stress:", width-width/7,height/8); textFont(f,random(24,26)); fill(random(200,255)); text(score,width-width/10,height/12); text(lostpoints,width-width/10,height/8); fill(255); textFont(g,18); text("points", width-width/16.5,height/12); text("points", width-width/16.5,height/8); //main display player player.display(); //display activities for(int i =0; i< activityarray.length;i++) { activityarray[i].display(); if(activityarray[i].yloc > height){ lostpoints += activityarray[i].point; activityarray[i].point = 0; } //resize the image upon collision to have effect //of collecting the activity if(activityarray[i].collisions() == true){ score += activityarray[i].point; activityarray[i].awidth = 0; activityarray[i].aheight = 0; activityarray[i].point =0; } } } } } //player class class Player{ float pwidth; float pheight; float xPos; float yPos; boolean left; boolean right; float speed; float fast_time; float slow_time; Player(){ pwidth= 100; pheight = 100; xPos = width/2 - pwidth; yPos = height - pheight; left = false; right = false; speed = 7; fast_time = 0; slow_time = 0; } void display(){ //tracking the time when boosted speed if(speed == 12){ fast_time += 1; //last 100 frames if(fast_time == 100){ fast_time = 0; speed = 7; } } //tracking the time when slowed speed if(speed == 1){ slow_time += 1; //last 100 frames if(slow_time == 100){ slow_time = 0; speed = 7; } } //update the position on screen update(); //draw the player if(left==true){ image(user,xPos,yPos,pwidth,pheight); } else if(right==true){ image(userr,xPos,yPos,pwidth,pheight); } else{ image(userr,xPos,yPos,pwidth,pheight); } } //update the position of the player void update(){ if(left==true && xPos >=0){ xPos -= speed; } if(right==true && xPos <= width-pwidth){ xPos += speed; } } } //Class of falling activities/ points class Activity{ float awidth = 60; float aheight = 60; //coordinates float yloc; float xloc; float speed; String ability; //standard point int point = 5; //image PImage activityimg; Activity(float xpos, float y,float s, String a){ xloc = xpos; speed= s; yloc = y; ability = a; //updating point values and image based on type if(ability == "GOODGRADES"){ activityimg = grades; point = 10; } else if(ability == "LEISURE"){ activityimg = leisure; } else if(ability == "DEADLINE"){ point = 0; activityimg = deadlines; } else{ point =5; activityimg = food; } } //display the activity object void display(){ update(); image(activityimg,xloc,yloc,awidth,aheight); } //update the locations void update(){ //move down yloc += speed; } //check for collisions boolean collisions(){ if((player.xPos + player.pwidth >= xloc) && (player.xPos <= xloc + awidth)){ if((yloc + aheight >= player.yPos) && (yloc <= player.pheight + player.yPos)){ //check if it collides with special activity and update speed accordingly if(ability == "LEISURE"){ player.speed = 12; } if(ability == "DEADLINE"){ player.speed = 1; } return true; } } return false; } } //keep track of key presses on screen void keyPressed(){ if(start.mode == "MAIN"){ if(keyCode == 73){ //73 = 'i' sel.play(); start.mode = "INSTRUCTIONS"; } if(keyCode == 71){ //71 = 'g' sel.play(); start.mode = "DIFFICULTY"; } } if(start.mode == "INSTRUCTIONS"){ if(keyCode == 71){ //71 = 'g' sel.play(); start.mode = "DIFFICULTY"; } } if(start.mode == "DIFFICULTY"){ if(keyCode == 69){ //71 = 'e' sel.play(); difficulty = "EASY"; start.mode = "PLAY"; } if(keyCode == 77){ //71 = 'm' sel.play(); difficulty = "MEDIUM"; start.mode = "PLAY"; } if(keyCode == 72){ //71 = 'h' sel.play(); difficulty = "HARD"; start.mode = "PLAY"; } } //move until key pressed if(start.mode=="PLAY"){ if(keyCode == RIGHT){ player.right = true; } if(keyCode == LEFT){ player.left = true; } } } //stop motion when key is released void keyReleased(){ if(start.mode == "PLAY"){ if(keyCode == RIGHT){ player.right = false; } if(keyCode == LEFT){ player.left = false; } } } //replay the game void mouseClicked(){ if(start.mode=="OVER"){ sel.play(); player.left = false; player.right = false; fillarray(); background(0); start = new Game("MAIN"); audio.play(); } }