NOTICE!!
THIS TUTORIAL IS OUT OF DATE! Please check out the UPDATED version, here: Make Your Own Arduboy Game: Part 8 - Starting DinoSmasher
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, but a follow on Twitter and/or a shout-out really motivates me to continue making more.
http://www.twitter.com/crait
And when you get done, please leave a comment letting me know if you had fun!
If you haven’t done any of the previous tutorials, please be sure to do that before this one! Part 1, Part 2, Part 3, Part 4, Part 5, Part 6, and Part 7
This Tutorial
This tutorial will be the first part of making a more complicated game than Pong. You’re going to make a game to challenge a player to find and destroy as many buildings as they can as a dinosaur in a given time limit, getting points as they do so. First, we need to learn about arrays and a few other things.
This is a HUGE tutorial, so I’m going to be breaking this down into smaller steps. Here’s what we’re going to cover in Part 8.
- Upgrading our library to Arduboy2
- Creating our sketch
- Learning about #define
- Structuring our code
- A moment about button presses
- Creating a 1D array
- Creating a 2D array
0. Upgrading our library to Arduboy2
So, I wrote the previous tutorials quite some time ago. Because of this, I used the original Arduboy library. I’m going to show you how to install the Arduboy2 library. If you are sure you have done this, you can skip this step.
Remember what I said back in tutorial 2… A library is a collection of instructions that someone else wrote that we can use that makes writing games easier.
To use the original library, we were using the following in our code:
#include <Arduboy.h>
Arduboy arduboy;
However, we want to use the updated Arduboy2 library, which was created by @MLXXXp . His Arduboy2 library includes more functionality that we’ll be using that the original does not include. Instead of the above, we’ll need to use the following:
#include <Arduboy2.h>
Arduboy2 arduboy;
We need to also download the library to your computer through the Arduino IDE.
In the Arduino IDE, go to Sketch > Include Library > Manage Libraries .
Simply search for Arduboy2 and select the latest version, then click Install .
1. Creating our sketch
Okay, let’s start working on the game! In the Arduino IDE, go to File > New, then save the file to your computer. In the IDE, let’s use all this.
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
arduboy.setCursor(0, 0);
arduboy.print(F("HELLO!"));
arduboy.display();
}
You should be able to understand most of this, but if there are some new things that you don’t recognize, don’t worry! Just save the file and transfer it to your Arduboy. It should simply say “HELLO”.
2. Learning about #define
There are a few reasons to use functions in our code. One reason is organize your code, but another is to prevent you from having to repeat a lot of the same code over and over again. There’s another cool feature that helps with both of those things that uses the #define
directive.
Pretend like you have the following code in your game to move your character around:
void move_left() {
player_x -= 5;
}
void move_right() {
player_x += 5;
}
void move_up() {
player_y -= 5;
}
void move_down() {
player_y += 5;
}
If you used this code and found that your character moves too fast and wanted to change its speed, you would have to change it in 4 places. Instead, we could use the #define
directive to create a keyword called SPEED
, set it to 5
and then use that.
#define SPEED 5
void move_left() {
player_x -= SPEED;
}
void move_right() {
player_x += SPEED;
}
void move_up() {
player_y -= SPEED;
}
void move_down() {
player_y += SPEED;
}
The way this works is that whenever you compile your code, the compiler will replace SPEED
with 5
before it compiles.
Let’s use this knowledge to change our code!
3. Structuring our code
Our Pong code started to get a little crazy, so let’s make sure this game’s code is a bit easier to read. We have already have the standard loop()
function that’s called many times. In our Pong game, that’s where we had a switch
statement that handled which part of the game was happening.
We’re going to have to use a switch
statement, as well, but let’s put it into its own function. Let’s make a gameplay()
function a reference it near the end of our loop()
function instead of printing “HELLO”. Remember, we want use arduboy.display()
last, so let’s make sure it comes right before that.
When someone turns on the Arduboy, we want it to start at the title screen, then go to the gameplay screen, and then if they lose, we want them to go to the game over screen. However, if they beat the saved high score, we want to take them to the high score screen. From either of those, we want to go back to the title screen.
We need to create an int
variable called gamestate
to keep track of which state we’re in. The switch case should look something like this:
switch(gamestate) {
case 0:
//Title screen stuff
break;
case 1:
//Gameplay stuff
break;
case 2:
//Game over screen
break;
case 3:
//High score screen
break;
}
Putting that into our gameloop()
function, our code now looks like this:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
int gamestate = 0;
void gameloop() {
switch(gamestate) {
case 0:
//Title screen stuff
break;
case 1:
//Gameplay stuff
break;
case 2:
//Game over screen
break;
case 3:
//High score screen
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
COOL!
Instead of having 0, 1, 2, and 3 represent the different states in our game, let’s use #define
to make the code easier to digest.
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
Now, any time we want to check the value of gamestate
, we can simply compare gamestate
to one of these defintions! We can even use it when defining/initializing varaibles! Take a look at how I’ve done it below:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
int gamestate = GAME_TITLE;
void gameloop() {
switch(gamestate) {
case GAME_TITLE:
//Title screen stuff
break;
case GAME_PLAY:
//Gameplay stuff
break;
case GAME_OVER:
//Game over screen
break;
case GAME_HIGH:
//High score screen
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
If we compile this, the code would run, but nothing would be displayed on the Arduboy screen. We should add some text to the screen to make sure this compiles and can be put onto the screen! However, I want to emphasize that we are going to make sure this program is easy to read, so let’s make sure we add it in sensibly!
Instead of putting a bunch of code into our switch
statements, let’s simply add a single function to each case. These functions will be titlescreen()
, gameplay()
, gameoverscreen()
, and highscorescreen()
. Any time we want to add code into one of those states, we’ll simply put the code into those functions! This means we’ll add the text to those functions. Here’s the code for that:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
int gamestate = GAME_TITLE;
void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
}
void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");
}
void gameoverscreen() {
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen\n");
}
void highscorescreen() {
arduboy.setCursor(0, 0);
arduboy.print("High Score Screen\n");
}
void gameloop() {
switch(gamestate) {
case GAME_TITLE:
titlescreen();
break;
case GAME_PLAY:
gameplay();
break;
case GAME_OVER:
gameoverscreen();
break;
case GAME_HIGH:
highscorescreen();
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
ALRIGHTY! Compile this and put it onto your Arduboy to see what happens!
4. A moment about button presses
Up until now, you’ve been learning about button presses with the first Arduboy library. The Arduboy2 library offers us a more simple way to handle button presses. We are able to check buttons 4 different ways:
- The button is currently being held down using
arduboy.pressed()
like before - The button was just pressed in using
arduboy.justPressed()
- The button was just released using
arduboy.justReleased()
- A button is not being pressed using
aduboy.notPressed()
The library automatically handles what we had been programming in all along! In order to check if a button was pressed this frame, but don’t repeat, we can use arduboy.justPressed()
. Changing the gamestate
with the A button with something like the following would be easy.
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
Try adding code like this to titlescreen()
, gameplay()
, gameoverscreen()
, and highscorescreen()
to cycle through all of the states. If you want to double-check your work, see my code below:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
int gamestate = GAME_TITLE;
void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}
void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_OVER;
}
}
void gameoverscreen() {
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_HIGH;
}
}
void highscorescreen() {
arduboy.setCursor(0, 0);
arduboy.print("High Score Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_TITLE;
}
}
void gameloop() {
switch(gamestate) {
case GAME_TITLE:
titlescreen();
break;
case GAME_PLAY:
gameplay();
break;
case GAME_OVER:
gameoverscreen();
break;
case GAME_HIGH:
highscorescreen();
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
I suggest running this on your Arduboy and seeing how the states do not flicker too fast.
(OH, YEAH! Did you notice that A_BUTTON
is a defined keyword?! It’s just a number!)
5. Creating a 1D array
Remember back in Part 6 when I told you about arrays? I told you that they were just groups of variables. Let’s talk a little more about them. They can be super helpful!
Let’s pretend that you have 5 cars in your game and you wanted to store how fast each one can drive. You could do something like this.
int car_one = 4;
int car_two = 5;
int car_three = 7;
int car_four = 8;
int car_five = 3;
But what if you wanted to add 5 more cars?? That’s a lot of typing!! Well, there’s a quicker way to write this with arrays!
int cars[5] = { 4, 5, 7, 8, 3 };
Using the above code, you would be creating an int array
with all the car data in it. Let’s break this down.
int
: This is the type of data you want a list of.
cars
: This is the name of the array. Since you want to store many cars into it, you call it ‘cars.’
[5]
: This is how many int
's you want in your array.
{ 4, 5, 7, 8, 3 }
: This is the data that you’re storing. Notice that there’s 1 number for each car.
To increase the number of cars, you increase the size of the array and simply add another value to the end of the list, which is a lot easier!
int cars[10] = { 4, 5, 7, 8, 3, 6, 3, 1, 7, 9 };
Now, what if we wanted to figure out the speed of one of the cars? Earlier, we would use car_one
to access the number 4
.
track_distance = car_four;
When using an array, we access using brackets like this:
track_distance = cars[4];
Does the above make sense to you? GOOD! Because there’s one important thing that I need to tell you about arrays.
The index is the number that you use to access the data inside of the array at a given location. In the above code, 4
would be the index since you’re seemingly trying to get the fourth car’s speed.
However, when counting indexes, you don’t start with 1, 2, 3, … etc. Counting indexes actually starts at 0! What does this mean?? This means that the first car’s speed is actually stored at cars[0]
. The second’s car’s speed is stored at cars[1]
. The third is at cars[2]
, etc, etc.
So, accessing the 4th car’s speed with cars[4]
is actually wrong. We need to use cars[3]
!!
track_distance = car_four; //This is lame
...
track_distance = cars[3]; //This is cool
This isn’t hard to remember and I’ll try to make sure I remind you! Besides, there are benefits to this!!
OKAY! Enough talking! Let’s code an array!
Let’s make an array called world
. This will eventually hold our game’s map! For now, we’ll just have it hold random numbers. Let’s make it have length of 20.
int world[20] = { 9, 5, 7, 2, 8, 3, 1, 7, 9, 3, 2, 1, 6, 4, 7, 8, 4, 3, 1, 4 };
We can also display some values from it in the titlescreen()
function to test it! Add this!
arduboy.print(world[0]);
arduboy.print(world[1]);
arduboy.print(world[2]);
arduboy.print(world[3]);
Here’s my full code:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
int gamestate = GAME_TITLE;
int world[20] = { 9, 5, 7, 2, 8, 3, 1, 7, 9, 3, 2, 1, 6, 4, 7, 8, 4, 3, 1, 4 };
void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
arduboy.print(world[0]);
arduboy.print(world[1]);
arduboy.print(world[2]);
arduboy.print(world[3]);
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}
void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_OVER;
}
}
void gameoverscreen() {
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_HIGH;
}
}
void highscorescreen() {
arduboy.setCursor(0, 0);
arduboy.print("High Score Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_TITLE;
}
}
void gameloop() {
switch(gamestate) {
case GAME_TITLE:
titlescreen();
break;
case GAME_PLAY:
gameplay();
break;
case GAME_OVER:
gameoverscreen();
break;
case GAME_HIGH:
highscorescreen();
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
Isn’t it annoying that you still have to specificy that you want to print each individual value? What if you wanted to print all the values of the world
array?? Would you copy/paste the code 20 times? NO! We have for
loops that can help us!
Remember, if we want to use a for
loop to go through something 20 times, we’d do something like this:
for(int i = 0; i < 20; i++) {
arduboy.print(i);
}
If we use the above code, the numbers 0~19 would be printed out to the screen. Instead, we want to print the 20 values inside of world
, starting at index 0.
for(int i = 0; i < 20; i++) {
arduboy.print( world[i] );
}
Put this code into your title screen function and run it! Full code, here:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
int gamestate = GAME_TITLE;
int world[20] = { 9, 5, 7, 2, 8, 3, 1, 7, 9, 3, 2, 1, 6, 4, 7, 8, 4, 3, 1, 4 };
void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
for(int i = 0; i < 20; i++) {
arduboy.print(world[i]);
}
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}
void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_OVER;
}
}
void gameoverscreen() {
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_HIGH;
}
}
void highscorescreen() {
arduboy.setCursor(0, 0);
arduboy.print("High Score Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_TITLE;
}
}
void gameloop() {
switch(gamestate) {
case GAME_TITLE:
titlescreen();
break;
case GAME_PLAY:
gameplay();
break;
case GAME_OVER:
gameoverscreen();
break;
case GAME_HIGH:
highscorescreen();
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
This is what you should get after compiling!
Alright, now that you have a good understanding of what arrays are, let’s talk about 2D arrays!
6. Creating a 2D array
Arrays can store all sorts of data; You can have an array of int
's… an array of boolean
's… These are all called 1D arrays because they store data in 1 dimension. It’s basically just a single list of variables all grouped together.
But, you can take arrays a step further! You can even have an array of arrays!! WHAT?! These are called 2D arrays because they store data in 2 dimensions… This means that instead of just a list of information, you can have a whole grid of it!
Here’s a neat way to visualize the difference between a 1D array and a 2D array.
We can make a 2D array and store numbers into it and then print the numbers out like we did with a 1D array! Lemme show you how by starting by modifying our world
array.
int world[4][20] = {
{ 9, 5, 7, 2, 8, 3, 1, 7, 9, 3, 2, 1, 6, 4, 7, 8, 4, 3, 1, 4 },
{ 8, 7, 2, 5, 6, 1, 4, 6, 6, 2, 6, 3, 5, 4, 2, 4, 3, 1, 5, 2 },
{ 7, 2, 6, 3, 5, 4, 0, 2, 3, 5, 9, 2, 3, 5, 4, 2, 5, 4, 1, 9 },
{ 6, 4, 5, 2, 6, 4, 5, 1, 2, 5, 4, 3, 2, 4, 6, 5, 4, 2, 4, 5 }
};
Notice that our world
array now has 2 lengths given to it. [4]
refers to the the amount 1D arrays that will be stored. [20]
is how long the 1D arrays will be. When creating this grid, you can think of the first number as the height of the grid and the second as the width.
In order to read one of the int
values, we need to give two different indexes. Something like this: world[2][6]
The first number is the row, and the second is the column. This would return the value 0
.
Now, how would you loop through all of these numbers to print them out? Well, we could write 4 separate for
loops, but that would cause the same problem as before. Like in Part 6, we can have a for
loop for the height and a for
loop for the width!!
We can print the world
by adjusting our for
loop to look like this:
for(int y = 0; y < 4; y++) {
for(int x = 0; x < 20; x++) {
arduboy.print(world[y][x]);
}
arduboy.print("\n");
}
Remember from Part 5 that arduboy.print("\n")
is supposed to create a new line of text! Compile and test the completed code:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
int gamestate = GAME_TITLE;
int world[4][20] = {
{ 9, 5, 7, 2, 8, 3, 1, 7, 9, 3, 2, 1, 6, 4, 7, 8, 4, 3, 1, 4 },
{ 8, 7, 2, 5, 6, 1, 4, 6, 6, 2, 6, 3, 5, 4, 2, 4, 3, 1, 5, 2 },
{ 7, 2, 6, 3, 5, 4, 6, 2, 3, 5, 9, 2, 3, 5, 4, 2, 5, 4, 1, 9 },
{ 6, 4, 5, 2, 6, 4, 5, 1, 2, 5, 4, 3, 2, 4, 6, 5, 4, 2, 4, 5 }
};
void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
for(int y = 0; y < 4; y++) {
for(int x = 0; x < 20; x++) {
arduboy.print(world[y][x]);
}
arduboy.print("\n");
}
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}
void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_OVER;
}
}
void gameoverscreen() {
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_HIGH;
}
}
void highscorescreen() {
arduboy.setCursor(0, 0);
arduboy.print("High Score Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_TITLE;
}
}
void gameloop() {
switch(gamestate) {
case GAME_TITLE:
titlescreen();
break;
case GAME_PLAY:
gameplay();
break;
case GAME_OVER:
gameoverscreen();
break;
case GAME_HIGH:
highscorescreen();
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
Cool, huh? Alright, let’s try something very dangerous. Something that you may have even wondered on your own!! Our world
array is current 4
tall and 20
wide, but what happens if we try to print out the 5th line of numbers? Or 6th line? Could we do it? What if those values don’t exist? What will be printed out??? Replace the for
loops with this and see for yourself!!
for(int y = 0; y < 6; y++) {
for(int x = 0; x < 20; x++) {
arduboy.print(world[y][x]);
}
arduboy.print("\n");
}
On my Arduboy, this is what it looks like!
Where did those exact numbers come from?? Shouldn’t those be 0’s?? Why does this happen? Well, whenever the Arduboy stores variables and things into memory, it puts them all into one big list. If you read outside of the array, you will be reading other variables. If you try to change the value that is outside of the array, you may accidentally change a random variable somewhere else in memory. This can cause all sorts of random stuff to happen that we don’t want right now. We can experiment with this later, but for now, let’s prevent this using our #define
directive!
Let’s define the height and width of world
and then use those numbers in our definitions and for
loops! OH, let’s also change all the values to 0’s or 1’s just to check something.
#define WORLD_WIDTH 20
#define WORLD_HEIGHT 4
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
{ 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }
};
void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
for(int y = 0; y < WORLD_HEIGHT; y++) {
for(int x = 0; x < WORLD_WIDTH; x++) {
arduboy.print(world[y][x]);
}
arduboy.print("\n");
}
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}
This Let’s move the for loops to their own function called drawworld()
and reference it inside of the gameplay()
function. Here’s the completed code:
//DinoSmasher
#include <Arduboy2.h>
Arduboy2 arduboy;
#define GAME_TITLE 0
#define GAME_PLAY 1
#define GAME_OVER 2
#define GAME_HIGH 3
int gamestate = GAME_TITLE;
#define WORLD_WIDTH 20
#define WORLD_HEIGHT 4
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 },
{ 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }
};
void drawworld() {
for(int y = 0; y < WORLD_HEIGHT; y++) {
for(int x = 0; x < WORLD_WIDTH; x++) {
arduboy.print(world[y][x]);
}
arduboy.print("\n");
}
}
void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}
void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");
drawworld();
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_OVER;
}
}
void gameoverscreen() {
arduboy.setCursor(0, 0);
arduboy.print("Game Over Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_HIGH;
}
}
void highscorescreen() {
arduboy.setCursor(0, 0);
arduboy.print("High Score Screen\n");
if(arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_TITLE;
}
}
void gameloop() {
switch(gamestate) {
case GAME_TITLE:
titlescreen();
break;
case GAME_PLAY:
gameplay();
break;
case GAME_OVER:
gameoverscreen();
break;
case GAME_HIGH:
highscorescreen();
break;
}
}
void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
arduboy.display();
arduboy.initRandomSeed();
arduboy.clear();
}
void loop() {
if(!(arduboy.nextFrame())) {
return;
}
arduboy.pollButtons();
arduboy.clear();
gameloop();
arduboy.display();
}
Let’s test this code and see what happens! Don’t the numbers in the gameplay screen kinda resemble a map? In fact, a lot of older games do something similar to draw the maps for their games.
But, we don’t want that. We want to actually draw a map using the techniques we talked about in Part 6.
Next Tutorial
You’ve learned a lot this tutorial, so let’s wait until the next tutorial to add graphics to make it look cool! You’ll also learn how to move around the map like in Zelda or Pokemon.
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.