Learn how to create a pong game with an Arduino and an LCD
If you’re a programmer with an interest in electronics, playing around with an Arduino Uno is a great way to learn.
We’ve had several tutorials on the Arduino on this site in our past, that cover topics such as using a button, creating a traffic light, and even making a temperature sensor.
If you’ve done those and are looking for more, we have a great tutorial for you!
In this tutorial you will create a fully functional 2-player Pong game, and learn about more advanced techniques like reading a data sheet, soldering, and more.
So, sit back, grab a nice refreshing beverage and get ready to build your very own console and Pong game! :]
Prerequisites
This tutorial will make use of the following topics:
- C programming language
- Electronic circuits
- Arduino platform (software and hardware)
- Soldering
Technically, you can follow this tutorial without any knowledge about these topics. Understanding any or all of these, however, will make things much easier for you, and help with building your own little console and pong game.
Note: Soldering irons get incredibly hot and can cause severe burns! Be very, very careful when you turn on your soldering iron and throughout the process. Make sure you are not around flammable materials or working on a wooden surface. Finally, when you finish soldering, let the iron cool down before storing it away.
Materials required
Since this tutorial will focus not only on writing the game for Arduino but also on building your own little console, you will need some materials:
Some of the materials required come with the Sparkfun Inventor’s Kit, at $99.95. The kit includes the potentiometer, jumper wires, 1 breadboard, USB cable, 2 of the 4 buttons you will need, and many other cool pieces of hardware that will allow you to make other projects and continue learning with the included tutorials and booklet. The full list of items you need to complete this tutorial are as follows:
- Arduino Uno R3 (Quantity: 1) at $29.95 [included in Sparkfun Inventor's Kit]. This is the core piece of your console/circuit. It’s the brains of your project and what you will connect everything to.
- 128×64 Graphic LCD (Quantity: 1) at $19.95. This is the screen you will use to draw everything and to be able to visualize your game. This tutorial uses a display based on the JHD12864E model, but the library you’ll be using will support several models from many different brands, including the KS0108 and JHD12864H models.
- 30-row breadboard (Quantity: 2) at $4.95 each [one is included in Sparkfun Inventor's Kit]. The breadboards are where you connect each component to interact together; You will be connecting the LCD for displaying the game, your buttons for user input, the potentiometer for controlling the LCD’s contrast, and more. Here’s what the breadboard looks like:
- Male-to-male jumper wire 30-pack (Quantity: 2) at $4.95 [one is included in Sparkfun Inventor's Kit]. Jumper wires are what you will use to connect things between your breadboards and the Arduino pins.
- Jumper wire kit (Quantity: 1) at $6.95. These smaller, fixed-size jumper wires are excellent for jumps within your breadboards without having cables clutter everything. They are also colored so it helps to determine what each one is doing.
- Break away headers – machine pin (Quantity: 1) at $2.95. The break away pins will be soldered into the LCD to properly connect the LCD to a breadboard.
- 10K potentiometer (Quantity: 1) at $0.95 [included in Sparkfun Inventor's Kit]. The potentiometer will control the contrast of the LCD.
- Push button (Quantity: 4) at $0.35 each [two included in Sparkfun Inventor's Kit]. The push buttons will be used for user input, as up/down buttons for each player’s pong paddle.
- USB cable A to B (Quantity: 1) at $3.95 [included in Sparkfun Inventor's Kit]. This is the cable you use to connect the Arduino board to your computer in order to upload sketches and provide it with power.
- Soldering iron (Quantity: 1) at $9.95. You will use the soldering iron to solder the break away header pins to the LCD holes. This will give you good, solid connection points out of the LCD and will also allow for easy connections to your breadboard.
- Lead-free solder wire rosin core spool (Quantity: 1) at $7.95. Solder is a metal combination that very easily melts under heat and is fantastic for soldering together components. The rosin core is used to keep the connections clean during the soldering process and facilitates the bond between the metals and the solder. Read more about rosin core here.
- Digital multimeter (Optional) (Quantity: 1) at $14.95. A multimeter will help you measure DC voltage, resistance, and more.
In total, the cost of all the materials (assuming you buy all of them from scratch) is approximately $104 without a multimeter and $119 with one. If you buy things with the Sparkfun Inventor’s Kit, it will cost about $54 extra ($158 without / $173 with the multimeter).
This may seem rather pricey, but remember you can use these materials more than once. Apart from the header pins (only 20 of them) that you will solder to the LCD, or the solder spool, everything is going to be useful for many, if not all of your projects.
Setting up your dev environment
In order to work with Arduino and upload your sketches (the name of a program in Arduino), go to the Arduino downloads page and get the latest version of the IDE for your operating system. If you are using an Arduino Due or Yún then you will need to download the latest beta version 1.5 or above.
Once downloaded, put the Arduino app in your Applications folder and run it. You may be prompted to install Java, go ahead and do so as it’s required to run the Arduino IDE.
Having run the IDE at least once, you will have a new folder in your user Documents directory named Arduino:
Writing your own code to draw to the LCD would be very time consuming. When you write an Android or iOS app you don’t code everything from scratch; you get APIs for data structures, drawing to the screen, sound, and more. Similarly, Arduino has a vast array of libraries (first and third party) that you can use for your projects.
A library called GLCD, which you can acquire here, provides you with easy-to-use functions to draw to many models and types of LCD displays.
Go ahead and download the source for GLCD and locate the folder. Drag the newly downloaded folder for GLCD and put it inside ~/Documents/Arduino/libraries. This is how you install additional libraries for Arduino:
Note: I made the GLCD folder in my Arduino libraries an all uppercase name. This is just so it shows up neatly underneath the Sketch menu option. You can use whichever naming convention you prefer.
Relaunch the Arduino IDE and in the menu bar go to Sketch/Import Library…, at the very bottom (underneath the “Contributed” header) you will see GLCD listed:
With that, your development environment is ready. Yaaaay! :]
But before you can do anything you have to solder some header pins to the LCD and connect everything to the Arduino.
Soldering the LCD
Soldering is not that difficult, and once you start it’s really quite addicting and fun :]
As mentioned previously though, it’s quite dangerous and a soldering iron can give you severe burns if you are not careful. Because soldering is not something that can be easily taught in pictures (specially trying to soldering something while taking a photo with a cell phone), here’s a cool YouTube video that will show you how to do it:
Note: if you can’t see the video above then it’s probably because it requires Flash installed. If you don’t like to have Flash installed, use the Chrome browser to view the video by visiting
this link.
Soldering requires practice and is not something that can be mastered in just a couple of minutes or by watching a tutorial. Try practicing by soldering together some jumper wires, the inner wires of an ethernet cable, LEDs, resistors, or other simple items like header pins.
Once you are comfortable with using a soldering iron, break off 20 male-to-male header pins:
Take the smaller or thinner end of the header pins depending on the type you have and put them through the LCD’s holes from the bottom. Next, solder the header pins to the LCD:
As you can see, these are not the solder joints of an expert :]. Once you’re done you will probably feel like this:
Don’t worry, practice makes perfect. The important things to do here are to be very careful avoiding getting burns or inhaling any of the fumes from the solder, and to make sure each pin is connected to the corresponding hole of the LCD and only to one hole. If not, you will have a short circuit in the LCD and things will not work correctly.
Connecting the LCD
Now that you have the LCD soldered to the header pins, it’s time to start connecting it to your Arduino board. Before getting started it’s a good idea to find the data sheet for your LCD’s model. In my case, and assuming you are using an LCD based on the 12864E model, you will find a document like the one found here.
Don’t worry if this document seems a bit daunting. You only have to understand what each pin corresponds to, how to connect it to the Arduino Uno, and some basic information like the electrical characteristics. How about you start with the latter:
The table above shows the parameter for the LCD, its symbol, the unit it uses, and the minimum, average and maximum values for each particular parameter. It’s very good to pay attention to this as you don’t want to damage any of your equipment. If you were to supply the LCD with a value that exceeds the maximum for a given parameter you will likely permanently damage it.
For now, you don’t have to understand every single value shown here (or everything in the data sheet for that matter), but do take this into account for future projects that you build on your own. If there is something that you are not sure about you can always search for an answer online, or ask in the Arduino (or other) forums.
Now take a look at the LCD’s dimensions section:
This is shown as if looking at the LCD from the top. The right-most pin is pin number 1, and the left-most pin is pin number 20 for the 12864E model (reversed for example on the 12864H model). This is of importance when looking at the pin values and connecting them to the Arduino board. Here are the LCD’s interface pin connections for the 12864E:
The LCD is divided into two, 64×64 sections, this is why there are pins for chip select (to draw on the left side of the screen or the right). There are also data pins to control the data that is drawn to the screen, pins for the LCD’s backlight, contrast, ground and voltage input, reset, and a couple others.
But where does each pin for the LCD go into the Arduino board? For that, you need to take a look at the GLCD documentation, available here. Scroll down a little bit in the document and you’ll see a table similar to the one below:
The table lists the pins for several different Arduino boards, in your case you want to use the left-most column titled Arduino Pins unless you’re using a board different than the Arduino Uno. Moving on you will notice a column with comments where appropriate and three columns for Panels A, B, and C.
These columns are what you need to focus on for now. Depending on your display type and what the data sheet says, your panel may need to be connected differently.
As a challenge, try and find what type of panel connection you need to use for an LCD based on the 12864E model.
Solution Inside: Solution |
SelectShow> |
The panel type is B. You can see the highlighted row for panel type B. Compare your LCD’s data sheet with the panel types and try to find a connection that corresponds to your LCD. In the case of the J12864E LCD the chip select pins are number 15 and 16, which is what helped determine that panel type B is the connection you need to follow.
|
Once you’ve determined what panel type you need to use for your connection, scroll down a bit further in the GLCD documentation and look at the graph showing how to connect the LCD panel to the Arduino board. Here’s the graph for panel type B:
You’re now equipped with all the information you need to successfully connect the LCD. Grab a breadboard with at least 30 rows and connect the LCD as shown:
Notice how pin 1 in the LCD is connected to row 1 on the breadboard. This is not necessary but it will make it easier to know what pins you’re connecting the jumper wires to. Grab a pair of jumper wires and connect the positive rail on the breadboard (shown in red above) to the 5V output on the Arduino, and the negative rail (shown in blue above) to the Arduino’s ground.
For the rest of the connections, make sure to reference the GLCD panel chart from earlier to identify which pin on your LCD corresponds with the instruction listed below. LCD pins are referenced in bold and use the naming convention found in the Function column of the same chart:
On the other end of the breadboard’s positive and negative input rails, jump a couple of wires from the +5 volt row of your LCD to the +5V rail of the breadboard and from the Gnd row to the ground rail:
Connect the Reset row of the LCD to the Arduino’s Reset pin:
Connect the CSEL1 and CSEL2 rows to the A0 and A1 pins on the Arduino, respectively:
Now connect the Data lines (D0 – D7) as follows:
- D0 to pin 8
- D1 to pin 9
- D2 to pin 10
- D3 to pin 11
- D4 to pin 4
- D5 to pin 5
- D6 to pin 6
- D7 to pin 7
Connect a jumper wire from the Backlight Gnd row to the ground rail:
Now connect the R_W row to pin A2, D_I row to pin A3, and EN row to pin A4:
Adding the potentiometer
Doing good so far :] just a few more connections and you’ll be ready to test your LCD.
Time to step back for a minute as you’re about to connect a potentiometer (commonly referred to as ‘pot’) to control the LCD’s contrast. You’ll be needing a 10k potentiometer. Potentiometers can come in many different shapes so don’t worry if yours looks different. What’s important is the value since, depending on the resistance, you don’t want to send too much or too little voltage to the LCD’s contrast pin.
As a challenge, try to find what each pin on the potentiometer does and what they correspond to.
Solution Inside: Solution |
SelectShow> |
From left to right, the pins correspond to ground, input and output which you can get from the potentiometer documentation .
|
Here’s what the potentiometer looks like:
The yellow arrow indicates the corner with a ridge and circle on the potentiometer to show you what each pin does. As you can see there’s a pin you connect to ground, one for voltage input and one for the output voltage. If you have a different potentiometer, or want to learn more about them, you can find a cool guide on connecting potentiometers here.
Testing the potentiometer (Optional)
If you have a multimeter, it’s always good practice to test out your components by comparing their real world values to the values you expect. If you don’t have one, feel free to skip ahead to the section Connecting the potentiometer.
To measure the resistance of your potentiometer and to ensure that it’s 10k Ohms, take out your multimeter and set it to measure resistance. Make sure you set it to a value larger than 10k so you get an accurate reading. This is how you connect both legs of the multimeter to the potentiometer:
You connect the negative and positive pins of the multimeter to the in and out pins of the potentiometer, respectively. The Yellow arrow is there to indicate the order of the legs, as previously shown.
Turn the pot all the way down and get a measurement, then all the way up and get that measurement:
The left side shows the value with the pot turned all the way down, and the right side shows the value with the pot turned all the way up. Notice how the multimeter is set to measure up to 20K Ohms, which means that the 10.12 value means 10.12K Ohms (approximately).
Resistors and potentiometers have a percentage by which their resistance can vary. You can usually buy more expensive hardware to reduce the variation, if you so desire, but for this scenario a small change like 120 ohms will not affect your circuit.
Connecting the potentiometer
Having learned about potentiometers, how to measure their resistance value, and what each pin does, it’s time to put your 10K pot to use in the circuit. Go ahead and connect the potentiometer to the breadboard next to your LCD:
Connect a jumper wire from the negative rail of the breadboard (ground) to the ground pin of the pot:
Now connect a jumper wire from the input pin of the pot to the Contrast in row of the LCD, and one from the output pin of the pot to the Contrast out row:
This will let you control the contrast of your LCD’s display by simply turning the knob in the potentiometer. Pretty cool, huh?
Finishing the LCD’s connections
Finally, and to complete the connections to the LCD, connect a jumper wire from the 3.3V output on the Arduino Uno to the Backlight +5 row of the LCD.
Why are you connecting the LCD’s sidelight voltage input to 3.3 volts when the electrical characteristics previously stated that the typical voltage is 4.2V?
Well, the maximum voltage is 4.6V and even after all the connections you’ve made the LCD will probably be receiving close to 5V. A jump from 4.6V to 5V may not seem like much but it could damage your LCD in the long run. It’s always very important to pay attention to the data sheet and the maximum voltage, current, and power that a component can support.
If you didn’t do this, you would need to get a resistor to lower the voltage to 4.6V or lower, and you may not have one handy. By sending 3.3V to the LCD’s sidelight panel you not only avoid damaging your LCD, but you also avoid having to use a resistor. And the end result works well, your LCD has more than enough voltage to shine very bright should you want to play without any lights turned on :].
Testing the LCD connection to Arduino
Hurray! You’ve successfully connected the LCD to your Arduino board. It was quite a lengthy process but you have learned about the electrical characteristics of an electronic component, how potentiometers work, some nice tips about voltage and making sure your devices don’t get damaged, and more!
Having set everything up, it’s time to test your connection. Fortunately the GLCD library has some examples and templates that you can upload to your Arduino in order to test everything’s working correctly. Open the Arduino IDE and go to File\Examples\GLCD\GLCDdemo:
Now it’s time to upload the sketch. Connect the USB cable to your Arduino and to your computer. Make sure the correct model for your board is selected under Tools\Board and that the Serial port under Tools\Serial is using the USB port you connected your Arduino to. With that ready, go to File\Upload or hit Command+U.
Wait a few seconds for the sketch to upload and behold your awesome little console starting to take shape.
In OS X Mavericks (10.9), Apple seems to have changed the USB drivers and this may cause conflicts when uploading a sketch to Arduino. There’s a thread on the Arduino forums that proposes uninstalling and re-installing the previous drivers when running OS X Mavericks.
You may not like tampering with your default install of OS X or modifying any of its drivers or inner workings. If that’s the case, one solution which often works is to turn on verbose output during compilation and upload, by going to Arduino\Preferences, and hitting the Arduino Uno’s reset button right before the sketch begins to upload.
If you see a line with something like this: avrdude: stk500_recv(): programmer is not responding then uploading has failed, and what usually did the trick for me was unplugging the USB cable from the computer and attempting to upload using the “reset” trick again. It’s tedious, yes, but let’s hope Arduino has a solution for this soon.
Connections for user input
Yay! Yet another milestone on what will eventually become your own little gaming console :]
So now that you have the LCD working, you need to detect user input in order to be able to control the pong paddles. To do this you will use 4 push buttons (one pair for each player) and correctly detect collisions against the top or bottom of the screen.
Time to make some more connections. But first, take a look at the push buttons you’ll use:
There are many different types of push buttons. In this case the image above shows a push button with 4 pins, and there are orange circles around two pairs of pins. Each pair of legs are connected, so it doesn’t matter which one you connect your cables to. The important thing is making sure you are connecting a wire to one of the legs of each pair.
Above the button notice an image showing the state of the push button when it’s not pressed. When the button is pushed down you allow electrical flow from one end to the other. This is how you can detect whether a button is pushed down or not, and this is how you will know when to move a player’s paddle.
Take out another breadboard, preferably another 30-row board, and connect your push buttons along with some jumper wires as shown below:
Their purpose are as follows:
- Red – Simply pass the ground voltage from one side of the breadboard to the other in order to avoid tons of cables that would make it uncomfortable to play.
- Orange and Yellow – Connect the ground voltage to one set of legs on each of the push buttons.
- Gray – Will be connected to the input pins on the Arduino. The reason why this jump is made is so that players can comfortably push the buttons without any cables getting in their way.
Now, you may be wondering why you connect the push buttons to an input pin in the Arduino and then to ground, when the image above shows you that you need to pass electricity to the button in order to detect when it’s pressed.
If you are up for another challenge, try to find out why you do this on your own. Here’s a hint: It has to do with the digital and analog input pins in the Arduino. If you don’t want to wait then go ahead and click on the solution below.
Solution Inside: Solution |
SelectShow> |
The Arduino’s analog and digital input pins have a pull-up resistor.
|
If you have already read the solution but want to know more about pull-up resistors, here’s a little explanation. The Arduino’s input pins typically work by receiving voltage to indicate input, this is by default using a technique called pull-down resistor. What you usually do is connect a resistor between your circuit and the input pin in order to lower the received voltage and avoid damaging the board’s input pins.
In order to avoid having to connect resistors to your circuit you can use the Arduino’s built in resistors on each pin and, thus, use a pull-up resistor. Here is information regarding the digital pins, and here is information regarding the analog pins.
What this technique does is send out +5V of output on the pin you use as a pull-up, and connect the push button to ground. When the input pin detects a value of 0V (ground) then you determine that the button has been pushed down, if the pin reads a value of +5V then the button is not being pressed.
To learn more about pull-up resistors you can visit this link.
For this tutorial you will not use analog pins as true analog inputs, meaning you will not read analog values (the Arduino has 10 bits for each analog pin, allowing you to get integer values from 0 to 1023).
For player one connect a set of push buttons, each from the gray jumper, to pin A5 for the up button and pin 2 for the down button. For player two, connect the remaining pair of push buttons to pin 12 for the up button and pin 3 for the down button.
Connect one of the ground rails of your controller to the ground rail of your other bread board as shown in the second picture below.
This is the final circuit with all of the connections ready:
Drawing Player 1′s paddle
It’s finally time to write some code of your own and to get your pong game ready and working.
Note: if you like Sublime Text 2, you should try out
Stino, a plugin that lets you code and run Arduino sketches form Sublime Text 2. I personally found the code highlighting and auto-completion much better, but due to the problems with Arduino on OS X Mavericks I always had to upload sketches via the main IDE.
Start by opening the Arduino IDE (or Sublime Text if you prefer to use that) and go to Sketch\Import Library…\GLCD.
The following lines of code should be added at the top of your sketch:
#include <glcd.h>
#include <glcd_Buildinfo.h>
#include <glcd_Config.h> |
Next declare constants for the pins corresponding to the buttons for player 1, the screen size, and paddle size, position and movement speed.
// Screen Size
const byte width = 127;
const byte height = 63;
// Variables to store the pin number for each player's up/down buttons
const byte oneUpButton = 19; // Pin A5
const byte oneDownButton = 2;
// Size of the ball and paddle
const byte paddleHeight = 8;
// X position of each paddle
const byte onePaddleX = 10;
// Y position of each paddle
short onePaddleY = 28;
// Speed of the ball for the X and Y directions, and speed of the paddle movement vertically
short paddleSpeed = 2; |
Finally, create constants for the refresh interval (in milliseconds) and the last refresh interval, to setup a steady frame rate.
// Refresh interval at which to set our game loop
// To avoid having the game run at different speeds depending on hardware
const int refreshInterval = 60;
// Used to calculate the delta between loops for a steady frame-rate
unsigned long lastRefreshTime; |
Now come the default and required Arduino functions for each sketch, setup()
and loop()
:
/*
* Default Arduino setup function
*/
void setup()
{
// Player 1 buttons setup
pinMode(oneUpButton, INPUT);
digitalWrite(oneUpButton, HIGH); // Use built in pull-up resistor
pinMode(oneDownButton, INPUT);
digitalWrite(oneDownButton, HIGH); // Use built in pull-up resistor
// Initialize the library to draw dark pixels on a light background
GLCD.Init();
}
/*
* Default Arduino loop function
*/
void loop()
{
unsigned long now = millis();
if ((now - lastRefreshTime) > refreshInterval)
{
checkInput();
draw();
lastRefreshTime = now;
}
} |
setup
sets the pins for the paddles for player 1 to input. You also call digitalWrite
with a value of HIGH
for each pin to activate the pull-up resistors that you read about earlier causing each of the pins corresponding to player 1 to output +5V instead of the default 0V.
In addition, setup calls GLCD.Init()
to initialize the display in its default draw mode; dark pixels on a light background. You can read more about the functions of GLCD in the documentation found here.
loop
gets the milliseconds since the Arduino board began running and stores the value in a variable called now
. Then, a check is performed to see if the refresh interval (acquired by subtracting the current time against the last refresh time) is greater than the refresh interval you defined earlier. If so then you call a pair of functions to check for input, and draw to the screen.
You also set the value of lastRefreshTime
to now
, in order to correctly check the refresh interval the next time loop
gets called.
Time to write each of the functions called inside the loop’s if
statement:
/*
* Checks for user input
*/
void checkInput()
{
// Get the state of each push button corresponding to player 1
bool oneUpState = !digitalRead(oneUpButton);
bool oneDownState = !digitalRead(oneDownButton);
// Small delay to cleanup and improve the readings
delay(1);
if (oneDownState)
{
// Move player 1’s paddle down
onePaddleY += paddleSpeed;
// If the paddle is outside of the screen, move it back in
if (onePaddleY >= height - paddleHeight)
{
onePaddleY = height - paddleHeight;
}
}
else if (oneUpState)
{
// Move player 1’s paddle up
onePaddleY -= paddleSpeed;
// If the paddle is outside of the screen, move it back in
if (onePaddleY <= 0)
{
onePaddleY = 0;
}
}
}
void draw()
{
GLCD.ClearScreen();
GLCD.DrawVLine(onePaddleX, onePaddleY, paddleHeight);
} |
checkInput
gets a boolean value with the state of each button. You negate the value read because you are using a pull-up resistor, so when digitalRead
returns 0 then the button is being pushed down.
Afterwards there’s a small, 1 millisecond delay to cleanup the readings and then a check is made to see if the down button is being pushed. If so then you update the Y position of the paddle and check for collisions against the screen. The logic used here is pretty straightforward so you shouldn’t have any trouble understanding it. The same is done for when the up button is pushed.
draw
first clears the screen so there are no remaining pixels or artifacts from your previous frame and then calls DrawVLine
with the X and Y positions, and the height of the vertical line to draw. Notice inside both methods how you make good use of the constants previously declared at the top of the header file.
Go ahead and upload the sketch to your Arduino and push the up and down buttons.
Hurray! the paddle should be moving and correctly colliding against the top and bottom edges of the screen. Awesome job :]
Drawing Player 2′s paddle
Having looked at how to draw the paddle for player 1, check for input, and correctly detect for collisions against the screen, you are now ready to do the same for player 2.
As a challenge, try to do all of this without looking at the resulting code. Again, it shouldn’t be too difficult because almost all of the code for player 1 is reusable for player 2.
If you prefer to look at the solution or get stuck then go ahead and take a peek below.
Start by adding some constants for player 2 at the top of the file:
// Variables to store the pin number for each player's up/down buttons
const byte oneUpButton = 19; // Pin A5
const byte oneDownButton = 2;
const byte twoUpButton = 12; // For Player 2
const byte twoDownButton = 3; // For Player 2
// X position of each paddle
const byte onePaddleX = 10;
const byte twoPaddleX = 118; // For Player 2
// Y position of each paddle
short onePaddleY = 28;
short twoPaddleY = 28; // For Player 2 |
The bytes to store the pin number for player 2 correspond to the connections you made earlier for the buttons. The X position of the paddle for player 2 is all the way to the right with the same amount of spacing between the screen and the paddle. Finally, the initial Y position of the paddle for player 2 is the same as that of player 1.
Update draw
to include a call to draw the paddle for player 2:
void draw()
{
GLCD.ClearScreen();
GLCD.DrawVLine(onePaddleX, onePaddleY, paddleHeight);
GLCD.DrawVLine(twoPaddleX, twoPaddleY, paddleHeight); // For Player 2
} |
This is, again, passing the X and Y position of the paddle for player 2, as well as the height of the paddle. You need to setup the pins to check for input for the up and down buttons corresponding to player 2. Update setup
as follows:
void setup()
{
// Player 1 buttons setup
pinMode(oneUpButton, INPUT);
digitalWrite(oneUpButton, HIGH); // Use built in pull-up resistor
pinMode(oneDownButton, INPUT);
digitalWrite(oneDownButton, HIGH); // Use built in pull-up resistor
// Player 2 buttons setup
pinMode(twoUpButton, INPUT);
digitalWrite(twoUpButton, HIGH); // Use built in pull-up resistor
pinMode(twoDownButton, INPUT);
digitalWrite(twoDownButton, HIGH); // Use built in pull-up resistor
// Initialize the library to draw dark pixels on a light background
GLCD.Init();
} |
Once again you set the mode of each pin to INPUT
and then call digitalWrite
on each pin with a value of HIGH
, to active the pull-up resistor. Now update checkInput
as shown:
void checkInput()
{
// Get the state of each push button corresponding to player 1
bool oneUpState = !digitalRead(oneUpButton);
bool oneDownState = !digitalRead(oneDownButton);
// Get the state of each push button corresponding to player 2
bool twoUpState = !digitalRead(twoUpButton);
bool twoDownState = !digitalRead(twoDownButton);
// Small delay to cleanup and improve the readings
delay(1);
...
// Code to check collision for player 1's paddle
...
if (twoDownState)
{
twoPaddleY += paddleSpeed;
if (twoPaddleY >= height - paddleHeight)
{
twoPaddleY = height - paddleHeight;
}
}
else if (twoUpState)
{
twoPaddleY -= paddleSpeed;
if (twoPaddleY <= 0)
{
twoPaddleY = 0;
}
}
} |
You get the input for the up and down button for player two and then check for collisions against the top and bottom of the screen. The logic is the same as player 1 except tweaked to update the y position of player 2′s paddle.
Upload the sketch to your Arduino and push the up and down buttons for player 2.
Congrats :] you’re almost done, all that remains is the ball and its collisions.
Coding the ball and collision detection
To kick off the final portion of the tutorial, and to make the complete pong game with the ball and collisions, add the following constants and variables at the top of the header file:
// Size of the ball and paddle
const byte paddleHeight = 8;
const byte ballSize = 2; // Size of the ball
const byte ballPadding = 1; // Padding because GLCD adds an extra pixel to the size you set the ball to
// X and Y position of the ball
short ballX = 63;
short ballY = 31;
// Speed of the ball for the X and Y directions, and speed of the paddle movement vertically
short ballSpeedX = 3; // Speed of the ball in the horizontal direction
short ballSpeedY = 2; // Speed of the ball in the vertical direction
short paddleSpeed = 2; |
The variables you create correspond to the ball size and some padding required when working with GLCD, the X and Y position of the ball, and the X and Y speed of the ball. You want the ball to move at different speeds because the screen is not square, it’s wider so horizontally the ball should move quicker to make the game challenging.
From there, update draw
to include a call to draw the ball:
void draw()
{
GLCD.ClearScreen();
GLCD.DrawVLine(onePaddleX, onePaddleY, paddleHeight);
GLCD.DrawVLine(twoPaddleX, twoPaddleY, paddleHeight);
// Draw the ball by passing in the X and Y position, and the width and height
GLCD.FillRect(ballX, ballY, ballSize, ballSize);
} |
The last line is what you updated in the method. It draws a filled rectangle with the set X and Y position, and with the width and height (which in this case is the same so it’s a perfectly square ball).
Add a call to two new methods inside loop
:
void loop()
{
unsigned long now = millis();
if ((now - lastRefreshTime) > refreshInterval)
{
checkInput();
checkCollisions(); // New method to check for the ball's collisions
update(); // New method to update the position of the ball on each frame
draw();
lastRefreshTime = now;
}
} |
You add a method called checkCollisions
that, as the name implies, checks for collisions between the ball and screen, and the ball and paddles. The other method is called update
and is going to be in charge of updating the X and Y position of the ball in each frame. Write the code for update
as follows:
/*
* Function to update the X and Y position of the ball;
*/
void update()
{
ballX += ballSpeedX;
ballY += ballSpeedY;
} |
On every call to update you add the X and Y speed of the ball. The speed can be positive or negative, to indicate if the vertical movement is up or down, and the horizontal movement is left or right. Move on to checkCollisions
, the beefiest method of them all:
/*
* Checks collisions between the ball and walls, and the ball and paddles
*/
void checkCollisions()
{
// 1
// Check vertical collisions
if (ballSpeedY > 0)
{
// 2
// Limit collision checks to the ball and bottom
if (ballY + ballSize + ballPadding >= height)
{
ballY = height - ballSize - ballPadding;
ballSpeedY *= -1;
}
}
else
{
// 3
// Limit collision checks to the ball and top
if (ballY <= 0)
{
ballY = 0;
ballSpeedY *= -1;
}
}
// 4
// Check horizontal collisions
if (ballSpeedX > 0)
{
// 5
// Limit collision checks to the ball and right edge of the screen
if (ballX + ballSize + ballPadding > width)
{
reset();
}
else if (ballX >= twoPaddleX-ballSpeedX && (ballY - ballSize >= twoPaddleY && ballY <= twoPaddleY + paddleHeight))
{
ballSpeedX *= -1;
ballX = twoPaddleX - ballSize - ballPadding;
draw();
}
}
else
{
// 6
// Limit collision checks to the ball and left edge of the screen
if (ballX < 0)
{
reset();
}
else if (ballX <= onePaddleX+ballSpeedY && (ballY - ballSize >= onePaddleY && ballY <= onePaddleY + paddleHeight))
{
ballSpeedX *= -1;
ballX = onePaddleX + ballPadding;
draw();
}
}
} |
Let’s go over each numbered section:
- The first
if
statement checks for vertical collisions, meaning if the ball has collided with the top or bottom of the screen.
- If the ball’s vertical speed is positive then it’s moving down, and thus you check for collisions against the bottom portion of the screen. If a collision occurred you reset the ball’s position to right up against the screen and change the vertical speed of the ball to be negative, so it now moves up.
- This is the same process as before except you now check for collisions against the top of the screen. Should the ball collide with it then you reset its position and change the direction of the Y speed.
- Now come all the checks for horizontal collisions. Either between the ball and paddles or the ball and the screen. If the X speed is positive then the ball is moving towards the right side of the screen. This check is useful as it spares you from checking for collisions in all four scenarios. Instead you can focus your checks against the right side of the screen and the right paddle, optimizing performance.
- In this conditional check you verify the ball’s position against the right side of the screen. If the ball is outside of the screen then
reset
is called. You’ll write that method in just a second, but all it does is reset the game so you can play again without having to hit reset on your Arduino. If the ball didn’t collide against the edge of the screen then you check for collisions against the paddle. If this is the case then you change its X speed, X position, and call draw
to update the screen.
- This is the same logic as before, except now you are first checking for collisions against the left side of the screen and then left paddle.
Last but not least, write the code for reset
as follows:
/*
* Resets the paddles and ball to their initial state should either player loose
*/
void reset()
{
// Reset the game to the initial state;
ballX = 63;
ballY = 31;
onePaddleY = 28;
twoPaddleY = 28;
ballSpeedX = 3;
ballSpeedY = 2;
draw();
delay(3000);
} |
This reset’s the ball’s X and Y position, the X and Y speed, and also the paddle’s Y position. Afterwards you call draw
to re-draw everything on screen and delay execution of your program for 3 seconds (3000 milliseconds) in order for players to get ready for the next round.
Upload your sketch one last time and give your pong game a test.
GREAT SUCCESS!!! :]
You’ve done it. You have gone from having a bunch of electric components, an Arduino board, and an LCD screen, to a fully functional pong game. This is your own little console, you’ve made something akin to the first Game Boy! A huge congratulations to you! Hopefully this is the beginning of many more fun circuits and video games with Arduino for you.
You can download the final project here. It includes the Arduino sketch along with the GLCD library, just in case.
Where to go from here?
Where can you go from here? Well, the possibilities are endless. Here are some nice ideas for you to try:
- Add score tracking and a UI for each player
- Add A.I. for player two so you can play alone
- Try making a new game instead of pong
- Try using potentiometers to control paddle movement instead of buttons
The possibilities really are endless. You basically have a Game Boy. Google around a bit and you’ll find tons of great games or sample projects that make use of LCD screens. You could build a calculator, you could make other animations, etc.
I hope you’ve enjoyed this tutorial and learned more about electronic circuits, Arduino, soldering, and everything else that you’ve done here. I also hope that this tutorial sparks more interest in you to create other electric circuits and to continue tinkering. It really is a lot of fun.
For any questions, comments or feedback please contact me via Twitter @Airjordan12345. Many thanks again among congratulations on your new game! :]
Arduino Tutorial: Creating Pong is a post from: Ray Wenderlich
The post Arduino Tutorial: Creating Pong appeared first on Ray Wenderlich.