Let’s Make Air Hockey!
As the title suggests we will be making a browser version of air hockey. If you don’t know what air hockey is then you are missing out on a great game and I beg you to go to the nearest arcade and bring $20. A short description of air hockey is a “2d” version of hockey where two players have a little puck like object with a small cylindrical handle called a striker representing a hockey stick and a thin puck that is the actual puck for the game. There are very small holes all over the board that blow enough air the push the puck up ever so slightly of the board giving it the effect of being on ice. There are also slits about 3 times the diameter of the puck but not as wide as the board at at each end representing the goal. Each player would wack the puck around and try to get it into the slits to score. It was usually timed for about 2–5 minutes. Air Hockey has been around since the 70’s and has only gotten more popular over time. It was my favorite arcade game as kid and I remember playing with my dad for hours, pinching my fingers against puck and the wall, the satisfying sound of a goal and going going nuts when I got one and getting frustrated when I got scored on.
We will be using HTML canvas and JavaScript to make this game and all you will need Lite-Server, a very basic understanding of the Canvas API and of the request Animation Frame API, all of which are linked. This might be our most difficult game so far since we need to have tight collision detection and some understanding of basic physics. The user should be able to play with their mouse and the computer player should respond based on the position of the puck. We will get started on the puck and the players striker for this round then we will continue to the computer player next week. Let’s get started on Air Hockey!
Step 1: Initial Setup
We will set up the directory create our files and open your editor of choice. Our HTML doc just needs the minimum markup with a script tag to our JavaScript file and a link to a CSS file. In our JS file, create the canvas element and append it to the body. We will set the width and height to fairly large. Since this isn’t a mobile game we can set our canvas to be around 700 x 500. We will also need to get the context of our canvas with a context id of ‘2d’ and set it to a simple variable. Most people set it to ctx, which is what I do. Finally we will set up our basic animation with a function called animate and the requestAnimationFrame API. This will make the animate function run in a loop roughly 60 time per second giving us a frame rate of 60fps. I’m going to draw a simple horizontal line through the center with a circle in the middle to give us a board affect and put this in a separate function that is called inside our animate function. Ideally we are going to base everything off of the width and height of our canvas so we can have some flexibility later in our game. We will also center the canvas with flexbox and take off the margin and padding for everything. I also added some color to the CSS file as well. Here is what I have.
Step 2: The Players Striker
Let’s start by creating our players striker that should follow the mouse cursor. The striker just needs to be a circle of about 5% of the canvas width. We will make a JavaScript class called Player that doesn't take any parameters for instantiation but will have instance variables of x and y. We will create a draw method that draws a circle around our mouse and instantiate a new player to put into our update function. Let’s also create a event listener that listens for a mouse move and updates the position of our instance for x and y. We will need to control for the fact that our mouse’ x and y position is relative and not to the canvas. I’m going to clear the board at the beginning of the animate function so we get a nice clean animation. Here is what I have
No we will need to calculate a the momentum of our striker but we can assume the mass of the puck and the striker are the same for simplicity meaning we just need to calculate it’s velocity. I do this by adding a couple more instance variables to the player class. We need ultimately need a dx and dy but we currently don’t have a way to calculate that. This is why we will need to save the previous x and y variables to a new variable and compare them to an updated ones which gives us our change in x or dx for short! This means we will need an update method as well. There is more than one way to do this but here is my solution. It’s really important to update this in a specific order or we will get wacky results.
Step 3: The Puck
This will be the most difficult aspect as this is where most of the game logic lies and all the collision detection. Let’s first start by creating a Puck that needs no class with a constructor that has a x, y, dx and dy. The draw function will be a circle that starts in the middle so start x as the width/2 and the height/2. Now we will make an update method that controls if it bounces of the walls. This will be perfectly elastic so just reverse the dx and dy.
For the collision detection we will need to use the Pythagorean theorem. We will calculate the difference between the centers and if that distance is less than their combined radii then we know they have hit each other. We will take the difference of their x positions and y positions, take the absolute value then plug those into the Pythagorean theorem as ‘a’ and ‘b’ to calculate ‘c’ and compare that value to we want to.
Now comes the difficult part of transferring the momentum from our striker to alter the dx and dy of the puck. I do this in a not so ideal way but and there is plenty of room for improvement, but for a simple game it will be fine. Since I’m assuming the mass for the puck and striker are the same, I just add the velocity of the striker to the velocity of the puck but if the velocity is zero then I reverse the velocity. Again there are much better ways to do it but this is my solution. There is an weird issue in a program however. Since the paddle only absorbs our momentum and our paddle has an infinite amount then the puck just goes faster and faster. I add friction to my game by slowly taking the dx and dy away at the end of every update function. This is really messy and it can always be better but for a first time game, it’s not so bad.
That should cover just the basics for this section. Now all we need to do is add the computer and some rules and we are golden! This will be all that we do today so read the part 2 next week!