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


(Scott) #21

This instructional series is a bit dated. It uses the original Arduboy library, which is no longer being developed. If you start developing your own game(s), I suggest you use the Arduboy2 library.

For all the code in this series, you just have to change the lines near the start:

#include <Arduboy.h>
Arduboy arduboy;

to:

#include <Arduboy2.h>
Arduboy2 arduboy;

(ET) #22

Dang ok… updating main file template now. TY


#23

Thanks @crait for these tutorials, I worked through them and the comments in a couple of days and I’m off to my first coding project, meaning, great job on your part, getting folks up and running quickly! Also, I’m now all Twittered up and following per your request, thanks again.


(Holmes) #24

Awesome, dude! Thanks for that! I’m trying to grow my online presence, but I also post on there when I put new tutorials up. This week is super hectic for me since I’m in California for GDC, but as soon as I get back to Texas, I plan on pumping out a few much-needed tutorials that go over the more important details about making your own games. These tutorials are good for beginners, but there’s a lot of ways to condense the code down or program these “better.” I want to make sure I let you guys know about the stuff I’ve skipped over.


(curly) #25

great guide , would like to see it updated a hair though


(curly) #26

what if the " if " requires one or more && 's
if ((cats == 5) && (dogs == 6) && (animals <= 11)) {

i dont know what the hell youd use that line of code for, but im too lazy to dig up an example from my poorly coded work


(Holmes) #27

What you did was fine!


(curly) #28

heres an example from my game Tic Tac Curly

``void VooDoo() {
emove = random(1, 10) ;

if ((emove == 1) && (a1 == 0)) {
 emove = 0;
 turn += 1;
 a1 = 2;

}
else if ((emove == 2) && (a2 == 0)) {
emove = 0;
turn += 1;
a2 = 2;

}
else if ((emove == 3) && (a3 == 0)) {
emove = 0;
turn += 1;
a3 = 2;

}
else if ((emove == 4) && (b1 == 0)) {
emove = 0;
turn += 1;
b1 = 2;

}
else if ((emove == 5) && (b2 == 0)) {
emove = 0;
turn += 1;
b2 = 2;

}
else if ((emove == 6) && (b3 == 0)) {
emove = 0;
turn += 1;
b3 = 2;

}
else if ((emove == 7) && (c1 == 0)) {
emove = 0;
turn += 1;
c1 = 2;

}
else if ((emove == 8) && (c2 == 0)) {
emove = 0;
turn += 1;
c2 = 2;

}
else if ((emove == 9) && (c3 == 0)) {
emove = 0;
turn += 1;
c3 = 2;

}
}``


(Holmes) #29

You could do something like this…

if(cats == 5) {
	if(dogs == 100) {
		switch(animals) {
			case 10:
				break;
			case 11:
				break;
			case 12:
				break;
		}
	} else if(dogs == 30) {
		switch(animals) {
			case 10:
				break;
			case 11:
				break;
			case 12:
				break;
		}
	}
} else if(cats == 6) {
	if(dogs == 100) {
		switch(animals) {
			case 10:
				break;
			case 11:
				break;
			case 12:
				break;
		}
	} else if(dogs == 30) {
		switch(animals) {
			case 10:
				break;
			case 11:
				break;
			case 12:
				break;
		}
	}
} else (cats >= 7) {
	if(dogs == 100) {
		switch(animals) {
			case 10:
				break;
			case 11:
				break;
			case 12:
				break;
		}
	} else if(dogs == 30) {
		switch(animals) {
			case 10:
				break;
			case 11:
				break;
			case 12:
				break;
		}
	}
}

You could also switch up the order and put the switch() at the beginning. This, of course, all depends on what the program will actually do.


(curly) #30

the ppost above you has been edited to reflect more so what im asking

ive been told that if there is more than 1 or 2 else you should use a switch but does that apply to “else if”


(Holmes) #31

Oh, that’s easy. You can do something like this…

void VooDoo() {
	emove = random(1, 10) ;

	switch(emove) {
		case 1:
			if (a1 == 0) {
				emove = 0;
				turn += 1;
				a1 = 2;
			}
			break;
		case 2:
			if (a2 == 0) {
				emove = 0;
				turn += 1;
				a2 = 2;
			}
			break;
		case 3:
			if (a3 == 0) {
				emove = 0;
				turn += 1;
				a3 = 2;
			}
			break;
		case 4:
			if (b1 == 0) {
				emove = 0;
				turn += 1;
				b1 = 2;
			}
			break;
		case 5:
			if (b2 == 0) {
				emove = 0;
				turn += 1;
				b2 = 2;
			}
			break;
		case 6:
			if (b3 == 0) {
				emove = 0;
				turn += 1;
				b3 = 2;
			}
			break;
		case 7:
			if (c1 == 0) {
				emove = 0;
				turn += 1;
				c1 = 2;
			}
			break;
		case 8:
			if (c2 == 0) {
				emove = 0;
				turn += 1;
				c2 = 2;
			}
			break;
		case 9:
			if (c3 == 0) {
				emove = 0;
				turn += 1;
				c3 = 2;
			}
			break;
		}
}

(curly) #32

soooo find whats common make the switch out of that and put the ifs inside the cases


(Holmes) #33

Yep! Kind in mind that switch() cases use more memory than if statements, but in many circumstances, it’s way easier to read.


(curly) #34

well in that case i have no use for switch, im looking for ways to cut down on memory, still good to know thanks


(Katie Hiller Mc Bride) #35

Is there a next tutorial? I wasn’t able to find #8


#36

Thank you for the tutorial series, I did a bit each evening this week and finished up with Pong today. I had a great time and did a few variations of each stage. I found that writing all the code out myself helped it stick a lot more than copying & pasting.

I want to experiment with the gameplay a little. I think that at the moment after someone scores the horizontal direction of the ball is maintained. This can mean the computer loses a few points in a row depending on their paddle position. I’d like to try randomising the direction direction both at the start of the game and when the ball goes back to the centre after someone scores.

I’d also like to try iterating on it adding a few different modes:

  • 2 player on a single arduboy (up, down vs a,b)
  • different computer difficulties (easy, normal, hard)
  • endless rally (using the 1st version of the computer ai so it never loses)
  • mode select on the title screen

…and perhaps look at adding:

  • art for the title, win and lose screens
  • sounds (a “pong” when the ball pounces and perhaps bg music)
  • a “screensaver” with two computers playing an unending match
  • highscore save for the endless rally mode

#37

I have now had a little experiment and here is the code for my “PONG DX”.

Gameplay changes:

  1. The direction of the ball is randomised after each point is scored.
  2. The player paddle moves twice as fast (I prefer how this feels).
  3. The score is shown at the bottom of the screen with a slightly smaller play area that doesn’t overlap the score.

Additions:

  1. Title Screen with mode select
  2. Different gameplay modes (1 player, 2 player, endless)
  3. Text for the win/lose screens
    note: I moved chunks of the code to different functions as that helped my brain when I was editing it.

Gameplay modes:

  • 1 Player
    • you vs the CPU!
    • first to 10 wins the game
  • 2 Player
    • you vs a friend
    • first to 10 wins the game
    • player 1 is up/down
    • player 2 is a/b
    • designed for 2 people sitting facing each other with the Arduboy held sideways between them
  • Endless
    • the computer will return every shot without fail
    • you score a point for each shot you return
    • miss one and shot it’s all over!
    • gives your score and the current highscore after each game
    • note: the highscore is lost when the Arduboy is turned off

Here is the code!

// Tom Bedford (oldmantom)
// July 22nd 2017
// PONG DX
// variations on Jonathan Holmes (crait)'s Pong
// see: https://community.arduboy.com/t/make-your-own-arduboy-game-part-7-make-pong-from-scratch/2615

#include <Arduboy.h>
Arduboy arduboy;

// variables declared here
int gamestate = 0;
int gametype = 0;
int screenwidth = 127;
int screenheight = 47;

// button press buffers
int buffera = 0;
int bufferb = 0;
int bufferup = 0;
int bufferdown = 0;
int bufferleft = 0;
int bufferright = 0;

// ball variables
int ballx = 62; // ball x position
int bally = 0; // ball y position
int ballsize = 4; // ball size
int ballright = 1; // ball horizontal direction
int balldown = 1; // ball vertical direction

// paddle / player variables
int paddlewidth = 4;
int paddleheight = 9;
int playerx = 0;
int playery = 0;

// AI variables
int computery = 0; // default = top
int computerx = screenwidth - paddlewidth; // default = far right - paddlewidth

// scoring
int playerscore = 0;
int computerscore = 0;
int player2score = 0;
int neededtowin = 10; // score needed to win 1 or 2 player
int highscore = 0; // highscore for endless rally

void setup() {
  // start arduboy
  arduboy.begin();
  // seed the random number generator
  srand(7/8);
  // set the frame rate to 60 FPS
  arduboy.setFrameRate(60);
  // randomise the horizontal direction of the first ball for the game
  randomballright();
  // clear the screen
  arduboy.clear();
}

void loop() {
  // prevent the game from running too fast (i.e. if not ready to display next frame, wait)
  if(!arduboy.nextFrame()) {
    return;
  }
  arduboy.clear();
  
  // game code here
  switch(gamestate) {
    case 0:
      // title screen
      gameselect();
      break;
    case 1:
      // game screen
      gameplay();
      break;
    case 2:
      // win screen
      win();
      if(arduboy.pressed(A_BUTTON) and buffera == 0) {
        buffera = 1;
        resetgame();
        gamestate = 0;
      }
      break;
    case 3:
      // game over screen
      gameover();
      if(arduboy.pressed(A_BUTTON) and buffera == 0) {
        buffera = 1;
        resetgame();
        gamestate = 0;
      }
      break;
  }

  resetbuttonbuffers();

  arduboy.display();
}

void gameplay() {

  if (gametype == 0) {
    arduboy.setCursor(40, 52);
    arduboy.print(" VS CPU ");
  }
  if (gametype == 1) {
    arduboy.setCursor(40, 52);
    arduboy.print("   VS   ");
  }
  if (gametype == 2) {
    arduboy.setCursor(40, 52);
    arduboy.print("ENDLESS");
  }
  
  // display the player score
  arduboy.setCursor(4, 52);
  arduboy.print(playerscore);

  // display the computer/player2 score
  if (gametype == 0 or gametype == 1) {
    arduboy.setCursor(115, 52);
    arduboy.print(computerscore);
  }
  
  // draw the ball
  arduboy.fillRect(ballx, bally, ballsize, ballsize, WHITE); // draw the ball
  
  // horizontal movement
  if(ballright == 1) {
    ballx = ballx +1; // move right
  }
  if(ballright == -1) {
    ballx = ballx -1; // move left
  }

//  // horizontal bounces
//  if(ballx == 0) { // if at left edge of screen
//    ballright = 1; // move right
//  }
//  if(ballx + ballsize == screenwidth) { // if at right edge of screen
//    ballright = -1; // move left
//  }

  // vertical movement
  if(balldown == 1) {
    bally = bally + 1; // move down
  }
  if(balldown == -1) {
    bally = bally - 1; // move up
  }
  // vertical bounces
  if(bally == 0) { // if at top of screen
    balldown = 1; // move down
  }
  if(bally + ballsize == screenheight) { // if at bottom of screen
    balldown = -1; // move up
  }
  
  // draw the paddle
  arduboy.fillRect(playerx, playery, paddlewidth, paddleheight, WHITE);
  // move the paddle with arrows
  if (arduboy.pressed(UP_BUTTON) and playery > 0 ) { // if up pressed and not at top
    playery = playery - 2;
  }
  if (arduboy.pressed(DOWN_BUTTON) and playery + paddleheight < screenheight) { // if down pressed and not at bottom
    playery = playery + 2;
  }
  
  // draw the computer's paddle
  arduboy.fillRect(computerx, computery, paddlewidth, paddleheight, WHITE);

  // 1 Player (computer will lose sometimes)
  if (gametype == 0) {
    // computer AI (won't always win)
    if (ballx > 115 or rand()% 20 == 1) { // originally... ballx > 115 or rand()% 20 i.e. if ball on right or ~1/20 frames
      // if ball is...
      // ...towards right hand side of screen (i.e ballx > position on screen)
      // ...or if our random number is equal to 1 or random number of frames passed
      if(bally < computery ) { // if the ball is higher than the computer
        computery = computery - 2; // raise the computer paddle
      }
      if(bally + ballsize > computery + paddleheight ) { // if the ball is lower than the computer
        computery = computery + 2; // lower the computer paddle
      }
    }
  }

  // 2 Player (A and B control computer paddle)
  if (gametype == 1) {
    // move the paddle with A and B
    if (arduboy.pressed(B_BUTTON) and computery > 0 ) { // if B pressed and not at top
      computery = computery - 2; // raise the computer paddle
    }
    if (arduboy.pressed(A_BUTTON) and computery + paddleheight < screenheight) { // if A pressed and not at bottom
      computery = computery + 2;
    }
  } 

  // Endless Rally (computer will always win)
  if (gametype == 2) {
    // computer AI (will always win)
    if(bally < computery ) { // if the ball is higher than the computer
      computery = computery - 1; // raise the computer paddle
    }
    if(bally + ballsize > computery + paddleheight ) { // if the ball is lower than the computer
      computery = computery + 1; // lower the computer paddle
    } 
  }
  
  // if the ball moves off the left end of the screen...
  if (ballx < -10) {
    if (gametype == 2) {
      gamestate = 3; // lose screen - you lost the endless rally!
    }
    ballx = screenwidth / 2; // move the ball to the middle
    randomballright(); // randomise ball horizontal direction
    // scoring for 1 or 2 player 
    if (gametype == 0 or gametype == 1) {
      computerscore = computerscore + 1; // give the computer/player2 a point
    }
  }

  // if the ball moves off the right end of the screen...
  if (ballx > 130) {
    ballx = screenwidth / 2; // move the ball to the middle
    randomballright(); // randomise ball horizontal direction
    // scoring for 1 or 2 player 
    if (gametype == 0 or gametype == 1) {
      playerscore = playerscore + 1; // give the player a point
    }
  }

  // check if player has won
  if (playerscore == neededtowin) {
    gamestate = 2; // win screen
  }
  
  // check if computer has won
  if (computerscore == neededtowin) {
    gamestate = 3; // lose screen
  }
  
  // bounce off player paddle (left side)
  if (ballx == playerx + paddlewidth and playery < bally + ballsize and playery + paddleheight > bally) {
    // if the ball is at the horizontal position of the paddle...
    // ...and the ball is at the vertical position of the paddle...
    ballright = 1; // ...bounce off left paddle (i.e. move right)
    // scoring for endless rally (add 1 for each return shot)
    if (gametype == 2) {
      playerscore = playerscore + 1;
    }
  }
  // bounce of computer paddle (right side)
  if (ballx + ballsize == computerx and computery < bally + ballsize and computery + paddleheight > bally) {
    // if the ball is at the horizontal position of the computer paddle...
    // ...and the ball is at the vertical position of the computer paddle...
    ballright = -1; // ...bounce off right paddle (i.e. move left)
  }
}

void gameselect() {
  
  // print title
  arduboy.setCursor(0,0);
  arduboy.print("PONG DX");
  
  // show gametypes
  arduboy.setCursor(0,16);
  arduboy.print("  1 Player");
  arduboy.setCursor(0,32);
  arduboy.print("  2 Player");
  arduboy.setCursor(0,48);
  arduboy.print("  Endless");

  // set the cursor position for the gametype
  if (gametype == 0) {
    arduboy.setCursor(0,16);
  }
  if (gametype == 1) {
    arduboy.setCursor(0,32);
  }
  if (gametype == 2) {
    arduboy.setCursor(0,48);
  }

  // print the indicator for the gametype
  arduboy.print(">");

  // change gametype when up/down pressed
  if(arduboy.pressed(UP_BUTTON) and bufferup == 0 and gametype > 0) {
    bufferup = 1;
    gametype = gametype - 1;
  }
  if(arduboy.pressed(DOWN_BUTTON) and bufferdown == 0 and gametype < 2) {
    bufferdown = 1;
    gametype = gametype + 1;
  }
  
  // advance to gameplay if A pressed
  if(arduboy.pressed(A_BUTTON) and buffera == 0) {
    buffera = 1;
    gamestate = 1;
  }
  
}

void randomballright() {
  // randomise the horizontal direction of the ball
  int randomnumber = rand() % 2;
  if (randomnumber == 0) {
    ballright = 1;
  }
  if (randomnumber == 1) {
    ballright = -1;
  }
}

void resetgame() {
  ballx = screenwidth / 2;
  if (gametype == 2) {
    if (playerscore > highscore) {
      // set the new highscore
      highscore = playerscore;
    }
  }
  playerscore = 0;
  computerscore = 0;
  // gametype = 0;
  // I used to reset the gametype...
  // ...but I think people are more likely to want to replay same gametype
}

void resetbuttonbuffers() {
  // reset buffer state
  if(arduboy.notPressed(A_BUTTON)){
    buffera = 0;
  }
  if(arduboy.notPressed(B_BUTTON)){
    bufferb = 0;
  }
  if(arduboy.notPressed(UP_BUTTON)){
    bufferup = 0;
  }
  if(arduboy.notPressed(DOWN_BUTTON)){
    bufferdown = 0;
  }
  if(arduboy.notPressed(LEFT_BUTTON)){
    bufferleft = 0;
  }
  if(arduboy.notPressed(RIGHT_BUTTON)){
    bufferright = 0;
  }
}

void win() {
  
  // 1 player  
  if (gametype == 0) {
    arduboy.setCursor(0,0);
    arduboy.print("You Won");
    arduboy.setCursor(0,32);
    arduboy.print("Congratulations!");
    arduboy.setCursor(0,48);
    arduboy.print("Can you do it again?");
  }

  // 2 player
  if (gametype == 1) {
    arduboy.setCursor(0,0);
    arduboy.print("Game Over");
    arduboy.setCursor(0,32);
    arduboy.print("Player 1 Wins!");
    arduboy.setCursor(0,48);
    arduboy.print("Player 2 Loses!");
  }

  // endless
  if (gametype == 2) {
    arduboy.setCursor(0,0);
    arduboy.print("This is wrong...");
    arduboy.setCursor(0,32);
    arduboy.print("How did you win?");
    arduboy.setCursor(0,48);
    arduboy.print("It's not endless?");
  }
  
}

void gameover() {

  // show game over
  arduboy.setCursor(0,0);
  arduboy.print("Game Over");

  // 1 player  
  if (gametype == 0) {
    arduboy.setCursor(0,32);
    arduboy.print("Better luck");
    arduboy.setCursor(0,48);
    arduboy.print("next time...");
  }

  // 2 player
  if (gametype == 1) {
    arduboy.setCursor(0,32);
    arduboy.print("Player 2 Wins!");
    arduboy.setCursor(0,48);
    arduboy.print("Player 1 Loses!");
  }

  // endless
  if (gametype == 2) {
    arduboy.setCursor(0,32);
    arduboy.print("You scored: ");
    arduboy.print(playerscore);
    arduboy.setCursor(0,48);
    if (playerscore > highscore) {
      arduboy.print("New highscore!");
    } else {
      arduboy.print("The highscore is: ");
      arduboy.print(highscore);
    }
  }
  
}

This time around I didn’t implement:

  • different difficulties
  • “art”
  • sounds
  • screensaver mode (CPU vs CPU)

I’ve had a lot of fun playing around with this today. I hope this code is helpful for some of you and perhaps you will enjoy playing it! I think the 2 player mode is the most fun, or taking turns with someone in endless mode.


(Kevin) #38

That is extreme multiplayer lol


(Simon) #39

I can see some heads butting together there!


#40

This means you throught of something better for the Arduboy Version II, right, right ? ;D (as much as i love programming for my current arduboy i can’t help to look at the future and dream about things like this).

It’s funny tho because i had this idea of doing this local multiplayer as well (and i through it was already made so i didn’t even tried). It looks weird and fun at the same time : )