Make Your Own Arduboy Game: Part 7 - Make Pong From Scratch!

Before You Start

Hey! Before you start! Do you mind following me on Twitter? I really spent a lot of time working on this tutorial series. I had to re-write this tutorial twice. It would be great if you took look at the other projects I work on. :slight_smile: http://www.twitter.com/crait

And when you get done, please respond and let me know that you’re done! :smiley:

Previous parts of this series:
Part 1, Part 2, Part 3, Part 4, Part 5, Part 6

This Tutorial

In this tutorial, we’re going to learn more about writing our own functions, switches, and basically combining everything we’ve learned up to this point in order to make a playable version of Pong that you can play against a simple AI.

Switch

Before we begin, I want to teach you about the switch keyword. This one is easy and can really save the amount of code you have to write.

Take a look at this code:

if (dude == 0 ) {
  a = 10;
  b = 2;
} 
else if (dude == 1) {
  c = 3;
} 
else if (dude == 2) {
  a = 0;
  b = 3;
  c = 4;
} 
else if (dude == 3) {
  a = 1;
} 
else if (dude == 4) {
  a = 2;
}

Just by looking at this code, you can tell that there are different sections that are executed depending on the value of dude . You can re-write this same code using the switch keyword. Check this out:

switch (dude) {

  case 0:
    a = 10;
    b = 2;
    break;

  case 1:
    c = 3;
    break;

  case 2:
    a = 0;
    b = 3;
    c = 4;
    break;

  case 3:
    a = 1;
    break;

  case 4:
    a = 2;
    break;

}

The different sections of code are called cases. The value of dude is checked and then then the right case is executed. For example, if dude is equal to 3 the code in the case 3 is run, which is a = 1; . Whenever the break; instruction is reached, the Arduboy jumps out of the switch .

Easy, right? We’ll use this to structure our game’s code.

Planning Our Game

Whew! We’re almost ready to start coding! Let’s plan this game out and then start coding.

By breaking down the project into a series of small steps, it helps me realize how much further I have to go as well as keep me motivated to finish the next step. After each major step, I typically test my game to make sure I didn’t break anything and that the new mechanics are working properly.

To break down our Pong game, we’ll need to follow these steps in development:

  1. Set up the new sketch
  2. Create a title screen, game screen, win screen, and lose screen
  3. Create a ball that bounces around the screen
  4. Create and control a paddle
  5. Create the computer’s paddle
  6. Programming collision
  7. Adjusting the AI
  8. Scoring
  9. Creating a new ball and resetting the game

Don’t worry if these don’t all make sense! I will explain them as we move forward!

1. Creating A New Sketch

Open up your Arduino IDE and create a new sketch. ( File > New ) In the sketch, like always, we’re going to include the Arduboy library, create an Arduboy object, as well as updating our setup()/loop() functions to include the standart display functions. If that sounds like a lot, don’t worry! Just make sure your code looks like this!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;

//Variables declared here

void setup() {
  arduboy.begin();
  //Set-up here
  arduboy.clear();
}

void loop() {
  arduboy.clear();
  //Game code here
  arduboy.display();
}

Notice that I added some comments at the top for the project as well as some placeholders. Up until now, this is usually how we started each project, but let’s add a little more to this to give us a better starting point for any future projects.

Frame Rates

Most game consoles can play games at 30 or 60 frames per second. That means that the screen is updated 30 or 60 times a second. The more instructions that a computer has to run in a second, the slower that framerate can be. If the frame rate is too slow, some games are unplayable. Have you ever played a game and it played so slowly that it wasn’t fun anymore?

During the setup() function, we can declare what framerate we want our Arduboy game to be using the arduboy.setFrameRate() function. To make the Arduboy game run at 60 frames per second, include this line of code.

arduboy.setFrameRate(60);

A game slowing down can ruin the gameplay experience, but if a game runs too fast, that can be bad, too! We want the game to run at a consistant framerate. In order to do that, add this to the beginning of the loop() function.

if (!arduboy.nextFrame()) {
  return;
}

This isn’t really important, but what this does is check if it’s too early to display the next frame. If it is, the Arduboy will loop again until it’s ready. Pretty much making sure that everything runs as smoothly as possible.

You’re ready!

During the setup() function, we also want to seed the random number generator so that we can grab some random numbers later. Include the srand(7/8); to your setup() function.

Okay! You should be ready! Here’s the code that you should be able to use at the start of all your projects!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;

//Variables declared here

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {
  
  //Prevent the Arduboy from running too fast
  if (!arduboy.nextFrame()) {
    return;
  }

  arduboy.clear();
  //Game code here
  arduboy.display();
}

2. Structuring The Game

We need to set up a title screen for our game as well as a win and game over screen. We also need a section for our gameplay. These will be called game states. Let’s create a variable to keep track of the game state as well as use the switch keyword to set up different blocks of code for each state.

Let’s start by declaring a variable called gamestate .

int gamestate = 0;

In the area of the loop() for the game code , let’s add the switch and check the value of gamestate .

switch (gamestate) {

}

If gamestate is 0, we want the title screen. If it’s 1, we want the gameplay state. If it’s 2 or 3, then we’ll show a win screen or a game over screen. Let’s set up those cases:

switch (gamestate) {

  case 0:
    //Title screen
    break;

  case 1:
    //Gameplay screen
    break;

  case 2:
    //Win screen
    break;

  case 3:
    //Game over screen
    break;
}

Let’s also add text so that we can test this.

switch (gamestate) {

  case 0:
    //Title screen
    arduboy.setCursor(0, 0);
    arduboy.print("Title Screen");
    break;

  case 1:
    //Gameplay screen
    arduboy.setCursor(0, 0);
    arduboy.print("Gameplay");
    break;

  case 2:
    //Win screen
    arduboy.setCursor(0, 0);
    arduboy.print("Win Screen");
    break;

  case 3:
    //Game over screen
    arduboy.setCursor(0, 0);
    arduboy.print("Game Over Screen");
    break;
}

Here’s the code so far! Test it out and see how it works!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;

//Variables declared here

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {
  
  //Prevent the Arduboy from running too fast
  if (!arduboy.nextFrame()) {
    return;
  }

  arduboy.clear();
  
  switch (gamestate) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      break;

    case 1:
      //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      break;

    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen");
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      break;
  }

  arduboy.display();
}

So, the title screen appears, but nothing else works. Let’s add the ability to go to different screens when you push the A button.

Inside of each case , let’s check if the A button is pressed, and if it is, then change the value of gamestate . Usually, we’ll increase gamestate by 1, but during the last case , we’ll set it back to 0 in order to cycle through all of the screens.

switch (gamestate) {

  case 0:
    //Title screen
    arduboy.setCursor(0, 0);
    arduboy.print("Title Screen");
    if (arduboy.justPressed(A_BUTTON)) {
      gamestate = 1;
    }
    break;

  case 1:
    //Gameplay screen
    arduboy.setCursor(0, 0);
    arduboy.print("Gameplay");
    if (arduboy.justPressed(A_BUTTON)) {
      gamestate = 2;
    }
    break;

  case 2:
    //Win screen
    arduboy.setCursor(0, 0);
    arduboy.print("Win Screen");
    if (arduboy.justPressed(A_BUTTON)) {
      gamestate = 3;
    }
    break;

  case 3:
    //Game over screen
    arduboy.setCursor(0, 0);
    arduboy.print("Game Over Screen");
    if (arduboy.pressed(A_BUTTON)) {
      gamestate = 0;
    }
    break;
}

Okay, let’s run the completed code to test it!

3. Bouncing Ball

Alright, let’s create the ball for this game. It’ll bounce around the screen. If it hits the top, bottom, or sides of the screen, it’ll change direction.

We’ll need to declare variables for the ball’s location as well as the ball’s size.

int ballx = 62;
int bally = 0;
int ballsize = 4;

We’ll also create one for the vertical and one for the horizontal direction of on the screen.

int ballright = 1;
int balldown = 1;

Whenever ballright is 1, we’ll move the ball to the right. If ballright is -1, we’ll move the ball to the left. Likewise, when balldown is 1, we’ll move the ball down. When balldown is -1, we’ll move the ball up.

Inside of the gameplay case, we’ll start by drawing the ball using the arduboy.fillRect() function. Add this line:

arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);

Next, let’s handle the movement of the ball horizontally by changing the value of ballx .

if (ballright == 1) {
  ballx = ballx + 1;
}
if (ballright == -1) {
  ballx = ballx - 1;
}

Whenever ballx is at the left or right edge of the screen, we’ll change the value of ballright . Where are the left and right edges of the screen? Well, the Arduboy screen is 128 pixels wide, so if ballx is 0, the ball is on the left side of the screen. If ballx is 127, then it’s on the right side of the screen.

if (ballx == 0) {
  ballright = 1;
}
if (ballx == 127) {
  ballright = -1;
}

This is what your code should look like. Give it a test and see the ball bounce from side to side!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }
      //Reflect the ball off of the left side of the screen
      if(ballx == 0) {
        ballright = 1;
      }
      //Reflect the ball off of the right side of the screen
      if(ballx == 127) {
        ballright = -1;
      }
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

Cool! Let’s add vertical movement! It’s pretty much the exact same thing that we did with the ballright variable.

When balldown is 1, we’ll add 1 to bally . When balldown is -1, we’ll subtract 1 from bally .

if (balldown == 1) {
  bally = bally + 1;
}
if (balldown == -1) {
  bally = bally - 1;
}

Of course, we’ll also have to reflect it off of the top and bottom of the screen, right? The Arduboy’s screen is 64 pixels high, so let’s add this code:

if (bally == 0) {
  balldown = 1;
}
if (bally == 63) {
  balldown = -1;
}

That makes sense, right? Good! Here’s all the code we have so far! Let’s test it out!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }
      //Reflect the ball off of the left side of the screen
      if(ballx == 0) {
        ballright = 1;
      }
      //Reflect the ball off of the right side of the screen
      if(ballx == 127) {
        ballright = -1;
      }
      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally == 63) {
        balldown = -1;
      }

      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

There’s one more little tiny thing I want use to change before moving on. If you watch the ball carefully as it moves, you’ll notice that it moves off of the screen on the bottom and right-side.

The reason it does that is because we change the ball’s vertical direction based on the top of the ball hitting the top of the screen or the top of the ball hitting the bottom of the screen as well as checking if the left-side of the ball hits the left-side of the screen or the left-side of the ball hitting the right-side of the screen.

We need to make sure the bottom of the ball bounces against the bottom of the screen and make sure the right-side of the ball bounces off of the right-side of the screen. This is an easy adjustment.

Instead of checking if ballx is equal to 127, we need to check if ballx + ballsize is equal to 127. Same for bally .

Change the reflection cod to…

if (ballx + ballsize == 127) {
  ballright = -1;
}

… and…

if (bally + ballsize == 63) {
  balldown = -1;
}

Run the code on your Arduboy! Mesmorizing, isn’t it?

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }
      //Reflect the ball off of the left side of the screen
      if(ballx == 0) {
        ballright = 1;
      }
      //Reflect the ball off of the right side of the screen
      if(ballx + ballsize == 127) {
        ballright = -1;
      }
      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }

      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}
3 Likes

4. Creating Your Paddle

This part is my favorite part since it really gives you control over the game. We’re going to make your paddle that you can move with the Up and Down buttons!

In the variable section, let’s declare these variables… These should be self-explanatory.

int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;

Back inside of the gameplay section, let’s draw the paddle, then allow it to be moved.

arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);

Next, let’s add some code to change the value of playery with the Up and Down buttons. Luckily, we don’t need to worry about coding any buffers for this since we want to be able to hold down the button for movement.

if (arduboy.pressed(UP_BUTTON)) {
  playery = playery - 1;
}
if (arduboy.pressed(DOWN_BUTTON)) {
  playery = playery + 1;
}

You probably could have figured that part out, but what if we only want to allow a player to move their paddle if they have room to move their paddle? We need to use the and keyword.

  • If the player presses the Up button and the top of the paddle ( playery ) is greater than ( > ) the top of the screen ( 0 ), then allow them to change the value of playery .
  • If the player presses the Down button and the bottom of the paddle ( playery + paddheight ) is less than ( < ) the bottom of the screen ( 127 ), then allow them to change the value of playery .
if (arduboy.pressed(UP_BUTTON) and playery > 0) {
  playery = playery - 1;
}
if (arduboy.pressed(DOWN_BUTTON) and playery + paddheight< 63) {
  playery = playery + 1;
}

In the above example, I have used the keyword ‘and’ keyword. This works in the Arduboy environment but in C++ (the language that this tutorial is teaching) the ‘and’ keyword is actually specified as ‘&&’. When you are viewing others code you will see this regularly so it’s best to get familiar with it. BTW, the ‘or’ command is represented as ‘||’.

So using the ‘&&’ keyword …

if (arduboy.pressed(UP_BUTTON) && playery > 0) {
  playery = playery - 1;
}
if (arduboy.pressed(DOWN_BUTTON) && playery + paddheight < 63) {
  playery = playery + 1;
}

That was relatively simple, right? Okay, here’s the code you should end up with. Let’s run it and test moving your paddle around!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }
      //Reflect the ball off of the left side of the screen
      if(ballx == 0) {
        ballright = 1;
      }
      //Reflect the ball off of the right side of the screen
      if(ballx + ballsize == 127) {
        ballright = -1;
      }
      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }

      //Draw the player's paddle
      arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
      //If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
      if (arduboy.pressed(UP_BUTTON) && playery > 0) {
        playery = playery - 1;
      }
      //If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
      if (arduboy.pressed(DOWN_BUTTON) && playery + paddleheight < 63) {
        playery = playery + 1;
      }

      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

5. Creating The Computer Paddle

Creating the computer’s paddle will be very similar to ours, except instead of moving the paddle with buttons, the paddle will move based on the position of the ball.

Let’s add these variables near the top of the sketch for the computer paddle:

int computery = 0;
int computerx = 127 - paddlewidth;

Notice that I set the computer’s paddle all the way to the right-side of the screen, then move it over a few pixels to fit it onto the screen. :slight_smile:

Down in the gameplay part of the code, let’s draw the computer’s paddle.

arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);

Great! Now, we need to set the rules for when the computer’s paddle moves. We can adjust the AI later, but for now, if the top of the ball ( ballx ) is higher than the computer’s paddle’s top ( computery ), then the computer’s paddle should move up. If the bottom of the ball ( bally + ballsize ) is lower than the computer’s paddle’s bottom ( computery + paddleheight ), then we need to move the comptuer’s paddle down.

In code form, it would look like this:

if (bally < computery) {
  computery = computery - 1;
}
if (bally + ballsize > computery + paddleheight) {
  computery = computery + 1;
}

Cool! :sunglasses: That was actually pretty easy! Run the code!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computery = 0;
int computerx = 127 - paddlewidth;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }
      //Reflect the ball off of the left side of the screen
      if(ballx == 0) {
        ballright = 1;
      }
      //Reflect the ball off of the right side of the screen
      if(ballx + ballsize == 127) {
        ballright = -1;
      }
      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }

      //Draw the player's paddle
      arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
      //If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
      if (arduboy.pressed(UP_BUTTON) && playery > 0) {
        playery = playery - 1;
      }
      //If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
      if (arduboy.pressed(DOWN_BUTTON) && playery + paddleheight < 63) {
        playery = playery + 1;
      }
      
      //Draw the computer's paddle
      arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
      //If the ball is higher than the computer's paddle, move the computer's paddle up
      if(bally < computery) {
        computery = computery - 1;
      }
      //If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
      if(bally + ballsize > computery + paddleheight) {
        computery = computery + 1;
      }

      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

Notice how good the AI is. The computer’s paddle will never miss! It’s too good! Don’t worry! We’ll adjust it later to make it possible to beat. :slight_smile:

6. Collision Checking

What we need to do next is change the ball up some. Instead of bouncing against the right and left side of the screen, we need to make it bounce off of the paddles instead. Once we get the ball bouncing off of the paddles, we’ll remove the original bouncing code!

Okay! Let’s begin by talking about when the ball will bounce off of the paddle. If the ball is higher than the paddle or lower than the paddle, then it will not bounce. If the ball is near the top, bottom, or middle of the paddle, then we’ll change it’s direction to bounce it back. Take a look at this beautiful masterpiece I drew for you to help illustrate this. :art:

Let’s look at the code for the ball hitting the left side of the screen.

if (ballx == 0) {
  ballright = 1;
}

Instead of checking if the ball’s left side ( ballx ) hits the edge of the screen, we need to do our check where the ball’s left side ( ballx ) hits the player’s paddle on the right side. The player’s paddle’s right is is going to be at playerx + paddlewidth .

We now have:

if (ballx == playerx + paddlewidth) {
  ballright = 1;
}

So, now that we know where we need to check along the X-axis, where do we check along the Y-axis?

Well, the paddle will make contact if the top of the paddle ( playery ) is higher on the screen than the bottom of the ball ( bally + ballsize ). Not only that, but the bottom of the paddle ( playery + paddleheight ) has to be lower on the screen than the top of the ball ( bally ).

if (ballx == playerx + paddlewidth && playery < bally + ballsize && playery + paddleheight > bally) {
  ballright = 1;
}

For the computer’s paddle, we need to offset ballx by ballsize since we are checking the right-side of the ball. Here’s what it would look like:

if (ballx + ballsize == computerx && computery < bally + ballsize && computery + paddleheight > bally) {
  ballright = -1;
}

That’s it! We’re sooo close to being done with this game! Here’s the code we have so far, but go ahead and test it out!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computery = 0;
int computerx = 127 - paddlewidth;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
       //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }
      //Reflect the ball off of the left side of the screen
      if(ballx == 0) {
        ballright = 1;
      }
      //Reflect the ball off of the right side of the screen
      if(ballx + ballsize == 127) {
        ballright = -1;
      }
      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }
      //Draw the player's paddle
      arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
      //If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
      if(arduboy.pressed(UP_BUTTON) && playery > 0) {
        playery = playery - 1;
      }
      //If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
      if(arduboy.pressed(DOWN_BUTTON) && playery + paddleheight < 63) {
        playery = playery + 1;
      }
      //Draw the computer's paddle
      arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
      //If the ball is higher than the computer's paddle, move the computer's paddle up
      if (bally < computery) {
        computery = computery - 1;
      }
      //If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
      if (bally + ballsize > computery + paddleheight) {
        computery = computery + 1;
      }
      //If the ball makes contact with the player's paddle, bounce it back to the right
      if( ballx == playerx + paddlewidth && playery < bally + ballsize && playery + paddleheight > bally) {
        ballright = 1;
      }
      //If the ball makes contact with the computer's paddle, bounce it back to the left
      if (ballx + ballsize == computerx && computery < bally + ballsize && computery + paddleheight > bally) {
        ballright = -1;
      }
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}
1 Like

Or Operator

We already talked about using the and operator. It’s a way we connect two comparisons inside of an if statement. To illustrate it, look at this code:

if (a == 5 && b > 20 ) {
  c = 0;
}

c will only be set to 0 if a is equal to 5 and b is greater than 20 .

There are some other operators that we can use instead of the and operator. One of them is the or operator. The or operator can be used to check if one of a few comparisons are true. Check out this code:

if (a == 5 || b > 20 ) {
  c = 20;
}

In the above example, c will be set to 20 if either a is equal to 5 or b is greater than 20 .

Keep the or operator in mind. We’ll use it soon!

7. Adjusting The AI

Okay, so, the computer player is way too good. We’ll never be able to win. One thing we can do to stop it from being so good is to only allow it to move some of the time. When will we allow it to move its paddle? Well, for starters, we could let it move its paddle only whenever the ball is close to the right side of the screen!

Find these lines of code:

if (bally < computery) {
  computery = computery - 1;
}
if (bally + ballsize > computery + paddleheight) {
  computery = computery + 1;
}

These are the lines of code that move the computer’s paddle. Let’s put an if statement around them that checks if the ball’s horizontal position ( ballx ) is close to the right-side of the screen. I put 115.

if (ballx > 115) {
  if(bally < computery) {
    computery = computery - 1;
  }
  if(bally + ballsize > computery + paddleheight) {
    computery = computery + 1;
  }
}

Test it out and think about it for a second. Notice how the computer’s paddle doesn’t always move like it used it? It’s a little more precise right now, but only moves whenever the ball is close to scoring.

But, we need to add more variance… More randomness… We need to make it so that the game is different every time you play. I KNOW! Let’s let the computer paddle move when the ball is close or let the computer paddle move when the computer picks a random number.

Back in Part 5 of this series, I showed you how to pick a random number. The line below shows how to select a random number between 0 and 19 - you might recall that the two parameters specify the lower range (inclusive) and the upper range (exclusive).

random(0, 20)

Let’s adjust that if statement from above!

if (ballx > 115 || random(0, 20) == 1) {

This means that if ballx is greater than 115, or a randomly-picked number is equal to 1, then we move the computer’s paddle. Basically, the computer’s paddle will randomly move 1/20th of the time.

Here’s the full code so far!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computery = 0;
int computerx = 127 - paddlewidth;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
       //Gameplay screen
      arduboy.setCursor(0, 0);
      arduboy.print("Gameplay");
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }
      //Reflect the ball off of the left side of the screen
      if(ballx == 0) {
        ballright = 1;
      }
      //Reflect the ball off of the right side of the screen
      if(ballx + ballsize == 127) {
        ballright = -1;
      }
      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }
      //Draw the player's paddle
      arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
      //If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
      if(arduboy.pressed(UP_BUTTON) && playery > 0) {
        playery = playery - 1;
      }
      //If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
      if(arduboy.pressed(DOWN_BUTTON) && playery + paddleheight < 63) {
        playery = playery + 1;
      }
      //Draw the computer's paddle
      arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
      //If the ball is higher than the computer's paddle, move the computer's paddle up
      if (ballx > 115 || random(0, 20) == 1) {
        if (bally < computery) {
          computery = computery - 1;
        }
        //If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
        if (bally + ballsize > computery + paddleheight) {
          computery = computery + 1;
        }
      }
      //If the ball makes contact with the player's paddle, bounce it back to the right
      if (ballx == playerx + paddlewidth && playery < bally + ballsize && playery + paddleheight > bally) {
        ballright = 1;
      }
      //If the ball makes contact with the computer's paddle, bounce it back to the left
      if (ballx + ballsize == computerx && computery < bally + ballsize && computery + paddleheight > bally) {
        ballright = -1;
      }
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

8. Scoring

Since we’re going to introduce scoring, we need a way for the ball to actually be scored. This means we need to remove some of our old bouncing code.

if (ballx == 0) {
  ballright = 1;
}
if (ballx + ballsize == 127) {
  ballright = -1;
}

Find those lines and remove them. This way, the ball will continue to move beyond the screen. In fact, we’ll check to see if the ball has moved off of the screen in order to give points!

Feel free to test the code out once you remove that code. :laughing:

To keep track of the scores, let’s declare some variables at the top of the sketch.

int playerscore = 0;
int computerscore = 0;

Now, look for the code in case 1 that displayed the text, “Gameplay.” Instead of just displaying that text, let’s modify it to display the player’s and computer’s scores.

arduboy.setCursor(20, 0);
arduboy.print(playerscore);

arduboy.setCursor(101, 0);
arduboy.print(computerscore);

Next, we need a way to actually score! Since the code now allows for the ball to move off of the screen, let’s check if it’s off the screen and if it is, then give someone a point, and then move it back into the middle of the screen.

Here’s how we can check if the ball is off of the screen.

if (ballx < -10) {

}
if (ballx > 130) {

}

To change the points, and put it back into the middle of the screen, we simply change the appropriate variables…

if (ballx < -10) {
  ballx = 63;
  computerscore = computerscore + 1;
}
if (ballx > 130) {
  ballx = 63;
  playerscore = playerscore + 1;
}

I really suggest testing the game out, so far. Here’s the code that I have:

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computery = 0;
int computerx = 127 - paddlewidth;
int playerscore = 0;
int computerscore = 0;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      //Display the player's score
      arduboy.setCursor(20, 0);
      arduboy.print(playerscore);
      //Display the computer's score
      arduboy.setCursor(101, 0);
      arduboy.print(computerscore);
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }

      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }
      //Draw the player's paddle
      arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
      //If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
      if(arduboy.pressed(UP_BUTTON) && playery > 0) {
        playery = playery - 1;
      }
      //If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
      if(arduboy.pressed(DOWN_BUTTON) && playery + paddleheight < 63) {
        playery = playery + 1;
      }
      //Draw the computer's paddle
      arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
      //If the ball is higher than the computer's paddle, move the computer's paddle up
      if (ballx > 115 || random(0, 20) == 1) {
        if (bally < computery) {
          computery = computery - 1;
        }
        //If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
        if (bally + ballsize > computery + paddleheight) {
          computery = computery + 1;
        }
      }
      //If the ball moves off of the screen to the left...
      if(ballx < -10) {
        //Move the ball back to the middle of the screen
        ballx = 63;
        //Give the computer a point
        computerscore = computerscore + 1;
      }
      //If the ball moves off of the screen to the right....
      if(ballx > 130) {
        //Move the ball back to the middle of the screen
        ballx = 63;
        //Give the player a point
        playerscore = playerscore + 1;
      }
      //If the ball makes contact with the player's paddle, bounce it back to the right
      if (ballx == playerx + paddlewidth && playery < bally + ballsize && playery + paddleheight > bally) {
        ballright = 1;
      }
      //If the ball makes contact with the computer's paddle, bounce it back to the left
      if (ballx + ballsize == computerx && computery < bally + ballsize && computery + paddleheight > bally) {
        ballright = -1;
      }
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 3;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

Awesome, huh? It’s pretty much a finished game at this point! We just need to clean it up, some!

One thing we need to do is check if someone wins! Let’s do a check to see if someone has 5 points. If they do, then change the gamestate variable to the appropriate value.

if (playerscore == 5) {
  gamestate = 2;
}
if (computerscore == 5) {
  gamestate = 3;
}

Hmm… Maybe we should change the code inside of case 2 for pushing the A button. Instead of taking you to gamestate = 3 , maybe we should use gamestate = 0 so that it takes you to the title screen of the game. Why would we want to show the win screen, then game over screen right afterwards, right?

Here’s the completed code so far:

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computery = 0;
int computerx = 127 - paddlewidth;
int playerscore = 0;
int computerscore = 0;

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      //Display the player's score
      arduboy.setCursor(20, 0);
      arduboy.print(playerscore);
      //Display the computer's score
      arduboy.setCursor(101, 0);
      arduboy.print(computerscore);
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }

      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }
      //Draw the player's paddle
      arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
      //If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
      if(arduboy.pressed(UP_BUTTON) && playery > 0) {
        playery = playery - 1;
      }
      //If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
      if(arduboy.pressed(DOWN_BUTTON) && playery + paddleheight < 63) {
        playery = playery + 1;
      }
      //Draw the computer's paddle
      arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
      //If the ball is higher than the computer's paddle, move the computer's paddle up
      if (ballx > 115 || random(0, 20) == 1) {
        if (bally < computery) {
          computery = computery - 1;
        }
        //If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
        if (bally + ballsize > computery + paddleheight) {
          computery = computery + 1;
        }
      }
      //If the ball moves off of the screen to the left...
      if(ballx < -10) {
        //Move the ball back to the middle of the screen
        ballx = 63;
        //Give the computer a point
        computerscore = computerscore + 1;
      }
      //If the ball moves off of the screen to the right....
      if(ballx > 130) {
        //Move the ball back to the middle of the screen
        ballx = 63;
        //Give the player a point
        playerscore = playerscore + 1;
      }

      //Check if the player wins
      if(playerscore == 5) {
        gamestate = 2;
      }
      //Check if the computer wins
      if(computerscore == 5) {
        gamestate = 3;
      }

      //If the ball makes contact with the player's paddle, bounce it back to the right
      if (ballx == playerx + paddlewidth && playery < bally + ballsize && playery + paddleheight > bally) {
        ballright = 1;
      }
      //If the ball makes contact with the computer's paddle, bounce it back to the left
      if (ballx + ballsize == computerx && computery < bally + ballsize && computery + paddleheight > bally) {
        ballright = -1;
      }
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

9. Reset Function

If you tested this out, you’re going to notice that once you win and try to play again, you won’t really be able to. The reason this happens is that the Arduboy remembers the score from the last match! It starts the new match of Pong, checks the scores, and realizes someone has already won, then shows you either the win screen or game over screen.

What we need to do is reset the game’s variables… ballx , playerscore , and computerscore .

Scroll all the way down to case 2 and case 3 . Inside of both of them, there’s a check to see if we push the A button. This is a great place to put the code to reset our variables since pushing A is supposed to reset the game to the very beginning.

So, inside of the button checks of case 2 and case 3 , put this code:

ballx = 63;
playerscore = 0;
computerscore = 0;

Actually! I got an idea! Instead of writing the same code multiple times, I should teach you about writing functions to save you time!

You already kinda know what functions are, right? They’re instructions that the computer follows. One example of this is arduboy.clear() , which will erase everything on the screen!

Well, one cool thing about programming is that you can write your own functions! It’s really helpful! Sometimes, you’ll have code that you want to use over and over again, but maybe you don’t want to type over and over again…

Scroll all the way up… After you declare your variables, let’s declare a function. :heart: We’ll call it resetgame() . Type this out:

void resetgame() {

}

Inside of the braces, we’ll be able to put the instructions that are contained within the resetgame() function. Let’s update it with the code we used a moment ago!

void resetgame() {
  ballx = 63;
  playerscore = 0;
  computerscore = 0;
}

There we go! Any time we want to run those instructions, instead of typing out all 3 of those lines, we can save time by typing resetgame() instead!

Back down near the bottom, add the resetgame() function where you change gamestate to 0! :slight_smile:

If you got confused, here’s the full code!

//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy2.h>
Arduboy2 arduboy;
 
//Variables declared here
int gamestate = 0;
int ballx = 62;
int bally = 0;
int ballsize = 4;
int ballright = 1;
int balldown = 1;
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;
int computery = 0;
int computerx = 127 - paddlewidth;
int playerscore = 0;
int computerscore = 0;

void resetGame() {
  ballx = 63;
  playerscore = 0;
  computerscore = 0;
}

void setup() {
  arduboy.begin();
  //Seed the random number generator
  arduboy.initRandomSeed();
  //Set the game to 60 frames per second
  arduboy.setFrameRate(60);
  arduboy.clear();
}

void loop() {

  //Prevent the Arduboy from running too fast
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  arduboy.pollButtons();

  //Game code here
  switch( gamestate ) {

    case 0:
      //Title screen
      arduboy.setCursor(0, 0);
      arduboy.print("Title Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 1;
      }
      break;

    case 1:
      //Gameplay screen
      //Display the player's score
      arduboy.setCursor(20, 0);
      arduboy.print(playerscore);
      //Display the computer's score
      arduboy.setCursor(101, 0);
      arduboy.print(computerscore);
      //Draw the ball
      arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE);
      //Move the ball right
      if(ballright == 1) {
        ballx = ballx + 1;
      }
      //Move the ball left
      if(ballright == -1) {
        ballx = ballx - 1;
      }

      //Move the ball down
      if(balldown == 1) {
        bally = bally + 1;
      }
      //Move the ball up
      if(balldown == -1) {
        bally = bally - 1;
      }
      //Reflect the ball off of the top of the screen
      if(bally == 0) {
        balldown = 1;
      }
      //Reflect the ball off of the bottom of the screen
      if(bally + ballsize == 63) {
        balldown = -1;
      }
      //Draw the player's paddle
      arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
      //If the player presses Up and the paddle is not touching the top of the screen, move the paddle up
      if(arduboy.pressed(UP_BUTTON) && playery > 0) {
        playery = playery - 1;
      }
      //If the player presses down and the paddle is not touching the bottom of the screen, move the paddle down
      if(arduboy.pressed(DOWN_BUTTON) && playery + paddleheight < 63) {
        playery = playery + 1;
      }
      //Draw the computer's paddle
      arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
      //If the ball is higher than the computer's paddle, move the computer's paddle up
      if (ballx > 115 || random(0, 20) == 1) {
        if (bally < computery) {
          computery = computery - 1;
        }
        //If the bottom of the ball is lower than the bottom of the computer's paddle, move the comptuer's paddle down
        if (bally + ballsize > computery + paddleheight) {
          computery = computery + 1;
        }
      }
      //If the ball moves off of the screen to the left...
      if(ballx < -10) {
        //Move the ball back to the middle of the screen
        ballx = 63;
        //Give the computer a point
        computerscore = computerscore + 1;
      }
      //If the ball moves off of the screen to the right....
      if(ballx > 130) {
        //Move the ball back to the middle of the screen
        ballx = 63;
        //Give the player a point
        playerscore = playerscore + 1;
      }

      //Check if the player wins
      if(playerscore == 5) {
        gamestate = 2;
      }
      //Check if the computer wins
      if(computerscore == 5) {
        gamestate = 3;
      }

      //If the ball makes contact with the player's paddle, bounce it back to the right
      if (ballx == playerx + paddlewidth && playery < bally + ballsize && playery + paddleheight > bally) {
        ballright = 1;
      }
      //If the ball makes contact with the computer's paddle, bounce it back to the left
      if (ballx + ballsize == computerx && computery < bally + ballsize && computery + paddleheight > bally) {
        ballright = -1;
      }
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        gamestate = 2;
      }
      break;
 
    case 2:
      //Win screen
      arduboy.setCursor(0, 0);
      arduboy.print("Win Screen"); 
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        resetGame();
        gamestate = 0;
      }
      break;

    case 3:
      //Game over screen
      arduboy.setCursor(0, 0);
      arduboy.print("Game Over Screen");
      //Change the gamestate
      if (arduboy.justPressed(A_BUTTON)) {
        resetGame();
        gamestate = 0;
      }
      break;
  }

  arduboy.display();
}

Wow!

Give yourself a pat on the back! Good job with this one! It was a long tutorial, but if you made it this far, you really deserve some kind of prize! :slight_smile: You are definitely ready to start experimenting and making your own game!

Next Tutorial

In the next tutorial, we start a HUGE game called DinoSmasher! Check it out, here: PART 8

Credits

I wrote this tutorial in order to give back to the programming community that taught me to get into it about 10 years ago. If you’d like to follow me on Twitter, please do so at http://www.twitter.com/crait . I’d greatly appreciate that. :smile:

7 Likes

This was so helpful to someone totally new to Arduboy like me! I really learned somthing:D Thank you so much!!!

4 Likes

Hi all,
This test it out code is missing arduboy.pollButtons().
I figured this out from the other tutorials before it.
Thank you for those!

1 Like

Where exactly? I can see an arduboy.pollButtons() in at least three of the code blocks.

1 Like

I think @grondak means the first full listing under section 2 … it is preceded with the words:

Obviously @crait or an admin will need to change it.

2 Likes

Just got my Arduboy and did your tutorial, thanks so much! Really easy to follow and it’s definitely got me excited to do more :grinning:

2 Likes

The teach is very thorough, and it can understand it at a glance. Thank you for helping me a lot!

1 Like

Thanks a lot @crait. Your tutorial is very helpful.
I complete my code with some upgrade. Let me show…

    //Balazs Sinko
    //October 10th, 2020
    //Pong game

    #include<Arduboy2.h>
    Arduboy2 arduboy;

    const uint8_t PROGMEM logo[] = {
    64, 25,
    0x00, 0xe0, 0x18, 0x04, 0x06, 0x02, 0x01, 0xc1, 0xa1, 0xe1, 0x01, 0x01, 0x02, 0x06, 0x0c, 0xfe, 0x02, 0x03, 0x01, 0x01, 0x03, 0x02, 0x06, 0x0c, 0x1c, 0x76, 0xc1, 0x01, 0x01, 0x03, 0x0e, 0x3c, 0x70, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0xf0, 0x1e, 0x03, 0x01, 0x01, 0x03, 0x0e, 0xfc, 0x06, 0x02, 0x02, 0x03, 0x01, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x82, 0xc2, 0xe2, 0xfc, 0xf0, 0xe0, 0x80, 
    0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0x98, 0x08, 0x0c, 0x06, 0x03, 0x00, 0xf8, 0xfc, 0x7c, 0x38, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xc7, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x07, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc7, 0xc7, 0x87, 0x07, 0x07, 0x0f, 0x0f, 0x1f, 0x17, 0xe7, 0x87, 0x03, 0x01, 
    0x01, 0x1f, 0x70, 0xc0, 0xc0, 0xc0, 0xe0, 0xf8, 0xff, 0xff, 0x3f, 0x03, 0x0c, 0x18, 0x30, 0x60, 0x40, 0xc1, 0xc3, 0xc2, 0xc3, 0xc1, 0xc0, 0xe0, 0xf8, 0xfe, 0x61, 0x40, 0x40, 0x40, 0xc0, 0xfe, 0xff, 0xfe, 0xfc, 0xf0, 0x70, 0x20, 0x60, 0xc0, 0xc0, 0xc0, 0xf0, 0xfe, 0xf9, 0xe0, 0x60, 0x20, 0x40, 0x40, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xe0, 0xf0, 0xfe, 0x7f, 0x7f, 0x3f, 0x1c, 
    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
    }; //PONG sprite

    const uint8_t PROGMEM spike[] = {
    5, 4,
    0x08, 0x08, 0x05, 0x02, 0x02, 
    }; //spike sprite

    const uint8_t PROGMEM boom[] = {
    8, 8,
    0x66, 0xd1, 0x05, 0x00, 0x80, 0x92, 0xc1, 0x23, 
    }; //ball explosion sprite

    int gameState = 0;
    float ballX = 64;           //ball initial X position
    float ballY = 32;           //ball initial Y position
    float gameSpeed = 0.4;      //game speed (on level 1)
    float ballDX = gameSpeed;   //ball speed - X direction
    float ballDY = gameSpeed;   //ball speed - Y direction
    int paddleHeight = 14;      //paddle height (on level 1)
    int playerY = 0;            //player paddle initial Y position
    float computerY = 0;        //computer paddle initial Y position
    int playerScore = 0;        //player initial score
    int computerScore = 0;      //computer initial score
    float factor = 1;           //bounce direction modifier initial value (=1 no change, <1 more flat, >1 more steep)
    int level = 1;              //initial level

    void resetGame(){           //variables set to original at game restart
      ballX = 64;
      ballY = 32;
      playerScore = 0;
      computerScore = 0;
      ballDX = gameSpeed;
      ballDY = gameSpeed;
      factor = 1;
      level = 1;
      paddleHeight = 14;
    } 

    void bounceVariation(){                                                                              //ball bounce direction modifier algorythm - just playing with the speed in the Y direction
      factor = 1 + 0.1 * random(-3, 4);                                                                  //random factor (0.7 - 1.3)
      if((abs(ballDY / ballDX)  < 0.8 && factor < 1) || abs((ballDY / ballDX) > 1.25 && factor > 1)){    //if the alignment is too flat or steep and the factor make it worse then it change the factor to make opposite effect
        factor = 1 / factor;
      }
      ballDY = ballDY * factor;                                                                          //apply the factor on the Y direction speed 
    } 

    void setup() {
      arduboy.begin();
      arduboy.initRandomSeed();
      arduboy.clear();
      arduboy.setFrameRate(60);
      }

    void loop() {
      if (!arduboy.nextFrame()){
        return;
      }
      arduboy.clear();
      arduboy.pollButtons();
      
      switch (gameState){
        case 0:                                                     //initial display
          Sprites::drawOverwrite(30, 12, logo, 0);                  //PONG logo
          arduboy.setCursor(20, 55);
          arduboy.print("Select level: ");                          //ask level selection
          arduboy.print(level);
          if (arduboy.justPressed(UP_BUTTON) && level < 3){         //up button add +1 level up to 3
            level = level + 1;
          }
          if (arduboy.justPressed(DOWN_BUTTON) && level > 1){       //down button lowers the level 
            level = level -1;
          }
          if (arduboy.justPressed(A_BUTTON)){                       //level selection confirmation with A button
            ballDX = ballDX * (1+ 0.5 * (level-1));                 //increase the X direction speed based on selected level (0, 50, 100%)       
            ballDY = ballDY * (1+ 0.5 * (level-1));                 //increase the Y direction speed based on selected level (0, 50, 100%)
            paddleHeight = paddleHeight -(level - 1);               //lowers the paddles height based on selected level (0, -1, -2)
            gameState = 1;                                          //switch to game start
          }
          break;

        case 1:                                                     //game display
          //display scoring
          arduboy.setCursor(20, 2);                                 
          arduboy.print(playerScore);
          arduboy.setCursor(101, 2);
          arduboy.print(computerScore);

          //frame drawing                                             
          for (int i = 0; i < 65; i = i +4){
            Sprites::drawOverwrite(0, i, spike, 0);                //spikes arrayed on the left side
            Sprites::drawOverwrite(122, i, spike, 0);              //spikes arrayed on the right side      
          }
          arduboy.drawLine (0, 0, 127, 0, WHITE);                  //upper frame
          arduboy.drawLine (0, 63, 127, 63, WHITE);                //bottom frame
                
          //ball drawing
          arduboy.drawCircle(ballX, ballY, 2, WHITE);                                         //drawing of the ball
          ballX = ballX + ballDX;                                                             //add the X speed to the X position
          ballY = ballY + ballDY;                                                             //add the Y speed to the Y position
          if (ballX <= 7 && playerY < ballY + 2 && playerY + paddleHeight > ballY - 2){       //ball collision detection with the player's paddle
            ballDX = abs(ballDX);                                                             //the ball X direction is changed to positive, so the ball is bounced on the paddle
            bounceVariation();                                                                //calling of the ball bounce direction modifier algorythm
          }
          if (ballX >= 120 && computerY < ballY + 2 && computerY + paddleHeight > ballY -2){  //ball collision detection with the computer's paddle
            ballDX = -abs(ballDX);                                                            //the ball X direction is changed to negative, so the ball is bounced on the paddle
            bounceVariation();                                                                //calling of the ball bounce direction modifier algorythm
          }
          if (ballY <= 3 || ballY >= 60){                                                     //collision detection on the top and the bottom
            ballDY = -ballDY;                                                                 //the ball Y direction inverterted, so the ball is bounced
          }      
           
          //player paddle drawing 
          arduboy.drawRect(0, playerY, 5, paddleHeight, WHITE);                               //player paddle is a rectangle                     
          if (arduboy.pressed(UP_BUTTON) && playerY > 0){                                     //if up button pressed the paddle moves up (until reach 0)
            playerY = playerY - 1;      
          }
          if (arduboy.pressed(DOWN_BUTTON) && playerY + paddleHeight < 63){                   //if down button pressed the paddle moves own (until reach the bottom)
            playerY = playerY + 1; 
          }

          //computer paddle drawing
          arduboy.drawRect(122, computerY, 5, paddleHeight, WHITE);
          if (ballX > 105 - 15 * (level-1) || random(0, 22)==1){                              //this sets from which X coordinate starts the computer follow the ball (depend on the level: 105, 90, 75), plus add random following factor
           if (computerY > ballY){                                                            //if the ball is higher than the computer's paddle
              computerY = computerY - (1 + 0.25 * (level - 1)) * gameSpeed;                   //the paddle goes higher (follow the ball), the following speed depend on the selected level (100, 125, 150%) and of course on the general gamespeed
            }
            if (computerY + paddleHeight < ballY + 4){                                        //if the ball is lower than the computer's paddle
             computerY = computerY + (1 + 0.25 * (level - 1)) * gameSpeed;                    //the paddle goes lower (follow the ball), the following speed depend on the selected level (100, 125, 150%) and of course on the general gamespeed
            }
          }
          
          if (ballX < 6){                                                                     //when the ball moves to the left and doesn't bounced on the player's paddle, then it will reach the spikes
            Sprites::drawOverwrite(ballX - 2, ballY - 2, boom, 0);                            //the ball changed to a small explosion drawing
            arduboy.display();                                                                
            delay(1000);                                                                      //little pause
            ballX = 64;                                                                       //ball initial position
            ballDX = -ballDX;                                                                 //ball goes to the computer direction
            computerScore = computerScore + 1;                                                //computer gain 1 point
          }
          
          if (ballX > 121){                                                                   //ball reach the spikes on the computers side
            Sprites::drawOverwrite(ballX - 2, ballY - 2, boom, 0);                            //explosion
            arduboy.display();
            delay(1000);
            ballX = 63;                                                                       //ball initial position        
            ballDX = -ballDX;                                                                 //ball goes to the player direction
            playerScore = playerScore + 1;                                                    //player gain 1 point
          }
          if (computerScore == 5){                                                            //if computer reach 5 points
            gameState = 3;                                                                    //call "YOU LOST" display
          }
          if (playerScore == 5){                                                              //if player reach 5 points
            gameState = 2;                                                                    //call "YOU WON" display
          }
          break;

        case 2:                                                                               //"YOU LOST" display
          arduboy.setCursor (random (38, 42), random(22, 26));                                //text in diferent random positions (look like vibrating)
          arduboy.print("YOU WON");
          if (arduboy.justPressed(A_BUTTON)){
            resetGame();
            gameState = 0;                                                                    //A button calls the initial display
          }
          break;

        case 3:                                                                               //"YOU LOST" display
          arduboy.setCursor (random (38, 42), random(22, 26));                                //text in diferent random positions (look like vibrating)
          arduboy.print("YOU LOST");
          if (arduboy.justPressed(A_BUTTON)){
            resetGame();
            gameState = 0;                                                                    //A button calls the initial display
          }
          break;
      }
      arduboy.display();                                                                     
    }

Thank you for this excellent tutorial. I learned a lot from following it and experimenting a bunch along the way as well.

In the original post under “You’re ready!” it says:

but in the setup example below it it says:

arduboy.initRandomSeed();

which is what is used in part 5 when etting up the random number. When would that ‘srand’ code be used?

A quick crash course in pseudorandom number generation:

arduboy.initRandomSeed() uses srandom (a variant of srand) internally, and is the preferred way to initialise the global pseudorandom number generator (commonly ‘PRNG’).

In software, numbers are rarely ever truly random. Instead they are usually ‘pseudorandom’ numbers that are generated from a ‘seed’ value using an algorithm of some kind (e.g. xorshift).

Modern computers often do have a ‘true’ random number generator implemented as part of the hardware, but usually the values these produce are only used as the seed value for a PRNG rather than being used exclusively.

The Arduboy does not have a hardware random number generator, so arduboy.initRandomSeed() initialises the global random seed used by random with a value derived from pin noise and elapsed time.

srand and rand are what the C standard library provide, but because they are required to use int the AVR-specific implementation that Arduino relies upon also provides srandom and random, which use long instead (because on AVR int is 16-bit and long is 32-bit).


Just to point it out: 7/8 is actually a bit pointless here because it would evaluate to 0 (because this is integer division, not decimal division) and 0 is actually the one value that should usually never be used to seed a PRNG purely because for many PRNG algorithms it prevents the algorithm from working properly because 0 is the only value with no bits set (i.e. no 1 bits) and many PRNGs rely on the seed/state value having at least some set bits.

Seeding with a constant is a bad idea anyway, unless you specifically want the same pseudorandom sequence to be generated each time, e.g. for debugging purposes or procedural generation.

1 Like

I completed this and made a few simple modifications of my own using what I’ve learned so far. Thought I’d post them here if anyone is interested.

I first added a line down the centre of the screen, addding this code in case 1 after printing the scores:

arduboy.fillRect(64, 0, 2, 64, WHITE);

I then added a new game state to pause the game, adding this code at the end of case 1

if (arduboy.justPressed(A_BUTTON)) {
gamestate = 4;
}

and adding this new case after case 3:

    case 4:
    arduboy.setCursor(46, 28);
    arduboy.print("Paused");
    if (arduboy.justPressed(A_BUTTON)) {
            gamestate = 1;
    }
    break;

I decided to display the scores on the win and game over screens. Since this code will appear 3 times now I turned it into a function. I added this code under the reset game function:

void printscores() {
    arduboy.setCursor(20, 0);
    arduboy.print(playerscore);
    
    arduboy.setCursor(101, 0);
    arduboy.print(computerscore);
}

Then replaced the code for printing the score at the top of case 1 with:

printscores();

And here’s my code for case 2 & 3 with the scores present and centered text:

    case 2:
    
    printscores();
    
    arduboy.setCursor(40, 28);
    arduboy.print("You Win!");
    if (arduboy.justPressed(A_BUTTON)) {
      resetgame();
      gamestate = 0;
    }
    break;

    case 3:
    
    printscores();
    
    arduboy.setCursor(34, 28);
    arduboy.print("Game Over!");
    if (arduboy.justPressed(A_BUTTON)) {
      resetgame();
      gamestate = 0;
    }
    break;

Finally, I decided to add the option for the player to change the winning score. I started by adding a new integer, keeping the default as 5:

int winningscore = 5;

I then edited my Title screen (case 0) and included a number which can be changed with the up and down arrows to change the winning score. I kept the minimum at 5 and made a maximum of 15:

    case 0:
    arduboy.setCursor(49, 12);
    arduboy.print("PING!");
    arduboy.setCursor(0, 32);
    arduboy.print("Set winning score: ");                                 
    arduboy.print(winningscore);
    arduboy.setCursor(20, 52);
    arduboy.print("Press A to Start");
    if (arduboy.justPressed(A_BUTTON)) {
      gamestate = 1;
    }
    if (arduboy.justPressed(UP_BUTTON) && winningscore < 15) {
      winningscore = winningscore + 1;
    }
    if (arduboy.justPressed(DOWN_BUTTON) && winningscore > 5) {
      winningscore = winningscore - 1;
    }
    break;

Finally I changed the code which sets the winning score at the end of case 1 to use my new variable rather than the set value of 5:

    if (playerscore == winningscore) {
      gamestate = 2;
    }
    if (computerscore == winningscore) {
      gamestate = 3;
    }

What do you think of my changes?

I think they made the over-all game a little better and more finished. It was also fun, personaly, to see if I could use what I learned to improve the game a little without prompts from the tutorials.

2 Likes

Couldn’t help but mod it a little more to adjust the difficulty.

I noticed that the AI was bad at catching the ball after missing it once which would often result in the player gaining multiple points at once, sometimes even just as the game starts so I spawned the ball another 1/4 of the screen away at the start of the game and also when a point is scored instead of right in the centre. This also makes it a little easier for theplayer as it spawns further for them too and makes it feel a little more like the other player has served. I changed the numbers in the following code:

int ballx = 30;
void resetgame() {
      ballx = 30;
      playerscore = 0;
      computerscore = 0;
}
    if (ballx < -10) {
      ballx = 94;
      computerscore = computerscore + 1;
    }
    if (ballx > 130) {
      ballx = 30;
      playerscore = playerscore + 1;
    }

I also made the AI’s reaction time a little quicker when the ball is heading towards them and added an extra random number in there for movement. This made the multiple scores when it missed less frequent, though still possible, and makes the computer’s paddle look a bit less jittery. I at first tried ‘ballx > 90’ but that made the game too hard but settled on this code:

if (ballx > 110 || random(0,20) == 1 || random(0,20) == 5) {

After this I noticed that the first 3 shots were almost always a miss giving the player a 3 point start right away, so I made the two paddles start in the centre instead of the top.

int playery = 28;
int computery = 28;

I think I’m done with modding this one for now. That adds enough extra difficulty to make it more challenging but it’s still easy enough to beat, although the computer proabaly still misses two times in a row a little more often than it should. I’ll maybe add more to it if I learn something else I want to add in later tutorials.

1 Like

Correct, the first complete program listing in section 2 - Structuring the game.

Thanks for the tutorial, I learned a lot from it and inspire me to write my own game