Programming is one of those activities that span a wide range of industries and professions, and one of the more interesting areas out there is game development. At first it can seem quite complicated when you look at all of the high-end, blockbuster gaming titles on the market today, and when you’re developing your programming skill it can seem quite intimidating trying to get started.
It doesn’t have to be complicated, however. If you’ve ever been interested in making games but have no idea where to start, it’s best to set a small goal that you can achieve relatively easily, which you can build upon later. A good example of this is trying to re-create Pong. When it comes to game development, Pong is widely considered to be the quintessential “Hello, world” application, given the simplicity of its mechanics. But even as simple as it looks, there might be more to it than meets the eye. Still, it’s not so much that it’s overwhelming, which makes it a great way to get started with game development.
This tutorial will focus a little more on the concepts you need to know as opposed to the programming language used. While it’s assumed that you know at least the basics of programming, we’ll be focusing more on the general structure of a game like Pong and how it can translate into code. For the sake of code examples, and because it’s easier to approach, we’ll be using the LÖVE framework. LÖVE uses the Lua scripting language in order to make game development very easy and straightforward, and works on Windows, Mac, and Linux. You can download it for free at love2d.org if you want to follow along. You can also find a handy wiki there as well as other tutorials.
First, we should do a quick review of what makes Pong the game that it is. On a high level it’s fairly simple: there are two players, one on each side of the screen, and they each control a paddle that can move up or down. In between the two paddles is a ball object that is in constant motion, bouncing along the top and bottom edges of the screen. The main objective of the game is for each player to block the ball with their paddle, and try to get it to bounce in such a way that causes the other player to miss. If the ball leaves the screen on the left or right side, the opposing player scores a point. The game ends when one of the players earns around 11 or more points (depending on what variant of Pong you’re playing).
The Finer Details
So we’ve established what Pong is on a high level. What does this mean in code? Well, first, let’s think about what goes on in a typical Pong game that we need to keep track of. We know that each player has a paddle, and can freely move up or down within the bounds of the screen, so we need to keep track of the screen position of each paddle. If you think back to your math classes, you might remember that when graphing 2D shapes, you use an X-axis and Y-axis to determine where individual points lie. In 2D computer graphics, it works much the same way; the X coordinate determines the horizontal position, and the Y coordinate determines the vertical position. You can freshen up your graphing skills in case you’re rusty.
An important note is that when working with 2D computer graphics, the Y-axis is typically reversed. This means that when Y is equal to zero, you’re at the top of the screen, and as you add more the Y coordinate, you start moving downward. The main reason for this is due to the fact that graphics data is stored top-to-bottom on a computer, as well as the fact that a computer monitor typically scans from top to bottom. Naturally, it would make sense in that case for an increasing Y to mean moving down on the screen.
So far, then, we have a total of four numbers to keep track of: the X and Y position of the left paddle, and the X and Y position of the right paddle. Of course, it wouldn’t be Pong without a ball to bump around, so we need to keep track of that as well. This would add another coordinate pair; an X and Y position for the ball, leaving us with six different numbers. In terms of code, this means that we’ll have at least six different variables, each holding a number.
Finally, we also need the score for each player, giving us two more numbers. This means that we’ll have a grand total of 8 variables in our code. It’s quickly getting more complex, but it’s not so bad when you know how it breaks down.
For now, let’s take a quick break from what comprises Pong and focus on how typical games are structured in terms of code.
Hopefully, you have at least some idea of what a loop does in programming. So we’ll start with the most essential piece of information: games are basically one giant nearly-infinite loop! They run the same pieces of code repeatedly for an indefinite amount of time, until the player decides to quit. But what happens during each run of this big loop? How do we know how much code to execute?
There are many answers to that question, but the most basic answer, and one that’s perfectly adequate for Pong, is that a game’s code will operate on a frame-by-frame basis. This means that the code, through one run of the loop, will do all of the operations necessary in order to draw one frame onto the screen. Then in the next run of the loop, it’ll start calculating the next frame, and so on. If we have a game that runs at 60 frames per second, this means that the loop (at least in this instance) should run 60 times for every second that passes. And in order to maintain a smooth 60 FPS, each run of the game loop should last no more than 1/60 of a second.
Fortunately, the LÖVE framework has a game loop already built-in. Let’s look at the structure of a typical LÖVE-based game.
function love.load() end function love.update(dt) end function love.draw() end
LÖVE automatically calls the love.load function above when the game first starts. Then, in its built-in game loop, it calls the love.update function, then the love.draw function afterwards.
While it’s likely that the game will run at about 60 frames per second on most computers, it’s possible for it to be higher or lower. This is why, in the LÖVE framework, there’s a “dt” parameter on the love.update function. This variable holds a number that represents the amount of seconds that have passed since the last frame. This allows your game logic to take into account when things aren’t running at 60 FPS, allowing it to run at the same apparent speed even if the graphics aren’t as smooth.
Since we already know that we need at least 8 variables for our Pong game, we can add them to the top of the code, before the love.load function. You don’t have to use these variable names; just make sure that they make sense.
paddle1_x = 20 paddle1_y = 300 paddle2_x = 580 paddle2_y = 300 ball_x = 400 ball_y = 300 score1 = 0 score2 = 0
From here, it’s a matter of adding code to your love.update function that will change and manipulate these variables, performing the calculations for the next frame to be drawn to the screen. For example, in order to get the paddles to move you would have to use the love.keyboard.isDown function. We want to move them up and down according to which arrow key is pressed.
if love.keyboard.isDown("up") then paddle1_y = paddle1_y - 2 elseif love.keyboard.isDown("down") then paddle1_y = paddle1_y + 2 end
As mentioned before, a lower Y value means that we’re moving up on the screen, while a higher Y value means that we’re moving down. And since this code will be run for every frame, as long as an up or down arrow key is held (which means the isDown function returns true), the paddle will continue to move.
Of course, making the paddles move is only part of the game logic we need. But for now, this is enough to get something on screen. After the game has run its logic in the love.update function, it then needs to draw to the screen using the newly calculated values. This is where love.draw comes in.
When using the LÖVE framework, drawing something to the screen is pretty straightforward. Typically we would draw some kind of sprite (2D graphic) for this purpose, but for the sake of simplicity we’ll draw using basic shapes such as rectangles for the paddles and a circle for the ball. To start off with, we’ll draw the paddles using the love.graphics.rectangle function.
function love.draw() love.graphics.rectangle("fill", paddle1_x, paddle1_y, 24, 64) love.graphics.rectangle("fill", paddle2_x, paddle2_y, 24, 64) end
The first parameter taken by the rectangle function is whether the rectangle should be filled with a color or simply be an outline. We want to use “fill” because we want a fully filled-in rectangle. Since we haven’t specified a color, it will default to white.
Next, the two parameters afterwards specify the location at which the rectangle will be drawn. Since we already have this position stored in the form of our four paddle variables, we can go ahead and use them here.
The last two parameters specify the width and height of the rectangle in pixels. We know the paddles will be taller than they are wide, so we give it a narrow width of 24 pixels and a taller height of 64 pixels.
If you run the game at this point (the details of which are discussed in the documentation for LÖVE), you should be able to move the left paddle up and down using your arrow keys.
The Rest of the Game
Obviously there’s quite a bit more to Pong than simply being able to move a paddle. Hopefully, though, you should now have an idea of how things work. It’s also good if you tinker around with it and deviate from these examples; after all, the best way to learn is to see for yourself how things work. You don’t need to worry about doing something wrong–the worst thing that happens is that your game shows an error message, which is most likely fixable.
To get going on the rest of the game, there are a few questions for you to ponder. We already know the position of the ball, but how can we keep track of how fast it’s moving, and in what direction? How do we integrate it into the game logic? Also, what happens when the ball touches a paddle? How do we check for that using the variables that we have? Under what conditions would we have to tell the ball to reverse direction? How do we know, using our existing variables, when the ball leaves the left or right sides of the screen? What happens to the score as well as the position of the ball after it leaves the screen?
You certainly don’t need to worry about this all at once. Take it one step at a time. Think about one of these questions and try researching and coding an answer. Keep building upon it until you have something that works.
While this tutorial gives you a rough guide as to how a game is put together, you have to do the deeper research and experimentation yourself. The wiki on the LÖVE website gives you all sorts of resources to choose from. If need be, feel free to brush up on your programming knowledge if you feel that any of this has gone over your head.
Most importantly of all, make sure that you have fun! While there is certainly a lot of work involved in making a game, the end result can be something quite satisfying and enjoyable.