Coding your MAKERbuino - Pong!

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 theArduino reference and the MAKERbuino library referenceopen 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:

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:

if(gb.update()){
}

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_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.

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:

gb.display.fillRect(player_x, player_y, player_w, player_h);

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:

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:

gb.buttons.repeat(button, duration);

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.

player_y = max(0, player_y - player_vy);

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.

player_y = min(LCDHEIGHT - player_h, player_y + player_vy);

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:

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:

gb.display.fillRect(oponent_x,oponent_y, oponent_w, oponent_h);

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.

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.

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:

ball_x = ball_x + ball_vx;
ball_y = ball_y + ball_vy;

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:

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.

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.

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:

gb.display.fillRect(ball_x, ball_y, ball_size, ball_size);

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.

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:

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:

if(gb.buttons.pressed(BTN_C)){
gb.titleScreen(F("Pong Tutorial"));
}

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:

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Cookie settingsACCEPT

Privacy & Cookies Policy

Privacy Overview

This website uses cookies to improve your experience while you navigate through the website. Out of these cookies, the cookies that are categorized as necessary are stored on your browser as they are as essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may have an effect on your browsing experience.

This website uses cookies to improve your experience while you navigate through the website. Out of these cookies, the cookies that are categorized as necessary are stored on your browser as they are as essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may have an effect on your browsing experience.

Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.

Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.