Make your game move on its own with a ball bouncing on the screen edges
This time, it is going to be a lot more dynamic. We are approaching a full game!
Length 20 minutes
Level Complete beginner
Prerequisites
In the previous workshop, you made a tally counter. You learned to use variables of type int
. We also looked at if statements and conditions to interact with the player. Finaly, we used a function to draw rectangles to the screen.
So in summary,** you made a rectangle move by pressing buttons**. So we are not far from having a Pong paddle ;)But a Pong game without a ball is not very fun. This is why we will now code a bouncing ball.
For our bouncing ball, we would need a new variable called positionX
, which, like you guessed, stores the X (or horizontal) position of our ball. While we are at it, let's add another variable called speedX
which keeps track of the speed of our ball along the X axis.
Now, look at this code: it makes the ball move forward on it's own!
#include <Gamebuino-Meta.h>
int positionX = 32;
int speedX = 1;
void setup() {
gb.begin();
}
void loop() {
while (!gb.update());
gb.display.clear();
positionX = positionX + speedX;
gb.display.print("positionX: ");
gb.display.print(positionX);
gb.display.print("\nspeedX: ");
gb.display.print(speedX);
gb.display.fillRect(positionX, 32, 4, 4);
}
When you were taught coordinates, you probably saw something like this where the center is where X and Y are equal to zero, and that X and Y get bigger when you go to the up and to the right. But for screens, this isn't the case.
Coordinates on the Gamebuino META's screen, just like all other screens, have their origin in the upper left corner. Not only that, but the Y values get bigger when you move downwards (the X values grow when moving to the right, just like a regular graph).
This means that the opposite corner (bottom right) is at (gb.display.width() - 1, gb.display.height() - 1
).
Now why did we subtract 1 to get the opposite corner? Well it has to do with the fact that the first pixel has a value of 0 on both axes. Let me put it this way: if you had a screen with only four pixels, and the the first pixel's coordinate was 0, what would be the coordinate of the forth (last) pixel?
Here you can easily see that the forth pixel has a coordinate of 3, not 4. Well the same thing happens with you console's screen! The rightmost pixel has an x coordinate equal to gb.display.width() - 1
. And the same thing goes for the lowest pixel.
Note: the screen's size is 80 pixels wide by 64 pixels tall. You can check the width by doing gb.display.print(gb.display.width());
in your code. Or by counting ;). Also note that even if you know this, you should not use 80
instead of gb.display.width()
, because it is less implicit and is a sort of light obfuscation.
While we are on the topic of screen and positioning, let's take a closer look at how the gb.display.drawRect()
function works. First, here is its structure:
gb.display.drawRect(int x, int y, int width, int height);
Just like the screen, the X
and y
passed as parameters are the position of the top left corner of our rectangle. So, knowing this, the bottom right corner of the rectangle has a position of (x + width - 1, y + height - 1
).
Now that this is clear, try to draw the ball higher up the screen. How about drawing the ball so that it is right above the bottom of the screen.
Let's launch our little game and see what happens...Hey! the ball just flies off the screen! Well actually that's expected, as we did not use conditions to stop it. So it just keeps going, even off screen. In that case, how could we keep the ball inside of our screen? Well, let's take a look at this algorithm.
If the ball reaches the right edge
Then go left
If the ball reaches the left edge
Then go right
So there are a couple of things we must look at:
Now, before I give you the solution, try to figure it out. Here are a few hints:
Think about what speedX
does. Play around with its value.
Coordinates on the screen range from 0
to gb.display.width()
. A value of 0
coresponds to the leftmost pixel, and gb.display.width()
is the rightmost pixel.
If you are still stuck, here's what we did:
#include <Gamebuino-Meta.h>
int positionX = 32;
int speedX = 1;
void setup() {
gb.begin();
}
void loop() {
while (!gb.update());
gb.display.clear();
positionX = positionX + speedX;
// If the ball reaches the left side of the screen
if(positionX < 0){
// Go right
speedX = 1;
}
// If the ball reaches the right side of the screen
if(positionX > gb.display.width()){
// Go left
speedX = -1;
}
gb.display.print("positionX: ");
gb.display.print(positionX);
gb.display.print("\nspeedX: ");
gb.display.print(speedX);
gb.display.fillRect(positionX, 32, 4, 4);
}
If the ball only moves left and right, this is not going to be a very exiting Pong match :P Why not create positionY
et speedY
so that the ball can now move across the screen. But just like the left-right movement, you will need to detect the edge (with gb.display.height()
) and keep the ball inside the screen. Go ahead and give it a go!
Make us proud on social networks with #gamebuino #bouncingball , we go through them all the time;)
If you are running out of ideas, or need a little guidance, take a look at what we wrote :)
#include
int positionX = 32;
int speedX = 2;
int positionY = 32;
int speedY = 1;
int ballSize = 8;
void setup() {
gb.begin();
}
void loop() {
while (!gb.update());
gb.display.clear();
// Update horizontal position
positionX = positionX + speedX;
// If the ball reaches the left side of the screen
if(positionX < 0){
// Go right
speedX = 1;
}
// If the ball reaches the right side of the screen
if(positionX > gb.display.width() - ballSize){
// Go left
speedX = -1;
}
// Update vertical position
positionY = positionY + speedY;
// If the ball reaches the top side of the screen
if(positionY < 0){
// Go down
speedY = 1;
}
// If the ball reaches the bottom side of the screen
if(positionY > gb.display.height() - ballSize){
// Go up
speedY = -1;
}
gb.display.setColor(BROWN);
gb.display.print("positionX: ");
gb.display.print(positionX);
gb.display.print("\nspeedX: ");
gb.display.print(speedX);
gb.display.print("\npositionY: ");
gb.display.print(positionY);
gb.display.print("\nspeedY: ");
gb.display.print(speedY);
gb.display.setColor(WHITE);
gb.display.fillRect(positionX, positionY, ballSize, ballSize);
}
By Aurélien Rodot, modified by Julien Giovinazzo
Any questions / comments / suggestions, be sure to drop a comment down below!