Concept
The code for the game can be found here.
The game that I wanted to create was a version of my favorite classic breakout game but actually without the bricks!
So how does the game work?
The game has 3 phases:
- The start screen – this screen shows the instructions on how to play the game as well as a button to start the game the first time user is playing. The instructions are fairly simple:
– Move the paddle with the A and D keys to left and right respectively.
– Don’t let the ball fall!
– Catching UFOs rapidly increases score while catching Xs rapidly decreases your score.
– Beware: hitting the paddle at the edges or Xs increases the speed of the ball!
– Overall, the further you go in the sky the higher the score you get.
– Compete with your friends to beat the highest score on your laptop!
2. The game phase – once the user clicks on “Start Game”, the ball flies up in the air and the game begins. The user can start moving the paddle using the A and D keys to save the ball and hit it at specific angles to prevent the ball from speeding up.
The score, UFOs collected and the highscore appears at the top of the game.
3. Game over phase – if the user fails to catch the ball, game is over. This screen displays the user’s final scores and gives and option to replay the game.
Implementation
There were a number of parts about the assignment that proved to be challenging for me.
Ball and paddle collisions
When the game begins the ball is assigned a random speed between 2 and 5. Initially, I tried to keep the variable to change the direction of the ball separate than the speed of the ball. However, this was becoming immensely messy and created further complications when I tried to include the collision with paddle. To cater to this problem, I used negative numbers for ball’s left speed and positive numbers for ball’s right speed and similarly for ball’s top and bottom speed as well. However, because of the number of times the ball’s speed changes throughout the game, it was even more difficult to manage this because it could lead to the ball’s speed becoming 0 in which case the ball would get stuck in the air. So the if conditions had to be used very carefully throughout the code to avoid such problems.
A very simple example of this issue is that when the game starts, I choose a random direction for the ball to start off. If I simply chose a random number between -5 and 5, the ball could start off extremely slow or not even start off if the speed is 0. Therefore, to avoid this problem I first generated a random number between 0 and 1. Then based on the result of this, I chose the speed of the ball. If the random number was between 0 and 0.5, I randomly choose the ball’s speed from -5 to -3 in the left direction. On the other hand, if the first random number is greater than 0.5 than I choose a random number again for the ball’s speed between 3 to 5 in the right direction.
Below is the code for this problem:
let PoNX = random(0, 1); let PoNY = random(0, 1); if (PoNX < 0.5) { ballSpeedX = random(-5, -3); } else { ballSpeedX = random(3, 5); } ballSpeedY = random(-5, -3);
The Y direction remains the same in the top direction to allow the user some time to adjust to the game screen. Though this problem is a very short one, it required navigating a lot of error statements to finally reach this conclusion. This is a very small example but I ran into similar problems throughout my code for when all ball collisions, with the wall, paddle, UFO and cross.
In particular, detecting the collisions with the corners of the paddle or from the sides of the paddle was the most difficult challenge costing me hours of hard work which I still doubt if it has been resolved fully. Adding on to the challenge, I had to play the game for a couple of minutes before I could bring the paddle in the exact position of contact with the ball which was causing an issue to be able to detect and test the problem, which made this challenge even more time consuming.
Mapping the paddle speed
Similarly, because of the above stated reasons, to come to a fully working code for mapping the paddle speed was a tough challenge. Below is the code for this:
//if ball hits the paddle else if (ballY >= 465 && ballX >= paddleX + 5 && ballX <= paddleX + 95) { ballSpeedY *= -1; bounces += 1; spaceBounce.play(); ballY -= 3; //map ball speed at left of paddle if (ballX >= paddleX && ballX < paddleX + 50) { newSpeed = map(ballX, paddleX + 49, paddleX, 2, 5); if (ballSpeedX < 0) { ballSpeedX = -newSpeed; } if (ballSpeedX > 0) { ballSpeedX = newSpeed; } console.log(ballSpeedX); } //map ball speed at right of paddle else if (ballX >= paddleX + 50 && ballX < paddleX + 100) { newSpeed = map(ballX, paddleX + 100, paddleX + 50, 2, 5); if (ballSpeedX < 0) { ballSpeedX = -newSpeed; } if (ballSpeedX > 0) { ballSpeedX = newSpeed; } console.log(ballSpeedX); } }
Scrolling background
To give the idea to the user of a moving background, so they feel the are going upwards not only adds greater user engagement in the game, but also makes it slightly more difficult to judge the speed of the ball. To make it more difficult for the user as the game progresses, every time the user score increases by 500, the background speed increases to make it more challenging for the user and make thes feel as if the game is speeding up even though it’s not.
The following is the code for this effect:
//increase background speed if (score % 500 == 0){ bgSpeed += 0.5 } //starry background image(artwork, 0, bgY); image(artwork, 0, bgY2); //scroll background bgY += bgSpeed; bgY2 += bgSpeed; if (bgY >= 1000) { bgY = bgY2 - 1000; } if (bgY2 >= 1000) { bgY2 = bgY - 1000; }
Saving high score in local storage
To allow the users to compete with each other and prevent the game from only being a one-off game, every time a user plays the game, the score gets saved in the local storage of the browser. So regardless of the screen being closed or refreshed, the score is saved and pulled from the local storage for each game. This feature motivates the users to compete with each other and beat the highest score.
If time allowed, I could have improved this feature further by storing the high score in a file and then reading from the file each time so regardless of the high score being limited to the browser or laptop that the game is being played on, it would be more accessible to everyone who plays the game.
Changing ball & border colors
Finally, for aesthetic purposes and to prevent the game from becoming boring, I set the border and the ball colors to continuously change over the course of the game. This not only adds color to the game but also make it a little challenging for the user to easily keep track of the ball. This aspect of the game was also particularly challenging to write so I browsed some online sources to write the code below:
// cycle ball colors r += rs; g += gs; b += bs; //ball color cycle if (r > 255 || r < 50) { rs *= -1; } if (g > 255 || b < 50) { gs *= -1; } if (b > 255 || b < 50) { bs *= -1; }
Overall, despite all the challenges, I thoroughly enjoyed creating the game, (even though by now I have played it hundreds of time and no longer love it as much as I used to), I learned multiple features of p5.js as well as some logical aspects of the coding which I did not initially consider myself either. The game stimulated me logically as well as creatively as I was continuously think of what new features I could implement which are not commonly found in such games to give my project a unique feel. Because of the different features such as the speeds of the ball and the background, changing colors, UFOs, crosses, and high scores, I believe I was successful in giving this project a new and unique feel which I am very proud of!