UNI RUSH
INTRODUCTION & GAME RULES:
Uni Rush is an innovative idea for gaming in processing which have sketched the idea of a student escaping the reality of university course grades. The basic logic behind it is that the student character is moving in all four directions and the grade letters are coming from the right side of the screen. When a grade passes behind the character, the score increases. However, if the character moves out of the screen or touches any grade, whether bad or good, then it is game over. For the character movements, conventional gaming keyboard keys were used: w for up, a for left, s for down, and d for right.
INSPIRATION BEHIND UNI RUSH:
The inspiration behind the game I wanted to create comes from how overprotective I tend to be with my GPA. Since I was a kid, I always would work to achieve the highest grades and even the slightest deduction of grades would make me cry. However, after I came to university, that reality changed, and it’s been like a roller-coaster with all its ups and downs. The game resembles a university student who is running all over to escape the sad reality of university grades. Based on that, the more grades the character is able to escape , the higher the score is. I have always been keen to understand how gpa calculation works, so I searched it up and decided to base my game on that. Yet, this did not work and the fun of it made me come up with a new game logic, closer to reality.
CHALLENGES & PROBLEMS:
The very first and foremost challenge I have faced was rendering the grades continuously on the screen, in addition to its movement within the specified speed. This has put me in chaos, as I felt that my code is all over the place. A second difficulty was during the movement of the character. Third, the management of frames and the glitches found during frame rendering. Last, but not least, playing the sound based on the movement of the character and rendering the grade letters as a series of image was an obstacle.
OBJECTS USED :
1- Grade letters
The grade letter objects are moving images of all grade letters
2- Student Character
This is an NYU Mascot cartoon character given a bigger size as compared to the grade letters and is able to move left- right & up-down based on the key pressing
3- Game Background
This object is a university campus image
4- GPA Score Prompt
This object is a simple score text prompt which gets added as the character escapes the grade letters.
PROCEDURE:
I have started my game by implementing the player character class. This class was responsible for the actions taken by the character. Later, I have implemented attributes and methods in that class that helps with the movement of the character in all four directions. The rendering of the character is also implemented in that class. I performed the default constructor for the character class which takes x and y positions of the character.
The die function implemented to check if the player character moves out of the screen.
//function to check if the player character is out screen which means the character died //stopping the game background sound and playing main menu sound void die() { this.xPos = -500; this.yPos = -500; this.CharacterDropped = true; BackSoundEffect.stop(); SkipGradeLetterSound.play(); MainMenuSoundEffect.play(); }
The player character is moving based on the key pressed events which work on the specified keys for the movement of the player character in upward, downward, left, or right direction.
void keyPressed() { //If the key is 'w' then moving the character upward if(key == 'w') Character.move(0, -1); //if the key is 'a' then moving the character left if(key == 'a') Character.move(-1, 0); //if the key is s then moving the character down if(key == 's') Character.move(0, 1); //if the key is 'd' then moving the character right if(key == 'd') Character.move(1, 0); //if the key is 'r' then setting the character to its default state and postion if(key == 'r' || Character.CharacterDropped) { //stop playing background sounds BackSoundEffect.stop(); MainMenuSoundEffect.stop(); BackSoundEffect.loop(); //restarting game to its default configuration restart(); gameFrame = 0; } }
For rendering the player character on screen I implemented show() method inside the character class which is loading the player character image on the current frame x and y position.
//for rendering the player character in rectangular box structure on screen void show() { rectMode(CENTER); fill(255); noStroke(); //showing player character image on screen image(PlayerCharacter[currentFrame], this.xPos + xRail * screenHeight/4, this.yPos + this.yRail * screenHeight/4); }
Then I did the implementation letter class which I used later to render grade letters on the screen. The letter class contains attributes for the position of letters and the movement speed of letter images on the screen. Afterwards, I implemented a function to update letters on-screen through frames and another function to check if the letter moves out of the screen. In this case, the letter further drops out from the object array.
The default constructor of the letter class takes the position of the letter as a parameter and sets the default speed and movement direction of the letter on screen.
//default constructor for letter class which takes position of letter as parameter Letter(PVector pos) { //setting radom speed and velocity of the letters for the movement of letter acceleration = new PVector(random(-0.05, 0.05), random(-0.05, 0.05)); velocity = new PVector(random(-2, 2), random(-2, 2)); //default position of each letter position = pos.copy(); //random boundry for each letter movement MovementSpan = random(100.0, 255.0); }
I implemented the function IsLetterDrops() to check if the letter moves out the screen or not. If the letter moves out of the screen then the letter will be removed further from the letters array.
//for checking if the letters moves out of the screen or not boolean IsLetterDrops() { //if the letter movement boundry value is less than 0 then letter is out of screen thus returning true else false if(MovementSpan < 0.0) { return true; } else { return false; } }
For loading background image and sound effects, I made a separate function and called that function in the draw and setup functions to render the graphical assets on screen. There are different sound effects used to represent different behaviours. When the character moves, then the sound effect is different. Thus, for the main menu screen and game over screen, a different sound effect is allocated.
I implemented the loadGraphics() function which is loading the images saving in the global image arrays for the player character and grade letters. Furthermore, the background image is also loading into the global background image variable.
// for loading graphics on screen void loadGraphics() { PlayerCharacter[0] = loadImage("C1.png"); PlayerCharacter[1] = loadImage("C2.png"); PlayerCharacter[2] = loadImage("C3.png"); PlayerCharacter[3] = loadImage("C4.png"); gradeLetters[0] = loadImage("A.png"); gradeLetters[1] = loadImage("B.png"); gradeLetters[2] = loadImage("C.png"); gradeLetters[3] = loadImage("D.png"); gradeLetters[4] = loadImage("F.png"); gradeLetters[5] = loadImage("B+.png"); gradeLetters[6] = loadImage("C+.png"); gradeLetters[7] = loadImage("D+.png"); GPAScoreValues[0] = loadImage("0.png"); GPAScoreValues[1] = loadImage("1.png"); GPAScoreValues[2] = loadImage("2.png"); GPAScoreValues[3] = loadImage("3.png"); GPAScoreValues[4] = loadImage("4.png"); background = loadImage("back.png"); KeyControlsImage = loadImage("Controls.png"); MainPromptText = loadImage("textprompt.png"); GPAScore = loadImage("gpascore.png"); }
In setup() function I am setting the frame rate by using framerate() builtin function, loading the graphical assets, and playing the main menu sound effect in the loop.
//Main setup function for the configuration of game void setup() { //setting the size of screen by width and height size(1080, 720); //setting framerate which means how many times screen should be repeated frameRate(30); //loading sound and background images assets on screen loadAssets(); //Put the game in default configuration and game renders from starting position restart(); //checking true which shows that character is at its default state Character.CharacterDropped = true; //for playing main menu sound MainMenuSoundEffect.loop(); }
In draw() function I am rendering the background image by using image() function and also updating the game assets, for example, repositioning the player character based on the key pressed by the user.
//Main draw function which iterates infinite time and render game screen void draw() { image(background,0,0); //Updating GPA score point and game configuration updateGame(); //showing graphichs and other assets on screen show(); }
FINAL WORK:
CONCLUSION:
For my midterm project, I learned how to implement game logic, render multiple graphics all at the same time, move images from one position to another, and use my knowledge of data structures (for example arrays lists in processing). The game making journey has enhanced my knowledge in regards to different object initialization and creation, and in audio file manipulations from the loading of sound effects that has been done.
GOOGLE DRIVE LINK:
https://drive.google.com/drive/folders/11FKKklnNtAAbftPXK7VQ85PIB9F_IHjX?usp=sharing
COMPLETE CODE OF UNI RUSH GAME 🙂
//importing library for sound import processing.sound.*; //Object Creation for Background Sound Effect while game playing SoundFile BackSoundEffect; //Object Creation For Main Menu Background Sound Effect SoundFile MainMenuSoundEffect; //Object Creation For Sound Effect When Character Fails to Eat Any Grade Letter SoundFile SkipGradeLetterSound; //Object Creation For Sound Effect during the movement of character SoundFile CharacterMovementSoundEffect; //Image object array creation for lion character PImage PlayerCharacter[] = {null, null, null, null}; //Object Creation for grade letters PImage gradeLetters[] = {null, null, null, null, null, null, null, null}; //Object Creation for the background image PImage background; //object creation for key controls for game playing PImage KeyControlsImage; //Object creation for main screen text prompt PImage MainPromptText; //Object creation for GPA Score Prompt PImage GPAScore; //Object creation for GPA score Points PImage GPAScoreValues[] = {null, null, null, null, null, null, null, null, null, null}; //Game screen width and height int screenWidth = 1080; int screenHeight = 720; //for how many times background should be repeated int backgroundRepeat[] = {((int)screenWidth/128) + 2, ((int)screenHeight/128) + 2}; //the starting point of the background image int backgroundOffset = 0; ArrayList<IntersectionWithLetter> fframes; float framespeed; float margin; float marginCounter; int score; int gameFrame = 0; GradeLetters GradeLetters = new GradeLetters(); Character Character; //Main setup function for the configuration of game void setup() { //setting the size of screen by width and height size(1080, 720); //setting framerate which means how many times screen should be repeated frameRate(30); //loading sound and background images assets on screen loadAssets(); //Put the game in default configuration and game renders from starting position restart(); //checking true which shows that character is at its default state Character.CharacterDropped = true; //for playing main menu sound MainMenuSoundEffect.loop(); } //Main draw function which iterates infinite time and render game screen void draw() { image(background,0,0); //Updating game background which include background image,character images and grade letters // updateBackground(); //Updating GPA score point and game configuration updateGame(); //showing graphichs and other assets on screen show(); } //Key pressed function event which occurs when any keyboard key is pressed void keyPressed() { //If the key is 'w' then moving the character upward if(key == 'w') Character.move(0, -1); //if the key is 'a' then moving the character left if(key == 'a') Character.move(-1, 0); //if the key is s then moving the character down if(key == 's') Character.move(0, 1); //if the key is 'd' then moving the character right if(key == 'd') Character.move(1, 0); //if the key is 'r' then setting the character to its default state and postion if(key == 'r' || Character.CharacterDropped) { //stop playing background sounds BackSoundEffect.stop(); MainMenuSoundEffect.stop(); BackSoundEffect.loop(); //restarting game to its default configuration restart(); gameFrame = 0; } } //Main class for Grade letters which inherits the letters class object array class GradeLetters { //grade letters object array ArrayList<Letter> letters; //default constructor for grade letters GradeLetters() { //initializing letters object array in defualt constructor letters = new ArrayList<Letter>(); } //populating grade letters with different screen positions void AddLetter(PVector position, int nletters) { //for loop which iterates through number of letters and adding letter with its position for(int i = 0; i < nletters; i++) { //adding letter with its position this.AddLetter(position); } } //single paramter add letter function which is for initializing new letter with its position void AddLetter(PVector position) { //calling letters array add function and adding new letter with it position into the array letters.add(new Letter(position)); } //function for updating letters on screen void UpdateLettersOnScreen() { //for loop which runs till letters array size in reverse order for (int i = letters.size()-1; i >= 0; i--) { //getting the letter from the array from ith index Letter l = letters.get(i); //rendering the letter on screen l.UpdateLettersOnScreen(); //checking if the letter goes out of screen then removing the letter from the array if (l.IsLetterDrops()) { letters.remove(i); } } } } //letter class which represents single letter object class Letter { //Position of Letter on screen PVector position; //Velocity of movement of letter on screen PVector velocity; //Accelration of movement of letter on screen PVector acceleration; //size of the letter which is randomly generated from values 1 to 4 int size = (int)random(1, 4); //varibale for limiting the area for the movement of each letter on screen. the letter moves in its setting boundry float MovementSpan; //default constructor for letter class which takes position of letter as parameter Letter(PVector pos) { //setting radom speed and velocity of the letters for the movement of letter acceleration = new PVector(random(-0.05, 0.05), random(-0.05, 0.05)); velocity = new PVector(random(-2, 2), random(-2, 2)); //default position of each letter position = pos.copy(); //random boundry for each letter movement MovementSpan = random(100.0, 255.0); } //to update the rendering of letter on screen void UpdateLettersOnScreen() { //updating speed and velocity of letter movement and decresing the movement boundry of letter by value 2. velocity.add(acceleration); position.add(velocity); MovementSpan -= 2.0; //displaying the letter on screen display(); } //to display letter on screen void display() { //setting transparent color fill(255, MovementSpan); //drawing rectangle which represents the boundry of letters rect(position.x, position.y, size, size); } //for checking if the letters moves out of the screen or not boolean IsLetterDrops() { //if the letter movement boundry value is less than 0 then letter is out of screen thus returning true else false if(MovementSpan < 0.0) { return true; } else { return false; } } } //Player Character Class class Character { //The default x and y position value variables for player character. int xPos, yPos; //The default x and y direction of the player character int xRail = 0, yRail = 0; //variable for default image frame value for player character int currentFrame = 0; //varibale for checking of the player character is inside screen or not boolean CharacterDropped = false; //default constructor for player character which sets the default position of the player character Character(int xPos, int yPos) { this.xPos = xPos; this.yPos = yPos; } //function for the movement of player character accoriding to the x and y direction of the character void move(int xDirection, int yDirection) { //checking if the movement of the character is in horizontal direction if(this.xRail + xDirection <= 4 && this.xRail + xDirection >= -1) { //if the y direction value is zero then adding new letter in the followed direction and playing player character movement sound if(yDirection == 0) { GradeLetters.AddLetter(new PVector(this.xPos + xRail * screenHeight/4, this.yPos + this.yRail * screenHeight/4 + 30), 20); keyViewerActive(key, 10, screenHeight - 10); CharacterMovementSoundEffect.play(); } //moving one step forward the direction of player character in x direction this.xRail += xDirection; } //checking if the movement of the character is in verticle direction if(this.yRail + yDirection <= 1 && this.yRail + yDirection >= -1) { //if the x direction value is zero then adding new letter in the followed direction and playing player character movement sound if(xDirection == 0) { GradeLetters.AddLetter(new PVector(this.xPos + xRail * screenHeight/4 + 20, this.yPos + this.yRail * screenHeight/4 + 30), 10); keyViewerActive(key, 10, screenHeight - 10); CharacterMovementSoundEffect.play(); } //moving one step forward the direction of player character in y direction this.yRail += yDirection; } } //function to check if the player character is out screen which means the character died //stopping the game background sound and playing main menu sound void die() { this.xPos = -500; this.yPos = -500; this.CharacterDropped = true; BackSoundEffect.stop(); SkipGradeLetterSound.play(); MainMenuSoundEffect.play(); } //for rendering the player character in rectangular box structure on screen void show() { rectMode(CENTER); fill(255); noStroke(); //showing player character image on screen image(PlayerCharacter[currentFrame], this.xPos + xRail * screenHeight/4, this.yPos + this.yRail * screenHeight/4); } //for updating grade letters on screen void UpdateLettersOnScreen() { //if the player character is in screen boundry if(!CharacterDropped) { //shwoing the player character this.show(); //updating the current frame value to render assests on screen if(frameCount % 5 == 0) { currentFrame++; if(currentFrame == 4) currentFrame = 0; } } } } // class for checking if the player character eat or absorb any grade or not class IntersectionWithLetter { // variable for the x postion int xPos; // varibales for the screen frames and its speed initialized by random values int currentFrame = (int)random(0, 7), frameSpeed = (int)random(4, 6), empty = (int)random(-2, 2); float speed, slowSpeed = 0; //check if there is need to add gpa point or not boolean canAddToScore = true; //default constructor which takes x position of intersecting the grade letter and player character + the speed with which intersection occurs IntersectionWithLetter(int xPos, float speed) { this.xPos = xPos; this.speed = speed; } //function to check if the player character eat any grade letter or not void Eat() { if(!Character.CharacterDropped && Character.yRail != this.empty && Character.xPos + Character.xRail * screenHeight/4 >= this.xPos -40 && Character.xPos + Character.xRail * screenHeight/4 <= this.xPos + 70) { Character.die(); } } //for showing grade letter on the current rendered frame void show() { rectMode(CENTER); noStroke(); //for loop which iterates one time and if the ith index is not empty then showing image of grade letter on current frame for(int i = -1; i <= 1; i++) { if(i != empty) image(gradeLetters[currentFrame], this.xPos, screenHeight/2 + i * screenHeight/4); } } //for updating letters on current rendered frame void UpdateLettersOnScreen() { //updating the speed of grade letters if(this.slowSpeed == 0) this.xPos -= this.speed; else this.xPos -= this.slowSpeed; this.Eat(); this.show(); // if the rendering is done with all frames then resetting the current frame value to zero if(frameCount % frameSpeed == 0) { currentFrame++; if(currentFrame == 8) { currentFrame = 0; } } } } //for updating frames on screen after intersection void UpdateFrames() { //looping to update letters on screen frame after intersection for(IntersectionWithLetter IntersectionWithLetter : fframes) { IntersectionWithLetter.UpdateLettersOnScreen(); } //checking if the top frame is first frame if(fframes.size() > 0 && fframes.get(0).xPos < -128) { //removing the first frame from the screen removeFirstFrame(); } //checking if there is space on rendering more frame on screen if(marginCounter <= 0 && !(gameFrame >= 2400 && gameFrame <= 3030)) { //adding new frame on screen addFrame(); } SlowFrameSpeed(); } //for removing the first frame from the screen void removeFirstFrame() { fframes.remove(0); } //for adding new frame on screen void addFrame() { fframes.add(new IntersectionWithLetter(screenWidth + 128, framespeed)); if(margin > screenWidth/4) margin *= 0.95; marginCounter = margin; if(framespeed < 20) { framespeed *= 1.015; } } //for slowing the frame rendering speed void SlowFrameSpeed() { //if there is space to render more frames on screen then setting the frame speed to 1 if(gameFrame >= 2400 && gameFrame <= 3030) { for(IntersectionWithLetter IntersectionWithLetter : fframes) { IntersectionWithLetter.slowSpeed = 1; } } //if there is no space to render more frames on screen then setting the frame speed to 0 else { for(IntersectionWithLetter IntersectionWithLetter : fframes) { IntersectionWithLetter.slowSpeed = 0; } } } //for updating background of the game void updateBackground() { // if the frame vlue is even then updating the background offset position if(frameCount%2 == 0) { backgroundOffset++; } //if the background offset value is maximum then resetting the offset value to 0 and showing the background image if(backgroundOffset == 128) { backgroundOffset = 0; showBackground(); } } //for displaying background on screen void showBackground() { //nested for loop to render the background image on each pixel on screen in x and y direction for(int x = 0; x < backgroundRepeat[0]; x++) { for(int y = 0; y < backgroundRepeat[1]; y++) { image(background, x*background.width - backgroundOffset, y*background.height); } } } // for converting number to images ArrayList<PImage> numberToArrayOfImages(int number) { ArrayList<PImage> images = new ArrayList<PImage>(); String numberS = str(number); int len = numberS.length(), buffer; //iterating till the number length for(int index = 0; index < len; index++) { //switch for checking for the desired gpa point and if the case match then updating buffer switch(numberS.charAt(index)) { case '0': buffer = 0; break; case '1': buffer = 1; break; case '2': buffer = 2; break; case '3': buffer = 3; break; case '4': buffer = 4; break; case '5': buffer = 5; break; case '6': buffer = 6; break; case '7': buffer = 7; break; case '8': buffer = 8; break; case '9': buffer = 9; break; default: buffer = 0; break; } images.add(GPAScoreValues[buffer]); } return images; } //showing gpa score void showScore() { fill(255); textSize(16); //rendering gpa score image image(GPAScore, 0, 0); ArrayList<PImage> images = numberToArrayOfImages(score); int index = 0; //iterating all gpa score images for(PImage image : images) { int yOffset = 0; //if the gpa score value matches with the image if(image == GPAScoreValues[0]) yOffset = 4; //then rendering the image on screen image(image, 60 + index * 11, 10 - yOffset); index++; } } // for updating gpa score void updateScore() { //checking if player is in screen boundry if(!Character.CharacterDropped) { //checking if the for(IntersectionWithLetter layer : fframes) { if(layer.canAddToScore == true && layer.xPos <= Character.xPos + (Character.xRail * screenHeight/4)) { score++; layer.canAddToScore = false; } } } } //for displaying key viewer on screen void keyViewer() { image(KeyControlsImage, 10, screenHeight - 10 - KeyControlsImage.height); } //for updating the position based on the active key press void keyViewerActive(char keyName, int xPos, int yPos) { fill(50, 255, 50); //if the key is w then drawing square to new required position if(keyName == 'w') { square(xPos + 3 * 3 + 44 + 25, yPos + 3 * 3 - 44 * 2, 46); } //if the key is a then drawing square to new required position else if(keyName == 'a') { square(xPos + 3 + 22, yPos - 3 - 22, 46); } //if the key is s then drawing square to new required position else if(keyName == 's') { square(xPos + 3 * 3 + 44 + 25, yPos - 3 - 22, 46); } //if the key is d then drawing square to new required position else if(keyName == 'd') { square(xPos + 3 * 5 + 44 * 2 + 28, yPos - 3 - 22, 46); } keyViewer(); } // function for main menu screen void mainScreen() { // filling up a rectangle with light color fill(50, 150); rect(screenWidth/2, screenHeight/2, screenWidth, screenHeight); textSize(36); fill(255); imageMode(CENTER); //prompting text on screen image(MainPromptText, screenWidth/2, screenHeight/3); imageMode(CORNER); } // for loading images and sound effect assests on screen void loadAssets() { loadGraphics(); loadSound(); } // for loading graphics on screen void loadGraphics() { PlayerCharacter[0] = loadImage("C1.png"); PlayerCharacter[1] = loadImage("C2.png"); PlayerCharacter[2] = loadImage("C3.png"); PlayerCharacter[3] = loadImage("C4.png"); gradeLetters[0] = loadImage("A.png"); gradeLetters[1] = loadImage("B.png"); gradeLetters[2] = loadImage("C.png"); gradeLetters[3] = loadImage("D.png"); gradeLetters[4] = loadImage("F.png"); gradeLetters[5] = loadImage("B+.png"); gradeLetters[6] = loadImage("C+.png"); gradeLetters[7] = loadImage("D+.png"); GPAScoreValues[0] = loadImage("0.png"); GPAScoreValues[1] = loadImage("1.png"); GPAScoreValues[2] = loadImage("2.png"); GPAScoreValues[3] = loadImage("3.png"); GPAScoreValues[4] = loadImage("4.png"); background = loadImage("back.png"); KeyControlsImage = loadImage("Controls.png"); MainPromptText = loadImage("textprompt.png"); GPAScore = loadImage("gpascore.png"); } //for loading sound effects void loadSound() { //background sound effect BackSoundEffect = new SoundFile(this, "backgroundsound.mp3"); //main menu sound effect MainMenuSoundEffect = new SoundFile(this, "mainmenusound.mp3"); //skipping grade letter sound SkipGradeLetterSound = new SoundFile(this, "dropsound.mp3"); //player character movement sound effect CharacterMovementSoundEffect = new SoundFile(this, "cmovesound.mp3"); } //for updating game void updateGame() { //upodating frames count on screen gameFrame++; //updating grade letters on screen Character.UpdateLettersOnScreen(); //updating frames on screen UpdateFrames(); //updating gpa score updateScore(); //for updating letters on screen GradeLetters.UpdateLettersOnScreen(); //upodating frame speed marginCounter -= framespeed; } //for showing gpa score and key viewer on game screen void show() { //showing gpa score showScore(); //if player character is out of screen then returning to main menu screen if(Character.CharacterDropped) { mainScreen(); } //for rendering key viewer on screen keyViewer(); } // for restarting the game. void restart() { // the gpa score will be zero score = 0; //frame speed reset to 5 which means 5 frames will be rendered in one second framespeed = 2; //margin between player character and grade letters margin = screenWidth/2; marginCounter = margin; //initializing player character to its default position Character = new Character(230, height/2); // resetting frames fframes = new ArrayList<IntersectionWithLetter>(); fframes.add(new IntersectionWithLetter(screenWidth + 128, framespeed)); }