Coding your MAKERbuino - Pong!Made by Miguel Dräger (@Bl4ckM4ch1n3)
Let’s recreate one of the first computer games ever created
Here we are, back for another tutorial! As cliché as it may sound, to become a famous MAKERbuino developer, you’ll have to start simple. And what could be simpler than Pong? Two paddles and a ball – that’s it! But let’s start with some basics before we get to the coding part of this tutorial.
Writing a game is pretty much like writing any other kind of software. There are a few tips and tricks you should keep in mind.
Always have your resources ready
This is one of the most important tips if you just started developing. It’s totally normal to forget about a couple of functions. Nobody can keep all this stuff memorized all the time. So make sure to keep the Arduino reference and the MAKERbuino library reference open in your browser.
Get a brief overview of what you want to do.
This will help you keep your eyes on the prize. “I want to create an MMORPG or shooter with knights or aliens or maybe even Egyptians.” is not a brief overview. But “I want to create a Legend of Zelda like fantasy RPG.” is a much better approach. In our case it’s: “I want to create a simple tennis game where the player is playing against a computer opponent.”
Use rectangles instead of sprites for rapid prototyping
This will speed up your process of testing new features. If you’ve managed to write a functioning piece of code, then you can take your time to design a nice sprite. You can avoid creating unnecessary sprites this way.
Break down the game into smaller chunks
This will ease your process of writing the game. You can plan and track the progress of your tasks a lot better that way. Our tasks will look like this:
– Setting up the Sketch
– Program the player paddle / movement
– Program the opponent paddle / movement
– Program the ball / movement
– Program the scoring system
– Program the exit to the main title screen
Alright, let’s get started! Let’s stick with our last tip and set up the Arduino Sketch now. Open your Arduino IDE and select File -> New. Your code should look like this:
The next step is to include the necessary libraries and to create a Gamebuino object. You can include the libraries by selecting Sketch -> Include Library -> SPI and Sketch -> Include Library -> Gamebuino or by typing #include and #include at the top of your code. To create a Gamebuino object just type in Gamebuino gb; right under your includes. Your Code should now look like this:
Now, we’re going to complete the setup function. This is pretty simple – we just need to add the 4 following lines within the curly brackets of the setup function:
Remember creating the Gamebuino object? We named it “gb”. So every time we want our MAKERbuino to do something that relies on the Gamebuino library, we’ll have to call the corresponding function from our Gamebuino object “gb”.
Calling gb.begin(); will initialize the Gamebuino object.
It is absolutely necessary to call this function right at the start of the setup function.
As you might have guessed, gb.titleScreen(); will bring up the title screen for our game. The parameter F(“Pong Tutorial”) will print “Pong Tutorial” right under the Gamebuino logo.
Pro Tip: If you have fixed strings, always use F(“String Goes Here”). Fixed strings are used for e.g. the title of your game, PopUp Messages (Enemy defeated etc.) or HUD-elements like “Score:” or “Health:”. You’ll save some RAM space that way, as the string will be stored within the flash memory of the microcontroller (not RAM).
The function gb.pickRandomSeed initializes the random number generation with a random seed. The seed is calculated using several variable factors. We’ll need those random numbers later.
The last lines are pretty easy to understand. If you set gb.battery.show to true you can see a little battery level indicator at the top right corner of the screen. We’ll set it to false so it won’t bother us while we’re playing.
Finally, we’re setting the font size to 2, since we want big and fancy score counters.
Let’s complete the first task of our list by adding the gb.update if-statement. Go to the loop function and add the following line within the curly brackets:
The code for our game will be inside the curly brackets of the gb.update if-statement. This is because gb.update returns true at a fixed time interval. Every 50 milliseconds to be exact, so our game will run at 20 frames per second.
Now we’re ready to roll! According to our list, we’ll start with the code for the player. Let’s add the following variables between the Gamebuino object and the setup function:
The player_score variable should be pretty self-explanatory.
The player_h variable represents the height of the player paddle. The value of 16 means its 16 pixels high. This is ⅓ of MAKERbuino’s screen.
The width of the player paddle is defined by the player_w variable. The player paddle will have a width of 3 pixels.
Now, we need to define the starting position of the player paddle. We’ll use the player_x and the player_y variables. We’ll need to set the player_x variable to 0 so the paddle will appear on the left edge of the screen. The y-position of the player is defined by the height of the screen (LCDHEIGHT) minus the player size. The result of this calculation will be divided by 2. This will make the player paddle spawn right in the middle of the left edge.
The last thing is the velocity of the player paddle. The paddle will move 2 pixels up or down every frame if the corresponding button is pressed.
Your code should look like this now:
If you would upload the Sketch to the MAKERbuino at this point, you wouldn’t see anything. We created the player variables but didn’t tell the MAKERbuino to draw the player on the screen. We’ll fix that now. Just add the following line within the curly brackets of the gb.update if-statement:
This function draws a filled rectangle at a given point with a given size. If you take a look at the syntax on the corresponding reference page you’ll see that it’s pretty easy:
Your code should look similar to this by now:
If you would upload your Sketch to the MAKERbuino you would now see your player paddle. But it won’t move – oh no! So our next step is to make the paddle move.
This will be easy to achieve since the player paddle will only move in two different directions: up and down.
Add the following lines of code inside the curly brackets of the gb.update if-statement, but make sure to add them before you draw the player!
It is essential to check the buttons before you draw the player in your code. Otherwise, you would always be a frame behind because you’ll be drawing the last frame’s state. Just follow the IPO model. IPO means Input-Process-Output. Check the Buttons (input), change player_y if needed (process), draw the player onto the screen (output).
Let’s get back to the code. As you can see, there are two if-statements. Both of them are using the gb.buttons.repeat function. We’ll take a short look at the Syntax:
The function returns true every time the given button has been held down for the given duration. The duration is measured in frames per second. So our paddle will move 2 pixels every frame as long as we’re holding down the button.
We’re using the min function and the max function to keep the player paddle from getting offscreen. The min function returns the smaller of the two given numbers and the max function returns the larger of the two given numbers.
If we’re holding down the Up-button we’re continuously decreasing the value of player_y by the value of player_vy (player paddle’s velocity). The max function will return the value of player_y minus player_vy because its result is still larger than 0. If the value of player_y reaches 0, the paddle will be at the top of the screen. The max function would return 0 because both values are identical. If you try to decrease the value of player_y once more, the max function will return 0 because 0 is larger than -2. This way we can keep the paddle from leaving the screen at the top.
The min function works identically but with different values and a different output. If we’re holding down the Down-button we’re continuously increasing the value of player_y by the value of player_vy (player paddle’s velocity). The min function will return the value of this addition as long as its result is smaller than 32.
And that’s it, the code behind player’s paddle is done! At this point your code should look similar to this:
Following the plan, we’ll move on to coding the opponent paddle.
Of course, we’ll need the same variables for the opponent as we needed for the player.
Since we can’t name it exactly the same, we’ll format their names by replacing “player_” with “opponent_”.
You can use the same values except for two variables: opponent_x and opponent_y. Set opponent_x to LCDWIDTH – opponent_w and opponent_y to (LCDHEIGHT – opponent_h)/2. The opponent paddle will spawn in the middle of the right edge this way.
Compare your source code with the following lines. If everything seems fine, you’re good to go ahead.
We’re using the buttons on the MAKERbuino to make the player’s paddle move. But how to move the opponent paddle?
Of course, we’ll need the min function and the max function to keep the paddle from going offscreen.
But when do we move the opponent paddle? It’s quite easy if you think about it. The opponent paddle is 16 pixels high. This means it has 8 upper pixels and 8 lower pixels. If the value of ball_y plus half the size of the ball is bigger than the y value of the paddle + 8 pixels, we’ll move the paddle down.
Otherwise, we’ll move it up. This can be achieved by a simple if-else-statement.
We’ll add the variables of the ball within the next task of our list. But we know that the ball needs an x value, a y value, and a size value so we can work with that for now. Just add the following lines of code right under our player movement code:
This will look quite intimidating at first, but if you take a closer look you’ll easily understand how it works.
First, we need to check if the y value of the ball plus half of its size is bigger than the y value of the opponent paddle plus half of the opponent’s height within the if-statement.
If this is true we’ll move the opponent down with its velocity (2 pixels every frame).
After we’ve moved the paddle, we’ll use the min function to keep it inside the screen. It should be pretty clear since we’ve used the same method to keep the player on the screen.
The lowest possible position for the opponent paddle is the height of the LCD minus the height of the paddle (48-16 = 32). So if we move the paddle down and the resulting value is bigger than 32, the min function will set the value of the opponent_y variable to 32. This way the opponent paddle can’t leave the screen at the bottom.
If the condition of our if-statement returns false, the program will move on to the else branch. This means that we’ll decrease the value of the opponent_y variable by the opponent’s velocity to move it up.
Of course, the lowest possible value is 0 so we’ll use the max function to keep the opponent paddle from leaving the screen at the top. The last thing to do is to make the opponent paddle visible.
We’ll use the same function as the one we’ve used for drawing the player paddle. Add the following line of code right after drawing the player paddle:
And that’s it about the opponent paddle! If you’d try to compile the code right now, Arduino IDE would most likely throw some errors because we didn’t declare the variables for the ball. But we’ll fix that right away.
Your code should look like this now:
The next step on our list is to program the ball and its movement. First, we’ll need to create all the necessary variables.
Variables named ball_x, ball_y, and ball_size that we used earlier are three of the five variables we’ll need. The last two missing variables are determining the ball’s velocity on the x and y axis.
Add the following variables right under the opponent variables:
The only variable worth mentioning is the variable ball_x, the rest should be pretty self-explanatory.
We’ll spawn the ball right next to the opponent paddle with a distance of one pixel. To achieve this we’ll subtract the ball’s size, the width of the opponent paddle and the distance from the edge of our screen. Now we’re going to make the ball move! It’s really simple since the ball should move all the time. Because the opponent paddle is adapting its position based on the position of the ball we’ll need to write everything regarding the ball between moving the player paddle and moving the opponent paddle. Otherwise, the opponent paddle would change its position based on an old state.
Add the following lines between the code for moving the player paddle and the code used for moving the opponent paddle:
This should be easy to understand. We’re only adding the different velocities to their corresponding position variables. You may have already noticed that the ball will leave the screen after a couple of frames.
So let’s add the bottom and top border collision right away.
And we’ll also add a fancy sound effect that goes off everytime the ball touches the border. We won’t need the min function and the max function this time.
Add the following lines right under the piece of code used for moving the ball:
Let’s take a look at those lines.
We’ve initialized both the x-axis velocity and the y-axis velocity with positive values. This will make the ball move to the right and down. If the ball is trying to leave the screen at the bottom border, the condition of the first if-statement will return true and the code within the curly brackets will be executed.
The value of the ball_y variable will be set to 42 (48-6) to keep the ball within the screen. Next, the y velocity of the ball will be negated (ball_vy = -3) and we’l play the Tick sound that’s defined within the gamebuino library.
We’ll also add the velocity values of the ball to their corresponding position variables. So if we negate its value within the first if-statement, we’ll add -3 every frame. This means that the ball starts traveling upwards after it touched the bottom border. And this is where the second if-statement comes into place. If the ball is leaving the screen on the top border (ball_y < 0), ball_y is set to 0 and ball_vy will be negated again, resulting in a positive value.
Wowzie, the ball is now able to bounce up and down, well done!
Unfortunately, the ball would go right through the paddles and leave the screen.
So, the next step is to check if the ball collided with one of the paddles. But let’s make a quick code check first.
Your code should look like this now:
It’s easy to check if the ball is trying to leave the screen, but how should we check whether it collided with one of the paddles?
There is an easy-to-use function within the Gamebuino library and it’s called gb.collideRectRect, let’s take a look at the syntax:
II have to admit that this function looks quite confusing at first, but you’ll understand how it works pretty quick.
The paddles and the ball are drawn as rectangles. Rectangles are always drawn from the top left corner and their size is defined by width and height.
If you take a look at the arguments of the function, you’ll see that they contain every aspect of a rectangle.
The x argument takes the x position of the top left corner, the y argument takes the y position of the top left corner, the w argument takes the width and the h argument takes the height. All arguments are numbered with 1 and 2 where 1 stands for the first rectangle you want to check and 2 stands for the second rectangle you want to check.
Let’s turn our knowledge into a helpful piece of code.
Add the following lines right under the bottom border check for the ball:
If the ball overlaps the player paddle, the condition of the if-statement returns true. This means that the ball actually collided with the player paddle. A collision occurs if at least one pixel of the first rectangle is overlapping a pixel of the second rectangle.
Therefore, we’ll need to set the value of ball_x to player_x plus player_w to move the ball out of the paddle.
After that, we’ll negate ball’s velocity on the x-axis. And yeah, we’ll play the fancy sound effect again.
Of course, the ball needs to collide with the opponent paddle as well. Otherwise, the game would be pretty easy.
We’ll handle the collision similar to our player collision check.
Add this code right under the player collision check:
As you can see it doesn’t differ that much from the first collision check. The only difference is the new value of ball_x.
We’re ready to finish this task! The only thing left to do is to draw the ball onto the screen. As you can imagine, it’s as simple as drawing the paddles.
Add the necessary code to draw the ball right after drawing the opponent paddle:
And we’re good to go! There’s not much left to do until our Pong game is complete. Compare your code to the following chunk, if your code looks similar, you can compile and test it on your MAKERbuino or using an emulator.
Let’s get to the last two tasks on our list: implementing the scoring system and the possibility to exit our game to the title screen.
Our ball can collide with the upper and lower border and with both paddles, but it will still leave the screen at the right or left border. We’ll fix that issue by adding a scoring system.
If the ball touches the right border, we’ll get a point. If it touches the left border, our opponent will get a point. If one of the score counters reaches 10, both counters will be set to 0 again.
Let’s start with the left side collision.
Add the following code right under the opponent paddle collision:
If the ball touches the left side (ball_x < 0), the opponent gets a point because we missed the ball.
After that, we’ll play another predefined sound effect from the Gamebuino library.
We won’t change the velocity on the x-axis because the ball will spawn right next to the opponent.
Maybe you’ll recognize the code from the third line. It’s the same as the one we used to initialize the ball. The last line will set ball_y to a random value between 0 (upper edge) and the height of the LCD minus the size of the ball (lower edge). This is why we needed to call gb.pickRandomSeed function within the setup function (all the way at the beginning of our program).
Now we’re going to add the right side collision. Add the following lines straight under the left side collision:
If the ball touches the right side, the player gets a point because the opponent missed the ball.
Yet again, we’ll play another pre-defined sound from the Gamebuino library.
The next line will place the ball on the opponent side of the playfield on a random y position. The velocity of the ball will be negated so it starts traveling to the left again.
We’re almost done with our score system, good job so far! We just need to reset both scores once a score reaches 10 and display the scores.
Resetting the scores is quite easy as we only need an if-statement. Add the next few lines right under the last collision check:
As you can see, it’s a simple if-statement with two conditions linked by an OR statement.
Now we only need to display the scores. We’re going to draw them right before we draw the paddles and the ball.
Add the upcoming lines of code right before drawing the player paddle:
Drawing the scores isn’t that complicated either, we just need to set a couple of variables and then print the current content of both score variables.
Your game should be working fine if your code looks like the following:
Let’s finish our game by completing the last task on our list: Create a possibility to exit the game to the title screen.
This is very important as the title screen allows you to enter the SD card loading screen again.
All we need to do is to check the C-button. If it was pressed, we’ll go back to the title screen. Of course, it’s as simple as checking the Up- or Down-button.
A simple if-statement will do the trick. Remember the IPO model? We could add the C-button check anywhere within the if(gb.update()) if-statement and it would work. But we’re sticking to the IPO model, so we’ll add the next few lines of code right after we’ve checked the Up and Down buttons:
And that’s it! Our game is done! Congratulations, you did a really great job! I hope you had fun and learned a lot! Feel free to leave your feedback at the Community Forum. Stay tuned for more tutorials and find the full code below: