“Pong is one of the earliest arcade video games; it is a tennis sports game featuring simple two-dimensional graphics.” - Wikipedia
How to make Pong in HTML5 canvas
Pong, at it’s core, is an extremely simple game. That’s why it’s a good one to begin with if you have just started learning game design basics. Of course you could start with many other games, but if you are looking for something relatively simple to build, Pong really is one of the simplest games ever made.
AFAIK, there are at least two ways of doing it: I personally call them the “simple way” and the “hard way”. I did both, but first let’s explore the simple one.
index.html is our single entry point to the game.
canvas.js contains the code for initializing the
canvas DOM object and the 2D context.
game.js contains the game objects. This file will be executed only once at the beginning, when the game loads.
keyboard.js has the keyboard bindings.
main.js is perhaps the most important file, because it contains the main game loop.
render.js does… the rendering. (you don’t say?)
reset.js is for resetting the game to the initial state, called every time a player wins.
update.js contains 90% of the game logic, and obviously is for updating the game state (before rendering).
The main loop
The main loop is at the core of our game. Maybe it’s hard to believe, but virtually every single videogame in the world lives and dies within a loop.
Implementing a game loop is a lot simpler than you think, but it’s not the focus of this tutorial. The resource I highly recommend for getting started is How to make a simple HTML5 Canvas game, by Matt Hackett. All my work is actually based on his tutorial. Read it, and you’ll get a basic understanding of the fundamentals of game development.
We want to focus on the game logic now, so for the time being let’s pretend our game loop looks like this:
Got it? :-)
ball object are
position represents the coordinates where the object is in the canvas space. Example:
In order to make it move, we should change its position, and we can do it through the speed. This is the heart of our game:
As you can imagine,
isGameStarted is just a boolean flag. But what’s
modifier? Well, it’s the delta time of our game loop. Put simply, the delta time is the time elapsed between a frame and another. This is very useful because we can use it to calculate how fast the ball should move. Without it, the game would just lag all the time.
The game logic is mainly about the ball: it should be able to bounce away from the paddles. How can you implement that? It’s pretty simple - have a look at the code below.
Can you see what’s going on in the code? Basically, if the ball goes beyond the canvas' left or right boundaries, all we do is increment the score and reset the game. If the ball touches the top or the bottom instead, we invert its speed on the Y axis. If you think about it, it’s all you need to make something reflect over a surface. So, in other words, if the speed is negative we make it positive, and viceversa.
What should happen when the ball touches one of the paddles? Fundamentally the same thing explained above: it should bounce away, reflecting on the paddle’s surface (and to do this we invert the Y speed). But how do we actually check if they are colliding?
The most common kind of collision detection is called AABB - Axis-Aligned Bounding Boxes. You can find plenty of resources around the Web explaining how this technique works, so I won’t talk about it (have a quick search for “AABB collision detection”, or just keep reading). As Linus Torvalds once said,
“Talk is cheap. Show me the code.”
Here we go:
The logic for the right paddle is exactly the same, but the speed on the X axis should be negative instead. In my case I also added a
randomize() function, so the game will be more interesting - you don’t have to implement it this way, but a bit of randomness never hurts in gaming!
We move the paddles with the keyboard. Keyboard controls can be handled simply by keeping track of which key is currently being pressed (watch for the
keydown events are the only two we need for handling the whole keyboard. So on
keydown we add the key; on
keyup we remove it. Simple.
p2, which can be interpreted as players too.
Here’s the code:
Rendering the objects in the canvas
render() function, in all its glory:
It’s probably worth mentioning that you can use
JSON.stringify() to debug your objects directly in the canvas, e.g.:
Resetting the game
We need to reset the game every time a player scores. The logic is very simple, we just need to provide default values for our objects. Example below.
This is the main logic of Pong. However, it’s not perfect, and it could be improved a lot in several ways… for example by implementing physics rules (or by using a physics engine, that has already done the job for us). We have just simulated the reflection of a ball on a surface, but it’s not realistic at all - let’s make it better.
The “hard way”
In a proper Pong game, you can usually control where the ball goes. It could have a steeper or shallower angle of reflection, based on where the ball landed. Should it land on one of the edges of the paddle, the collision should be inelastic. In case it lands exactly on the middle of the paddle, the collision should be totally elastic.
In order to implement physics rules in a game, you should have an understanding of basic vector math, trigonometry and - of course - physics. But don’t fear, you don’t have to know everything: just the basics. I personally didn’t know much about physics, but I learned it by reading about it.
Here are some useful resources on the Web:
- Math lessons: www.mathsisfun.com
- Physics lessons: www.physicsclassroom.com
- Vector math cheatsheet: higherorderfun.com/blog/2012/06/03/math-for-game-programmers-05-vector-cheat-sheet/
- Linear algebra for game developers: blog.wolfire.com/2009/07/linear-algebra-for-game-developers-part-1/
Let’s explore together the potential of 2D vectors.
Using 2D Vectors
The main thing you’ll have to understand is how vectors are used in game development. As an example, let’s go back to our
ball object and modify it to use vectors. It will look like this:
Four values at the price of two attributes! And this is a lot better now, not only because we are using less attributes, but because we can use vector math. Believe me, vectors simplify your game a lot.
You may have noticed that I didn’t use
speed, but I used
velocity instead. The reason is that
speed is a scalar quantity, while
velocity is a vector quantity. Put simply,
speed is an information that’s contained in
velocity! You may want to read about it, albeit not directly related to programming.
A proper ball reflection
This is how I’ve implemented it by using a vector library I found on the Web (find the source code on GitHub). Given a paddle’s normal, it will reflect any vector, but you have to make sure the paddle’s normal is a unit vector (in other words, it’s normalized).
Demo and source code
You can play the game here.
The full source code is on GitHub so you can clone it, fork it and even make your own from scratch, if you feel like it’s worth your time. If you are interested in the simple way, checkout the v1.0 release. The hard way is in the master branch.
As always, if you have any thoughts or questions, feel free to leave a comment below.