Here you are, the new and intrepid game developer just itching to make a game. You already know a programming language, and you’ve already looked at a few options in making games. You’ve decided that the best route is to not use an existing game engine, at least not yet, instead you’re going to do it the hard way.
Or; maybe you’ve already made a game in an engine, and you figure it’s time to learn how to do it yourself?
In these articles, I’m going to show you how to make Pong from scratch. Not completely from scratch, we’re going to use some libraries. Or maybe you could combine this article with some learning of your own to build your own libraries.
Let’s get started.
What do I need to make Pong?
Not a lot, but I can’t start you completely from zero knowledge.
- You need to know a programming language of some sort. A language that you know games can be made using. Hopefully the language you learned was one that you picked because you know you can make games with it. Some good candidates are C, C++, C#, Javascript, or Python.
- You need to find out what libraries you’ll need for your chosen language that will allow you to do the following (pretty much any library for game development):
- Create a window, or manage a client area for your game
- Draw shapes to the client area
- Detect keyboard and mouse input
- Play sounds and music
- You’ll need to read a little bit to learn your chosen library. You don’t need to have made a game with it, but you’ll need to know how to use it, or at least be familiar enough with the reference material to look it up.
- Time and some creativity
You’ve got all that. If you need some help choosing a library for your programming language, I’d highly recommend SFML, it has bindings for a whole bunch of languages, as well as being able to target iOS and Android if you want it to.
How is this going to work?
I’m not going to get into any code, at least, not right now. I want this tutorial to be adaptable to any language and library choice. Later on, I’ll put some code examples around everything here, and unless I decide to spend a lot of time making the Ultimate Guide to Programming Pong, it’ll be on you to adapt my code to your language and library choice.
But trust me, it’s not that hard to do.
What I will be doing is taking you through the architecture I’ve chosen to make Pong. Be warned, this is an Object Oriented Programming structure, so if you’re not using an OOP language, you might find this pretty hard. Fortunately, every language recommended for game programming (one of the languages I’m sure you know), supports OOP.
I will also run through the general gist of how the architecture will work so that you’re not left there hanging, trying to understand how to glue it all together. I’d really recommend that you try to build Pong with just this first article, before moving onto the second one and seeing how it’s all done in code, it would really help you to hone your craft.
Why make Pong?
Umm, because it’s fun? Duh.
Well, that, and you already know how Pong works, right? That’s the brilliance of these old games, learning to make them instead of your own original idea removes the need for thinking about gameplay, which automatically increases the likelihood of you getting to the end goal of making a game.
If you’re stuck there focusing on how the game should play, you’ll never get past the how the hell do you make a game. After you’ve made a few of these old classics, then you can worry about designing your own game, of course, starting with a Game Design Document.
Now with all of that out of the way, let’s get down to brass tacks.
The Architecture of Pong
In the grand scheme of things, Pong isn’t too complicated, neither playing it or architecturing it. Here’s a UML diagram that covers how the classes within our game code are going to work. If you don’t know UML, don’t worry too much, I’ll explain as I go (I’ll assume you don’t know what it is for the most part).
UML is one of those great tools for the developer. It’s a way to model things in a consistent manner that has thought about all the different little bits and bobs so you don’t need to. You can learn UML here if you want. For what it’s worth, after my Game Design Document, it’s the first stop on my way to making a game.
(Open it up in a new window and you can zoom in, it’s an SVG file)
For those not familiar with UML, each of those boxes is a class that you’re going to need to code. Each of the lines is a relationship between those two classes. The end of the relationship with a shape, mostly a filled diamond, is the class that contains the class being pointed to by the arrow. A filled diamond in UML lingo means that the containing class (the one with the diamond) contains an instance of that class that is destroyed when the class is destroyed.
The relationships without a diamond indicate that the containing class just references the pointed-to class. Most likely it’s a parameter to a class method, or it’s a local variable in one of the methods.
There is one more relationship there, the crosshair looking one. That just indicates that the class is a sub-class, meaning that the entire class is defined within the other one (could be public, protected, or private as any other member).
I should also mention that this is not the most optimal architecture in the sense of speed. There is a reason for that. The reason is that this is optimal to learn from. And let’s be honest, there isn’t a device on the planet that can’t handle running Pong, we’re not trying for the latest Battlefield game here.
So now; let’s go through each of these and put some explanation around them.
GAME_STATE
This is a enumeration, not really a class. The text between the <<
and >>
in UML is a stereotype. I won’t go into detail on what that is, but you can read more on stereotypes here.
There are three possible values, MENU
, IN_GAME
, and EXIT
. These should be self explanatory.
The way we use this GAME_STATE
enumeration is for the main loop to determine whether the program is currently displaying the games Menu, whether we’re currently in the middle of a rousing game of Pong, and finally whether the user wants to exit the program.
Wait, what? “Main Loop”?
In game-programming there’s always a main loop. It’s basically a loop that we continue forever until the game is ready to exit back to the operating system. In Win32 programming, this is the good ol’ Message Pump. We’ll see it later, but basically it will run forever, and based on the value of a GAME_STATE
variable, will either draw the main menu, or draw the game in play.
PLAY_STATE
This one is very similar to the GAME_STATE
. This time though, while our main loop needs to keep track of the state of the entire game-executable, the game part of the executable needs to keep track of the game.
We’ve got 4 possible states of play here.
SERVE_PLAYER_ONE
and SERVE_PLAYER_TWO
indicate that the respective player is in the process of serving the ball. This means that the ball will stick to their paddle until they press the serve key. TOWARD_PLAYER_ONE
and TOWARD_PLAYER_TWO
are the PLAY_STATE
s used when the ball is in flight.
If Player One serves, the PLAY_STATE
variable will have a value of SERVE_PLAYER_ONE
. As soon as Player One serves the ball, the state will change to TOWARD_PLAYER_TWO
. If Player Two hits the ball back, the state will change to TOWARD_PLAYER_ONE
, but if they miss it will change to SERVE_PLAYER_TWO
(and Player One will also get a point).
MOUSE_STATE
Let’s just bring up the final enumeration now. The MOUSE_STATE
is going to be used to pass to the input handlers for the Button
class we’ll get to later. Basically, the mouse can have it’s button UP
or DOWN
. You might be wondering about left, right, middle buttons, but we don’t care for Pong, and mouse click will do.
Vector2D
You know what a vector is, right? If you don’t, just consider it one of two different things. It is a point in euclidean space, meaning in our two-dimensional world, it is a single pixel in it. So, if our client area is 100×100 pixels, a point in it could be position {10,90}
which would be close to the bottom-left corner.
It could also be a direction and magnitude. So the vector {1,0}
which would be strongly to the right, with no movement up or down. Or {-0.01,0.5}
which could be very slightly to the left, and strongly down (assuming the top-left of our client area is position {0,0}
with increase in X going to the right, and increase in Y going down.
Our Vector2D
class has two public attributes, an x
and a y
coordinate. Both are floating point values. We’ll use this to keep track of the position of our ball and it also comes in handy to pass the coordinates of the mouse cursor around.
Your library of choice may already have a vector class. If so, great, use it. We don’t need to reinvent the wheel right now.
RectangleShape
You can probably already guess what this is, huh? Much like how the Vector2D
class represented a point, the RectangleShape
represents, yep, a rectangle.
Just four public attributes, x
, y
, width
, and height
.
It’s used for a few different things. A Paddle
in the game is basically just a rectangle that the players can move. The Court
that the ball bounces around is also a rectangle. Finally, the Button
s that we’ll show in the main menu also have a rectangle that denotes the area we can click in with the mouse.
Just like the Vector2D
your library may already have a rectangle shape class. Use it!
Court
This is a straightforward object in our game world because it’s… well it’s a rectangle. We could use the RectangleShape
to denote the bounds of the court, but I like to create a new class for a few reasons.
We might need to extend it later for one. Secondly, it’s a little more… obvious if we make it it’s own class. For now, it’s just got a RectangleShape
that we can retrieve when needed.
An instance of it is maintained by the PongGame
class. The GameRenderer
is also passed the Court
instance when it needs to draw the game to the client area.
Paddle
The players are represented by a Paddle
each in the game. These are almost as simple as the Court
, we just provide an ability to change the position.
It internally uses a RectangleShape
. The PongGame
contains two instances of it, one for Player One and Player Two. Much like the Court
, the GameRenderer
also needs an instance of it to draw the game to the client area.
Ball
Represents the ball in play. There’s a little bit more to this class than what we’ve seen so far.
A ball has a position in our game world (the Court
). It also has a radius, because it’s a ball.
Your game library of choice needs to be able to draw a circle. The circle will represent our ball. Usually, the method/function used to draw a circle takes on a form like:
void draw_circle(position, radius, color)
Code language: JavaScript (javascript)
And so we keep track of the balls radius. In all reality, for this version of Pong, we could just use a statically defined size of the ball. We’re not going to, because later on, when you want to add new features to the game, a great idea is the ability of the ball to change size, like it gets smaller and smaller as the game goes on or something like that.
The ball also has a velocity. The ball is in a constant state of movement after it’s been served, and the most obvious place to store this velocity is in the ball itself.
The class is rounded out with a few getters and setters.
PongGame
This is a real workhorse of a class, it handles quite a lot. It wouldn’t be a bad idea to try and reduce the scope of this class, but for the sake of learning, it’s not too bad.
It’s doing all of the following:
- Keeping track of the score
- Bouncing the ball around the court and off paddles
- Keeping track of the ball and paddles
It also has a couple of operations that are important.
Update
will update the position of the ball and perform all of the collision detection (the ball bouncing) and update scores if needed. Render
will draw the game to the client area, that’s another thing this big class is taking ownership of, well sort of, it has some help.
GameRenderer
Now we introduce a new stereotype. Static classes are ones that don’t contain any state, i.e. no values that each instance of the class keeps track of. We don’t need state for a class when it’s only job is to render to the client area.
There’s a constructor in this class, but that’s generally not something you have in most languages when declaring a static class. We’re just using it here to denote that at some point, this class needs to know the size of the client area (screen dimensions) that it’s drawing onto.
The one operation in this class draws the provided paddles and ball to the screen. Simple.
PongMenu
Now we’re done with the game part of the game, and on to the other stuff around it. Menu’s are important!
Every game starts with a menu screen / title screen, and this class is ours. It’s pretty simple, there’s a Button
to play the game, and a Button
to exit the game. Unlike PongGame
that uses the GameRenderer
to draw itself to the screen, the PongMenu
will draw itself.
It also has an Update
operation that it will use to get details on the mouse to pass to the buttons in case they’ve been clicked or hovered over. When the player wants to start a game, the Update
operation will return a IN_GAME
value, and when they want to exit it will return an EXIT
value. At all other times it will return MENU
.
Button
This should be educational! Look at the size of this class!
It is the biggest so far. The User Interface, in my experience, in the most code-heavy part of all my games and game engines. This is just a button, imagine if there’s Windows, Scrollbars, Inputs, Panes, Dropdowns, Checkboxes, Switches, Layouts, Alignments etc etc.
This is where that BUTTON_STATE
comes into play. Everything in this class should be obvious except maybe the m_callback
attribute which is a CallbackFunction
.
CallbackFunction
is a placeholder for us. In your language of choice, the particulars of this function-type closure will be different. Essentially, we are providing the Button
class a function that it should execute when the button has been clicked. In C++ we would pass a Lambda, in Javascript you would pass a function.
It’s really a big class, but it has another “class” in it, our last enumeration.
BUTTON_STATE
We’ve got three possible states that our Button
is in. Look at any button in any user interface, they can nearly always be either UP
, meaning it’s normal state; DOWN
meaning it’s currently clicked down; and finally HOVER
meaning that the mouse cursor is currently on top of it but it’s not clicked.
Main Loop
Alright, now that all of that’s out of the way, let’s go over how it’s all stitched together. Essentially, it all comes down to a single main function loop. The best way to show this is with some pseudo-code.
function main()
{
GAME_STATE state = MENU; // initial state is the game menu
PongMenu menu;
PongGame game;
Time previousTime = GetCurrentTime();
const float UPDATE_RATE = 33; // 33 milliseconds each update
float lag = 0;
while(state != EXIT)
{
Time currentTime = GetCurrentTime();
float elapsedTime = currentTime - previousTime;
previousTime = currentTime;
lag += elapsedTime;
while(lag >= UPDATE_RATE)
{
GAME_STATE newState = state;
if(state == MENU)
newState = menu.Update(UPDATE_RATE);
else if(state == IN_GAME)
newState = game.Update(UPDATE_RATE);
if(newState != state)
{
state = newState;
if(newState == IN_GAME)
game = new PongGame(2);
break; // if the state changes, we don't want to update again
}
lag -= UPDATE_RATE;
}
if(state == MENU)
menu.Render(elapsedTime);
else if(state == IN_GAME)
game.Render(elapsedTime);
else if(state == EXIT)
break;
}
}
Code language: PHP (php)
This uses a fixed timestep of 33 milliseconds to update the game. That means the position of the ball will only be updated once every 33 milliseconds. The game will be rendered as fast as possible, but only updated occassionally. This is good. It’s common practice and 100% the right way to do it.
If you instead updated the game constantly, the ball would move faster on a faster computer. Sure, you could divide the amount of change by the number of milliseconds that have elapsed since the last update, and make the velocity be a “pixels per second” value or something, but that just complicates things and when rounding errors are introduced, it just doesn’t work anyway.
Hopefully this makes sense, but if not, here’s another diagram to help you step through the logic of what’s happening.
This is another kind of UML diagram called a sequence diagram. The gist of it is you see messages passed between systems over time, time going down from the top to the bottom.
It’s probably not that well-suited for explaining things like game-loops, but it’s fairly easy to understand so will do for our purposes right now.
On the left-most lifeline we have the main loop. It’s split into a couple of “segments”, the first being when we’re in MENU
state, the second when we’re in IN_GAME
state, and the final (unfinished) segment is the MENU
state again.
Each of these states first send an Initialise message to the respective class from our architecture. This may or may not actually be needed in our final code, and we may only initialise once, not every time we’re in that state, but it serves to illustrate to us when we need to do. Subsequently, we’re sending two more messages, an Update message and a render message.
The Update messages have a corresponding response message (the return type of the Update
operation) which provides us with a new GAME_STATE
. These changes in state are what progresses us down the segments in the Main Loop lifeline.
Update Operations
The rendering and other operations should be pretty clear in what they do, it’s in their name. In the next article we’ll start putting together some code for these (so if you’re a learn by doing sort of person you’ll pick it up then). For now, let’s discuss the Update
(and related) operations.
The Update
operation in PongMenu
is going to do the following:
- Get the position of the mouse in terms of the client area. I.e. if the mouse is at screen position
{201,23}
but that happens to be the very upper-left corner of our client area, then we will have a mouse position of{0,0}
. - Get the state of the mouse buttons, i.e., are any of them down?
- Pass that info onto both of our
Button
sHandleInput
methods. - If the
m_shouldExit
attribute istrue
returnEXIT
- If the
m_shouldStart
attribute istrue
returnIN_GAME
- Otherwise return
MENU
The Button
s HandleInput
operation uses the provided mouse coordinates and button state to determine if the button is being hovered over, or is clicked, in which case it executes the m_callback
attribute.
The Update
operation in PongGame
does quite a lot:
- Will check for input from either player (we’ll talk keypresses in the next article) and move their paddle accordingly
- Will re-position the ball and set it’s velocity to zero if one of the players is serving the ball
- Will update the position of the ball based on it’s velocity if we’re in a toward play state
- Check for collision against the court or paddles and either update the score or ball velocity if needed
- If a player score has reached the max it will return
MENU
, otherwise it will returnIN_GAME
(there’s no way to immediately exit the program when we’re playing.
That’s really all there is to it.
Before moving onto the next article, where I’ll start giving code for these classes, I think it’s important that you have a go at this yourself. I’m fairly certain that all the info you need is in this article, the real difficulty you might face, will be in using your chosen game library and programming language.
And that’s a good thing. If you’re not struggling with the Pong aspects of making Pong, you’ve got a much greater chance of completing this.
If you make Pong you will have gotten past the first major hurdle every would-be developer faces, completing their first game. 90% of people do not get that far. You can get it done. If you need help or have questions, leave a comment or contact me through the website.