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


(Holmes) #1

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 and 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 <Arduboy.h>
Arduboy 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.

Framerates

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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	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.pressed(A_BUTTON)) {
			gamestate = 1;
		}
		break;
	case 1:
		//Gameplay screen
		arduboy.setCursor(0, 0);
		arduboy.print("Gameplay");
		if(arduboy.pressed(A_BUTTON)) {
			gamestate = 2;
		}
		break;
	case 2:
		//Win screen
		arduboy.setCursor(0, 0);
		arduboy.print("Win Screen");
		if(arduboy.pressed(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!

Oops! Too Fast!

Does that look familiar? When you test this out, the gamestate is changing too fast! We went over this in the 5th part of this tutorial series.

We want the Arduboy to know if the A button is being pressed or if it’s being held down. If it’s held down, If it’s being held down, we need to ignore it. That way, the Arduboy won’t go through the different cases too fast.

Last time, we used a buffer variable. Let’s make a new one in the variables area.

int justpressed = 0;

Whenever we push a button, we’ll change justpressed to 1. Whenever we let go of the button, we’ll change justpressed back to 0.

If we are holding down the A button, we’ll be pressing down the A button and justpressed will be 1. On the other hand, the first moment you push the A button, you’ll be pressing the A button and justpressed will be 0.

When we check if the A button is checked…

if(arduboy.pressed(A_BUTTON)) {

… let’s change it to…

if(arduboy.pressed(A_BUTTON) and justpressed == 0) {

Of course, if we need to add a line right after it to change justpressed.

if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
	justpressed = 1;

Replace all of them and after your switch, add this:

if(arduboy.notPressed(A_BUTTON)) {
	justpressed = 0;
}
//Jonathan Holmes (crait)
//December 7th, 2016
//A simple Pong clone

#include <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 0;

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 1;
			}
			break;
		case 1:
			//Gameplay screen
			arduboy.setCursor(0, 0);
			arduboy.print("Gameplay");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	arduboy.display();
}

Test out the code and let’s finally move on to programming the gameplay section of this game! It’s definitely the most fun! Sorry that it’s been taking so long to get to!

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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	arduboy.display();
}

Make Your Own Arduboy Game: Part 6 - Graphics!
Pong DX - variations on the Pong tutorial
How do I make a solid object
Adding sound - error 48 [Solved]
Super Crate for the Arduboy? [Solved]
Make Your Own Arduboy Game: Part 8 - Starting DinoSmasher
First game - Rabbit Catch
Make Your Own Arduboy Game: Part 9 - Mapping DinoSmasher
How difficult is creating a game, for an absolute beginner?
(Holmes) #2

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;
}

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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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) and 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) and playery + paddleheight < 63) {
				playery = playery + 1;
			}
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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 computerx = 127 - paddlewidth;
int computery = 0;

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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) and 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) and 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.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	arduboy.display();
}

Notice how good the AI is. The computer’s paddle is 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 and playery < bally + ballsize and 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 and computery < bally + ballsize and 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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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 computerx = 127 - paddlewidth;
int computery = 0;

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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) and 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) and 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 and playery < bally + ballsize and 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 and computery < bally + ballsize and computery + paddleheight > bally) {
				ballright = -1;
			}
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	arduboy.display();
}

(Holmes) #3

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 and 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 or 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!

Modulo Operation

Do you know the answer to 12 ÷ 6? The answer is 2! That was easy, right? Okay, what about 23 ÷ 5? The answer is either 4.6 or 4 with a remainder of 3. Which is the correct answer??

If you asked the Arduboy what 23 ÷ 5 was, the answer would be 4! That seems wrong! Where did the remainder go?? We need give the Arduboy another instruction to figure out the remainder.

Remember when I said that computers have to be very precise when given instructions? When computers were young, people tried to come up with the best way to do devision. There were a lot of ideas, but the best way seemed to be introducing the modulo operator.

To get the remainer, we ask the Arduboy for the answer of 23 % 5. It will output 3! If you asked 12 % 6, you would get 0!

Why is this important? Well, we can use this to figure out more complex math, but there’s something special in game development that we can use this for.

Anytime you use two numbers with the modulo operator, the answer will be somewhere between 0 and the second number. a % b will never be a number more than b.

Keep this in mind and we’ll do something really fun with it later!

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. I’m going to explain to you how that works, now.

First of all, we need to know about the rand() function. This will give us a random number, but the number can really be anything. It can be 2,000, or 9 or -200. There’s really no control over it. HOWEVER, we can use the modulo operator that we learned about earlier!! Take a look at this code and think about what this will output!!

rand() % 20

Well, we’ll randomly pick a number, then divide it by 20, then only have the remainder left. It doesn’t matter what number is randomly picked, if we just looked at the remainder, the remainder will be some number between 0 and 20. Which number? I don’t know. Isn’t that awesome?! :laughing:

That little line of code will essentially pick a random number between 0 and 20!

Let’s adjust that if statement from above!

if(ballx > 115 or rand() % 20 == 1) {

This means that if ballx is greater than 115, or a randomly-picked number is equal to 1, then we move the comptuer’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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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 computerx = 127 - paddlewidth;
int computery = 0;

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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) and 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) and playery + paddleheight < 63) {
				playery = playery + 1;
			}
			//Draw the computer's paddle
			arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
			//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
			if(ballx > 115 or rand() % 20 == 1) {
				//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 and playery < bally + ballsize and 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 and computery < bally + ballsize and computery + paddleheight > bally) {
				ballright = -1;
			}
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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 computerx = 127 - paddlewidth;
int computery = 0;
int playerscore = 0;
int computerscore = 0;

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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) and 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) and playery + paddleheight < 63) {
				playery = playery + 1;
			}
			//Draw the computer's paddle
			arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
			//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
			if(ballx > 115 or rand() % 20 == 1) {
				//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 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 and playery < bally + ballsize and 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 and computery < bally + ballsize and computery + paddleheight > bally) {
				ballright = -1;
			}
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 3;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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 computerx = 127 - paddlewidth;
int computery = 0;
int playerscore = 0;
int computerscore = 0;

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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) and 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) and playery + paddleheight < 63) {
				playery = playery + 1;
			}
			//Draw the computer's paddle
			arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
			//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
			if(ballx > 115 or rand() % 20 == 1) {
				//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 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 and playery < bally + ballsize and 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 and computery < bally + ballsize and computery + paddleheight > bally) {
				ballright = -1;
			}
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	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 <Arduboy.h>
Arduboy arduboy;

//Variables declared here
int gamestate = 0;
int justpressed = 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 computerx = 127 - paddlewidth;
int computery = 0;
int playerscore = 0;
int computerscore = 0;

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

void setup() {
	arduboy.begin();
	//Seed the random number generator
	srand(7/8);
	//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
	switch( gamestate ) {
		case 0:
			//Title screen
			arduboy.setCursor(0, 0);
			arduboy.print("Title Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				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) and 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) and playery + paddleheight < 63) {
				playery = playery + 1;
			}
			//Draw the computer's paddle
			arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);
			//If the ball is close to the edge of the screen or if a random number out of 20 is equal to 1
			if(ballx > 115 or rand() % 20 == 1) {
				//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 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 and playery < bally + ballsize and 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 and computery < bally + ballsize and computery + paddleheight > bally) {
				ballright = -1;
			}
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				gamestate = 2;
			}
			break;
		case 2:
			//Win screen
			arduboy.setCursor(0, 0);
			arduboy.print("Win Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				resetgame();
				gamestate = 0;
			}
			break;
		case 3:
			//Game over screen
			arduboy.setCursor(0, 0);
			arduboy.print("Game Over Screen");
			//Change the gamestate
			if(arduboy.pressed(A_BUTTON) and justpressed == 0) {
				justpressed = 1;
				resetgame();
				gamestate = 0;
			}
			break;
	}
	//Check if the button is being held down
	if(arduboy.notPressed(A_BUTTON)) {
		justpressed = 0;
	}
	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:


Displays and Decimals?
(Scott) #4

Nice work @crait!
Just a couple of suggestions:

Since this is an Arduino based system, you should be using the Arduino functions for random numbers: randomSeed() instead of srand() and random() instead of rand().

Also, using srand() or randomSeed() with a constant value for the parameter will always position the pseudorandom number generator to the same position in its fixed sequence. The game will always start at, and generate, the same sequence of random numbers, so it really serves no purpose in this case. If you want to get a different set of (pseudo)random numbers every time the game starts after a power on, instead of using srand() or randomSeed() there is a library function, initRandomSeed() to do this.

By using the Arduino random() function instead of rand() you don’t need to use the modulo operator. The random() function allows you specify the range of numbers you want it to generate.


It’s not very good programming practice to hard code the screen dimensions in your calculations. It doesn’t make things very obvious, requiring more reliance on commenting, and also makes it difficult to modify the game for a different screen size in the future.

The library provides the defined values HEIGHT and WIDTH for the screen dimensions, in pixels. Even if these weren’t provided, it would be better to define them yourself at the start of the sketch instead of using numeric values throughout.

Using available pre-defined constants, or defining them yourself at the start of the program or in a header, makes the code more readable and easier to modify if a value needs to be changed.


(Holmes) #5

Hey! Thanks for the feedback! There was a lot of stuff I wanted to incorporate into this tutorial, but it was already sooo long, I didn’t want to talk about too much.

In the next tutorial, I’m going to go over a lot of stuff, including keywords like that. It’ll be a short tutorial, but the following will be revisiting this Pong game and fixing some of the issues, including that, as well as giving this game a few more features.

As for the random functions, I’m not too familiar with the Arduino functions, so I’ll look into changing this tutorial after I read up on those!

Again, thanks for the feedback!


(Scott) #6

It’s always a good idea to research if there is an “Arduino” way of doing something instead of using the C++ standard library functions. For instance a web search for arduino random will find the info I gave above.

It also can’t hurt to skim through the Arduino Language Reference before doing any Arduino specific programming.


#7

This is great, thanks!


#8

So, coming back to this a day later - we can write our own functions after declaring variables and we can reference them when we need them? For example, I could write a collision function and define some parameters and then reference it whenever I need to? In this game for example, perhaps I could create a collision function and then apply it to the ball and paddles?

Thanks again for these tutorials, I have really leveled up this weekend.


(Holmes) #9

Yes, exactly! As I said above, we’ll revisit this game and re-write some with the new stuff we learn. :slight_smile: If you’re feeling up for it, you could even try doing it yourself! :smiley:


#10

:open_mouth: Amazing. I will try.


(Mathias Zillmann) #11

Amazing work!!! Easy to understand and follow. Thanks for putting this much effort into this!


(Tom Sparrow) #12

Brilliant tutorial, thank you very much for taking the time to write these.
w̶h̶e̶n̶ ̶I̶’̶v̶e̶ ̶f̶i̶n̶i̶s̶h̶e̶d̶ If I finish circuit dude I will give them a go.
Cheers!


(Holmes) #13

Haha! Love the feedback you’ve been posting so far on Twitter. :smiley: One of the best features of the Arduboy is that you can change the game and save your progress. Feel free to take a crack at them anytime. :wink:


(yoz) #14

Thanks for the tutorial!
I’ve added two improvements in my version of the game.

  1. I’ve added small borders at the top and the bottom edges of screen. It is hard to see where the ball bounces back because of the black space around the screen.

  2. Also the screen has 1:2 proportions, that’s why the ball goes through nearly the same path.
    I’ve moved the paddles from the edge by 5 px. It’s a little changed the proportions of the playing area and the ball’s trajectory.


(Holmes) #15

Love it, @yoz! Glad you are following enough to be able to implement your changes! I definitely encourage anyone to try to mix it up and make it their own. That is what another tutorial I have planned will be all about! :wink:


(John) #16

Love the tutorials! I added a skill level to mine which changed the distance before the ai would react and the range of the random number. For a next/future tutorial I would like to see you combine lessonsland make a “simple” character game using an image for the character/sprite and a background with stuff to avoid.


#17

Heya crait, I’m sure you’re super busy, but will you be continuing this series soon? It’s great!


(Holmes) #18

I’m working on releasing new version of my Arduboy Manager program. I just finished fixing the latest bugs, so if it’s the bug testers are good with it, I’ll be releasing it in the next few days. As you said, I’m a little busy, but I’ll try to get at least one up in the next week and another the following week. :slight_smile:


#19

Awesome, thanks! I’m pretty busy this month too, so another tutorial of yours would be great on a weekend or something like that.


(ET) #20

WOW just got all 7 done - Thanks for this series it really got me to not just like the Arduboy but fall in love with this lil gadget. I cant wait to try to make a game.