Javascript Pong!

***DISCLAIMER : ***
As per previous projects I appreciate that the code will contain many errors and shouldn’t be followed stylistically. Hopefully my code will get better with experience but if you have any comments to help me with my learning I would love to hear them!

******************

This was a really fun challenge. It had it’s ups and downs but we were able to get a working version of the 70s arcade classic Pong coded in Javascript. 

If you’ve not read some of my earlier posts (here, here or here), I’m in my second week as a graduate software engineer. This week has led us to switch from Ruby to Javascript, a language that I’m much more comfortable with.

This project actually started off as trying to recreate a bouncing screensaver icon, knocking off the walls of your browser window and changing colour. It then evolved into recreating the Pong game which was a lot of fun to do. I’ve wrote down some notes of our experience coding this up.

If you’d like to download the files for yourself, please head over to my GitHub page and clone the repo.

HTML Canvas

I’d never used the HTML canvas element before. I’d seen some amazing things on codepen.io, including this fantastic effort which looks so realistic!

I’m not the most artistic or design oriented of people and thought I’d struggle with drawing items with canvas. However once I gained some experience I was much better at it than I thought. The shapes used are dependent on providing x/y axes and the project did need some mathematical thinking at times which worked in my favour.

Canvas required a couple of lines of code to be included from the outset:

After that it was pretty straight forward to get what we wanted. Below is some of the main code we used to set up our background and assets:

The way I looked at this was each item was a new layer on top of the last. By inputting some simple parameters (eg height, width, radius, x/y co-ordinates) we were able to create the required shapes for our game.

Later on, thinking about canvas as layers helped us to populate the scores and winners message at the end. Technically once the game is finished, the paddles and ball are still present, but they’re hidden by another black layer and some writing. Not stopping the ball led to some bugs we had to iron out, which I’ll discuss further a bit later on.

Moving the ball

We created a ball object and gave it x/y co-ordinates as values. We needed to make the ball move and so gave it a dx & dy value – the rate of change in x/y axes. We set the equal to +2 initially, which meant the ball could move from left to right. But we needed to implement this rate of change:  

The top line above checks whether the ball is within the border of the game. It does this by considering the ball’s x value (situated in the middle of the circle), adds on its’ radius so that we get the right hand side of the circle and compares that to the border’s edge. A similar calculation was done for the left hand side as well.

If the ball is within the boundaries, its x co-ordinate would update by 2 each time (‘+=ball.dx‘). Similarly this was repeated underneath for the y position. This code enabled our ball to move in all directions, by changing the ball’s co-ordinates.

The section at the bottom says that if the ball hits the top or bottom border then we need to reverse the direction of the ball. This is down by reversing the dy value from positive to negative, and vice versa. Note, this wasn’t needed when we hit the left/right borders as if that happens then someone has scored and the ball wouldn’t be returned.

This took some iterations to get too. At first we had a constantly bouncing ball, and later we included interaction with a paddle. But next…

Moving the paddles

We decided to use the ‘w’ and ‘s’ keys to move the left hand paddle up/down and the arrow buttons to move the right paddle. To do this we needed to include an event listener which listened for all the key strokes:

We created a function that translated a keystroke and deciphered its purpose. It then set a flag to ‘true‘ if it had been triggered. This then got used to move the paddle, as seen below (taking into account the paddle hadn’t hit an edge):

This was good, but we also needed to add a function to handle the release of a key, which set the flags (eg ”downpressed’) back to ‘false’. This way the paddles wouldn’t continue moving when a key was released.

So now our paddle moves and our ball moves, but next was the biggest issue:

Making the ball interact with the paddles

Eurgh…this caused me a headache. We got there in the end, but the code was pretty messy and it took us a while to get there!

Initially I thought about this problem as considering when the left/right side of the ball touched the right/left paddle. It was at point of contact. When we coded this up, it didn’t allow for what would happen on the corners, and I felt this was the wrong way to go about it. If the ball’s furthest part missed the paddle, but an edge later did, this should be returned (I think!)

In the end we thought of it a different way. By considering the radius of the ball. 

This was the biggest issue and so demands the biggest picture!

The ball’s radius would need to intersect the x-axis at the paddles edge +/- the ball radius (depending if you’re hitting the left/right paddle). We put in a test at this point (see the top line). At that point we considered whether the ball would hit the paddle, by considering whether the ball’s y-position was within the range of the paddle’s. But again, we needed to add on the balls radius for this, otherwise we wouldn’t be considering the edge cases.

If those conditions are met, the ball is returned, else the ball would continue to the boundary and a goal is scored.

When we first considered this we hadn’t taken out the left/right boundaries so the ball would rebound. When it did, the x-axis intercept (top line) would be triggered again. This meant that a ball could ping left/right constantly through the height of the paddle (an example of which is shown in my sketch below). That caused more issues than it should have….

We had another big issue when calculating the point of impact. The right hand test didn’t ever hit and I was baffled why. I put in a console log to get the difference between the ball’s x position and the intercept. If it hit 0, our code should run. But the console logs brought up two interesting bugs:

  1. Our speed was actually double what we anticipated. This was due to some erroneous code that actually called ‘+=ball.dx’ again. We removed this duplicate which sorted one problem out.
  2. The console log never actually hit 0. As the differences were logged, we had a point where we’d have:
    – differences: [6, 2, -2, 2, 6]….

…our co-ordinates were off. 

The left paddle had a 10px buffer, then a 10px width, meaning it’s right hand side started at px 20. But we allowed for the radius in our intercept, so this actually would start at px 30. Similarly for the right hand paddle, it’s left hand side started at px 770. Therefore there was a 740px difference between the two points. This is easily divisible when we used a ball speed of say 2 or 4. But initially our ball speed was randomly set as 3. Using this value, 740/3 = 246.67, meaning that it would never reach the paddle exactly which is what we were testing for. 

This issue led to a lot of sketches!

In the end we changed the ball speed and the initial placement of the ball so that it had a chance to marry up to both the left hand side and the right hand side. Disaster averted.

But now it’s working! Once we reached this point we included a scoreboard and end game sequence. But we also wanted something different.

Sound Effects

I was pretty chuffed with this feature. One of the group suggested it and found a neat bit of code on w3 schools. We copied the code over to our script and downloaded a free tennis serve sound effect. This worked really well and I’m glad we included it. We called the ‘mySound.play()’ function as a paddle was hit.

Other notes

As noted above, when we would place a blank screen over the game once a winner had been made. But the just papered over the cracks – the game was still running in the background. At first our conditional for hitting a score of 3 was a simple ‘==’, but this meant if someone scored in the background the blank screen would be removed and the game continued. Also if a paddle hit the ball the sound effect would alert! In the end I opted for changing the value of ‘ball.dx’ to be 0. This then stopped the ball and the game would effectively be over.

Conclusion

This was really fun! The paddles did cause some issues and brought up things we hadn’t considered from the outset. It’s been a long time since I’ve done anything with co-ordinates but it was good to be thinking about the problem mathematically. Once those issues were sorted it was a pretty straight forward project that I feel we handled quite well.

I’m looking forward to seeing what we have in store next!

If you’ve made it this far I really appreciate your time. If you have any questions or comments for me to help with my development I would love to hear them. Please feel free to leave a not or contact me directly.