As for the Pong, it is better to attack the game by creating a first version that contains the bare minimum, then rely on it to do the rest.
When we made Pong, we started by simply having a single paddle with a ball. Once that was functional, we added a second paddle and a scoring system. So, like Pong, we should begin by creating a simpler first version with the bare minimum of features. Then, we will build on top of those foundations.
So this first version will only display the first brick. This means that we only have to keep track of one thing, like so:
If the brick is to the left, and the player presses the left arrow, then 15 points will be awarded and a new brick appears. The logic is the same if the brick is to the right. If the player ever misses, his/her score will be reset. Finally, to add a little challenge, the score will progressively lower.
The game must have a way to keep track of which side the current brick occupies, so that is can check whether the player is right or not. Let's declare these variables.
#include
int brick; // Position of our brick. 1 corresponds to left and 2 to right
int arrow; // 1 corresponds to left, 2 to right, and 0 means no presses yet (waiting on player)
int score = 0;
void setup() {
gb.begin();
brick = random(1, 3); // 50% left, 50% right
arrow = 0;
}
void loop() {
while(!gb.update());
// INPUTS //
// LOGIC //
// DRAW //
gb.display.clear();
}
So here we just created three variables that we need. brick
is an integer which is either 1 or 2. If it is 1, then we consider the brick to be on the left. Otherwise, if it is worth 2, then we consider it to be on the right. arrow
is also an integer and, like brick
, 1 is left, 2 is right, and if the player hasn't pressed a key yet, it is worth 0. Well! This was a lot to process for not a lot of behavior - we only have 2 directions here!
How could we improve on this code to make it easier to think about? Well with constants obviously :D
A constant is just like a variable, except for the fact that, once set, its value cannot change (hence the name). A constant is simply a name given to a value. It is very useful in situations like ours: when we have arbitrary values. Like the number 1 corresponding to the side 'left'. We could have chosen another value for 'left', like 12 or -358, instead. Without constants, we type brick = 1
; to mean "the brick is on the left". But we can write brick = LEFT;
! Much better right? We don't even need to comment this line really :p
So then, how do we create constants? Well, like I said, constants are nearly identical variables. All you need to do is add the key word const
(short for 'constant') before the type in the declaration:
const int LEFT = 1;
However, unlike variables, you must give a value to your constant when it is declared. This is simply because, after the declaration, a constant's value cannot change.
#include
// Constants
const int LEFT = 1;
const int RIGHT = 2;
const int NO_DIRECTION = 3;
int brick; // Position of our brick. Either LEFT or RIGHT
int arrow; // LEFT, RIGHT, or NO_DIRECTION
int score = 0;
void setup() {
gb.begin();
brick = random(LEFT, RIGHT + 1); // +1 because random(1, 3) => 1 or 2 (not 3)
arrow = NO_DIRECTION;
}
void loop() {
// loop...
}
And there we go, we are now using some very useful constants :D The brick is either to the left or to the right. The same goes for arrow
(except that it can also be in NO_DIRECTION
). Way easier to read right?
If you read somebody else's code, you might stumble on another way of making constants. They might use #define LEFT 1
. This is (almost) identical to const int LEFT = 1;
. The differences are not important right now, be we suggest that you use the const
method we just saw. It is easier to debug in the eventality of a compiling error. We will only be using const
in these workshops.
Now we can implement the inputs as well as the drawing with ease :
#include
// Constants
const int LEFT = 1;
const int RIGHT = 2;
const int NO_DIRECTION = 3;
int brick; // Position of our brick. Either LEFT or RIGHT
int arrow; // LEFT, RIGHT, or NO_DIRECTION
int score = 0;
void setup() {
gb.begin();
brick = random(LEFT, RIGHT + 1); // +1 because random(1, 3) => 1 or 2 (not 3)
arrow = NO_DIRECTION;
}
void loop() {
while(!gb.update());
// INPUTS //
if (gb.buttons.released(BUTTON_LEFT)) {
arrow = LEFT;
}
else if (gb.buttons.released(BUTTON_RIGHT)) {
arrow = RIGHT;
}
// LOGIC //
// DRAW //
gb.display.clear();
if (brick == LEFT) {
gb.display.fillRect(20, 40, 20, 10);
}
else { // RIGHT
gb.display.fillRect(40, 40, 20, 10);
}
// Score
gb.display.print(score);
}
Our simplified prototype is nearly done :) We are just missing the game logic. Here is a reminder of our goal:
This give us the following code:
// Constants //
// Variables //
void setup() {
// setup //
}
void loop() {
// INPUTS //
// ...
// LOGIC //
// Slowly lower the score
if (score > 0) {
score -= 1;
}
// Did the player press an arrow?
if (arrow != NO_DIRECTION) {
if (brick == arrow) { // Correct arrow press
score += 15;
brick = random(LEFT, RIGHT + 1);
}
else {
// Lost :(
score = 0;
}
arrow = NO_DIRECTION; // User input handled
}
// DRAW //
// ...
}
I also added a mechanic that lowers the score over time : score -= 1
;. The x -= y
operator is a shortcut for x = x - y;
. This also exists for most operators:
You can find +=
when I increase the score. Don't forget to reset arrow
's value after it was handled. Otherwise the Gamebuino will think that the arrow is constantly being pressed!
And there it is, our prototype is done! You can upload it (if you still haven't do so already :P) and play.