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();
}
}