# 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.

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, Part 7 and ESPECIALLY PART 8.

# This Tutorial

In this tutorial, we’ll be adding graphics to our game, DinoSmasher!! Make sure you’ve worked on Part 8 of the tutorial series because this tutorial directly follows the previous one. We’ll end up creating and moving a player around a map.

Here’s what we’ve go to work on to finish this tutorial.

1. Converting & drawing some images
2. Formatting our world’s images
3. Moving the map
4. Cropping the map
5. Cycling the map
6. Bounding the map

# Our Current 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();

}
``````

# 1. Converting & drawing some images

In the previous tutorial, we made the `world` 2D array out of 0’s and 1’s. Instead of drawing those numbers, let’s draw pictures, instead! Where we see a 0, let’s draw some grass, and where we see a 1, let’s draw some water!

Our maps will be made up of 16x16 images. Here are two sprites that I’ve made for us to use. They are 16x16 pixels.

GRASS:

WATER:

Converting them using the TeamARG Image Converter (https://teamarg.github.io/arduboy-image-converter/) gives:

``````const unsigned char PROGMEM grass[] = {
// width, height,
16, 16,
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,
};

const unsigned char PROGMEM water[] = {
// width, height,
16, 16,
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,
};
``````

Since we’ll be dealing with 16x16, let’s define a keyword to keep track of that called TILE_SIZE and set it to 16.

`#define TILE_SIZE 16`

Let’s update our `drawworld()` function’s `for` loops. Instead of printing each number, we need to use an `if` statement to determine which picture we’re going to draw.

``````#define TILE_SIZE  16
void drawworld() {
for (int y = 0; y < WORLD_HEIGHT; y++) {
for (int x = 0; x < WORLD_WIDTH; x++) {
if(world[y][x] == 0) {
Sprites::drawOverwrite(x, y, grass, 0);
}
if(world[y][x] == 1) {
Sprites::drawOverwrite(x, y, water, 0);
}
}
}
}
``````

If you run this code, you’ll see this.

Our problem is that we need to tile the images 16 pixels apart and the way we are drawing the images, we are only separating them 1 pixel since the `for` loops increase the `x` and `y` variables by 1 each loop.

Instead, let’s multiply the X and Y values by 16, or the `TILE_SIZE` when drawing the images.

``````#define TILE_SIZE  16
void drawworld() {
for (int y = 0; y < WORLD_HEIGHT; y++) {
for (int x = 0; x < WORLD_WIDTH; x++) {
if (world[y][x] == 0) {
Sprites::drawOverwrite(x * TILE_SIZE, y * TILE_SIZE, grass, 0);
}
if (world[y][x] == 1) {
Sprites::drawOverwrite(x * TILE_SIZE, y * TILE_SIZE, water, 0);
}
}
}
}
``````

There we go!

# 2. Formatting our world’s images

In our game’s world, we want to have a lot of different kinds of images. It would be pretty annoying to copy/paste a bunch of `if` statements in our `drawworld()` function. We could save some time by using a `switch` statement, but there’s a better way to condense this code using a feature of the `Sprites::drawOverwrite()` function.

Let’s combine the water and grass data as shown below.

``````const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,
};
``````

See how the data is constructed. The first two bytes of the array detail the width and height of the images. Following these two bytes are the data for the first image (the grass) and then the second image (the water).

To draw the `grass` and `water` data with `tiles` , we’d do something like this:

``````Sprites::drawOverwrite(a, b, tiles, 0);  // Will draw grass
Sprites::drawOverwrite(c, e, tiles, 1);  // Will draw water
``````

Notice that the only thing different is the index `0` or `1` . This means that we won’t have to use any `if` statements in our `drawworld()` function! This is what the updated `drawworld()` function would look like:

``````void drawworld() {
for (int y = 0; y < WORLD_HEIGHT; y++) {
for (int x = 0; x < WORLD_WIDTH; x++) {
Sprites::drawOverwrite(x * TILE_SIZE, y * TILE_SIZE, tiles, world[y][x]);

}
}
}
``````

Compile the code and see how it the result is the same as before!

``````//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;

const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,
};

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

#define TILE_SIZE  16
void drawworld() {
for (int y = 0; y < WORLD_HEIGHT; y++) {
for (int x = 0; x < WORLD_WIDTH; x++) {
Sprites::drawOverwrite(x * TILE_SIZE, y * TILE_SIZE, tiles, world[y][x]);

}
}
}

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();

}
``````

Even though the results are the same, we are doing this for a very important reason! If we want to add in more images, we can do that very easily! Let’s add two more in!

TREES:
STONE:

I converted the images with the TeamARG Image Converter and then added them to the `tiles` array. The tree image should be the third and stone image should be the fourth image.

``````const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,

//Tree
0xff, 0x1f, 0x5b, 0x3f, 0xeb, 0xdd, 0xff, 0xf7, 0xbb, 0xef, 0xfd, 0x7f, 0xe3, 0xcb, 0xe3, 0xff,
0xff, 0xc7, 0x96, 0xc7, 0xff, 0xff, 0xef, 0xfd, 0xff, 0xe3, 0xcb, 0xe3, 0xff, 0xff, 0x7b, 0xff,

//Stone
0xff, 0xdf, 0x7b, 0x3f, 0x9f, 0x6f, 0x77, 0xab, 0xdb, 0xd7, 0xcd, 0x5f, 0xbf, 0x77, 0xff, 0xff,
0xff, 0xc1, 0xdc, 0xd3, 0xaf, 0x9f, 0xae, 0xb0, 0xbb, 0xbd, 0xbd, 0xba, 0xd7, 0xcc, 0x63, 0xff,
};
``````

Now that the images are added, let’s updated the `world` array to include the numbers `2` and `3` . These will cause stones and trees to be drawn.

``````int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ 2, 0, 0, 1, 0, 0, 0, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 1, 0, 0, 1, 2, 0, 0, 0, 3, 3, 0, 2, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 2, 0 },
{ 3, 0, 0, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }
};
``````

This is great! Except there’s two more things that I want to do before we get on to cooler things.

First of all, let’s adjust the `WORLD_WIDTH` . As it, we can’t see the entire map on the Arduboy’s screen, so we don’t really need one that big. Let’s change it to `8` and remove some values.

``````#define WORLD_WIDTH    8
...
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ 2, 0, 0, 1, 0, 0, 0, 2 },
{ 0, 1, 1, 1, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 0, 1, 3, 0 },
{ 3, 0, 0, 3, 2, 1, 1, 1 }
};
``````

Now that the `world` array is small enough, take a look at your Arduboy screen, you could (in theory) create any kind of combination of map using the trees, grass, stone, and water tiles that we have. But, it may be hard visualizing the result because the numbers could get confusing, especially if we end up with over 100 different numbers!

Let’s use `#define` to replace these numbers with words that makes sense to us. Use the following code:

``````#define GRASS      0
#define WATER      1
#define TREES      2
#define STONE      3
``````

Since we’re defining `GRASS` , `WATER` , `TREES` , and `STONE` , we can replace the way we store information in the `world` array to match.

``````int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ TREES, GRASS, GRASS, WATER, GRASS, GRASS, GRASS, TREES },
{ GRASS, WATER, WATER, WATER, GRASS, WATER, GRASS, GRASS },
{ GRASS, GRASS, GRASS, GRASS, GRASS, WATER, STONE, GRASS },
{ STONE, GRASS, GRASS, STONE, TREES, WATER, WATER, WATER }
};
``````

When making a games with 2D arrays for maps and stuff, I like to do this. I did this in Circuit Dude and Midnight Wild . This means you could download and modify the maps for those games by changing their 2D arrays.

Another thing about doing this is that you could have a different 2D array for each level.

Anyway, there is another really great reason to do this, but we’ll have to wait for that! For now, try this code on your device!

``````//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;

const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,

//Tree
0xff, 0x1f, 0x5b, 0x3f, 0xeb, 0xdd, 0xff, 0xf7, 0xbb, 0xef, 0xfd, 0x7f, 0xe3, 0xcb, 0xe3, 0xff,
0xff, 0xc7, 0x96, 0xc7, 0xff, 0xff, 0xef, 0xfd, 0xff, 0xe3, 0xcb, 0xe3, 0xff, 0xff, 0x7b, 0xff,

//Stone
0xff, 0xdf, 0x7b, 0x3f, 0x9f, 0x6f, 0x77, 0xab, 0xdb, 0xd7, 0xcd, 0x5f, 0xbf, 0x77, 0xff, 0xff,
0xff, 0xc1, 0xdc, 0xd3, 0xaf, 0x9f, 0xae, 0xb0, 0xbb, 0xbd, 0xbd, 0xba, 0xd7, 0xcc, 0x63, 0xff,
};

#define WORLD_WIDTH    20
#define WORLD_HEIGHT  4
#define GRASS      0
#define WATER      1
#define TREES      2
#define STONE      3
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ TREES, GRASS, GRASS, WATER, GRASS, GRASS, GRASS, TREES },
{ GRASS, WATER, WATER, WATER, GRASS, WATER, GRASS, GRASS },
{ GRASS, GRASS, GRASS, GRASS, GRASS, WATER, STONE, GRASS },
{ STONE, GRASS, GRASS, STONE, TREES, WATER, WATER, WATER }
};

#define TILE_SIZE  16
void drawworld() {
for (int y = 0; y < WORLD_HEIGHT; y++) {
for (int x = 0; x < WORLD_WIDTH; x++) {
Sprites::drawOverwrite(x * TILE_SIZE, y * TILE_SIZE, tiles, world[y][x]);

}
}
}

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();

}
``````

# 3. Moving the map

Now, in Part 6, we were able to draw some grass and allowed the player to move a character around by changing their character’s X and Y position values. However, not all games work like that. Some games, the player is stuck in the center of the screen and the game’s map moves in the background. Games like Zelda and Pokemon do this.

To learn how to do it, there’s a few things we need to understand, but first, let’s just simply move the X and Y position of the background based on the player’s position to offset the map on the screen. Let’s make a variable for both the X and Y called `mapx` and `mapy’.

``````int mapx = 0;
int mapy = 0;
``````

In the game, we are going to allow the player to move around and change the values of `mapx` and `mapy` . After they are updated, we want to draw the world. This means that we need to get the player’s input before drawing the world.

We should make a function called `playerinput()` to handle these changes. In the `grameplay()` function, before `drawworld()` , let’s reference the `playerinput()` function.

``````void playerinput() {

}

void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");

playerinput();
drawworld();

if (arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_OVER;
}
}
``````

Changing the values of `mapx` and `mapy` will be easy. Use this code, which should be familiar to you:

``````void playerinput() {
if (arduboy.pressed(UP_BUTTON)) {
mapy += 1;
}
if (arduboy.pressed(DOWN_BUTTON)) {
mapy -= 1;
}
if (arduboy.pressed(LEFT_BUTTON)) {
mapx += 1;
}
if (arduboy.pressed(RIGHT_BUTTON)) {
mapx -= 1;
}
}
``````

When we draw the game world, let’s offset it by `mapx` and `mapy` .

``````void drawworld() {
for (int y = 0; y < WORLD_HEIGHT; y++) {
for (int x = 0; x < WORLD_WIDTH; x++) {
arduboy.drawBitmap(x * TILE_SIZE + mapx, y * TILE_SIZE + mapy, tiles[world[y][x]], TILE_SIZE, TILE_SIZE, WHITE);

}
}
}
``````

After compiling your code, you should be able to use the direction buttons to move the map around the screen. Try the following code. (I’ve adjusted the world map’s size.)

``````//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 mapx = 0;
int mapy = 0;

const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,

//Tree
0xff, 0x1f, 0x5b, 0x3f, 0xeb, 0xdd, 0xff, 0xf7, 0xbb, 0xef, 0xfd, 0x7f, 0xe3, 0xcb, 0xe3, 0xff,
0xff, 0xc7, 0x96, 0xc7, 0xff, 0xff, 0xef, 0xfd, 0xff, 0xe3, 0xcb, 0xe3, 0xff, 0xff, 0x7b, 0xff,

//Stone
0xff, 0xdf, 0x7b, 0x3f, 0x9f, 0x6f, 0x77, 0xab, 0xdb, 0xd7, 0xcd, 0x5f, 0xbf, 0x77, 0xff, 0xff,
0xff, 0xc1, 0xdc, 0xd3, 0xaf, 0x9f, 0xae, 0xb0, 0xbb, 0xbd, 0xbd, 0xba, 0xd7, 0xcc, 0x63, 0xff,
};

#define WORLD_WIDTH    14
#define WORLD_HEIGHT  7
#define GRASS      0
#define WATER      1
#define TREES      2
#define STONE      3
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ TREES, GRASS, GRASS, WATER, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, GRASS, GRASS, TREES },
{ GRASS, WATER, WATER, WATER, GRASS, WATER, GRASS, GRASS, GRASS, GRASS, GRASS, STONE, GRASS, GRASS },
{ GRASS, GRASS, GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS },
{ STONE, GRASS, GRASS, STONE, TREES, WATER, WATER, WATER, GRASS, WATER, WATER, GRASS, TREES, GRASS },
{ GRASS, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, TREES, WATER, GRASS, GRASS, STONE, TREES },
{ GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, TREES, TREES, TREES, GRASS, GRASS, WATER, WATER },
{ GRASS, WATER, WATER, TREES, GRASS, WATER, WATER, TREES, TREES, GRASS, GRASS, GRASS, GRASS, STONE }
};

#define TILE_SIZE  16
void drawworld() {
for (int y = 0; y < WORLD_HEIGHT; y++) {
for (int x = 0; x < WORLD_WIDTH; x++) {
Sprites::drawOverwrite(x * TILE_SIZE + mapx, y * TILE_SIZE + mapy, tiles, world[y][x]);

}
}
}

void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
if (arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}

void playerinput() {
if (arduboy.pressed(UP_BUTTON)) {
mapy += 1;
}
if (arduboy.pressed(DOWN_BUTTON)) {
mapy -= 1;
}
if( arduboy.pressed(LEFT_BUTTON)) {
mapx += 1;
}
if (arduboy.pressed(RIGHT_BUTTON)) {
mapx -= 1;
}
}

void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");

playerinput();
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();

}
``````

Right now, the map is being drawn that is bigger than the Arduboy screen. The bigger the map is, the more images the Arduboy will try to draw. This will slow down the Arduboy at some point, so we need to crop the map and only draw a portion that is slightly bigger than the Arduboy’s screen.

We also want to stop the player from being able to move the map off of the screen. There needs to be some kinds of boundaries!

# 4. Cropping the map

Attempting to loop through and draw everything in our `world` array will slow our game down so much that it’ll be unplayable if `world` gets too big.

We really only need to draw 8 tiles wide and 4 tiles high to fill the entire screen. The way we get these numbers is by finding the how many tiles can span across the the width of the screen and height of the screen. The tiles are 16 pixels wide and 16 pixels tall. The screen is 128 pixels wide and 64 pixels tall.

Screen width of 128 pixels wide ÷ tile width of 16 pixels = 8
Screen height of 64 pixels wide ÷ tile height of 16 pixels = 4

Since these values won’t change, we could store these into constant `int` 's!

``````const int tileswide = 128 / TILE_SIZE;
const int tilestall = 64 / TILE_SIZE;
``````

The Arduboy2 library has defined `WIDTH` and `HEIGHT` for us to use in a situation like this to represent the screen’s size so we don’t have to always remember the exact values. Let’s use those:

``````const int tileswide = WIDTH / TILE_SIZE;
const int tilestall = HEIGHT / TILE_SIZE;
``````

Alright, let’s adjust our draw function’s `for` loops. Instead of looping through the entire `WORLD_HEIGHT` and `WORLD_WIDTH` , we only need to go to 8 tall and 4 wide, which are the values of `tileswide` and `tilestall` . Let’s swap those out:

``````void drawworld() {
const int tileswide = WIDTH / TILE_SIZE;
const int tilestall = HEIGHT / TILE_SIZE;

for (int y = 0; y < tilestall; y++) {
for (int x = 0; x < tileswide; x++) {
Sprites::drawOverwrite(x * TILE_SIZE + mapx, y * TILE_SIZE + mapy, tiles, world[y][x]);
}
}
}
``````

Compiling and running this code would cause you to realize that you can now move a smaller section of the map around the screen.
When we draw the map tiles, we are offsetting them with the `mapx` and `mapy` variables. If these get too big, the map will move off of the screen and we won’t see anything!!

Instead, we need to keep the map on the screen and prevent the `mapx` and `mapy` variables from getting too big.

What we could do is use some code like this:

``````if (mapx > 10) {
mapx = 0;
}
if (mapx < 0) {
mapx = 10;
}
``````

This would ensure that `mapx` is between `0` and `10` . This isn’t optimal, though. The modulo operator could be used to do this. The modulo function returns the remainder of a division operation. For example 12 divided by 3 returns a remainder of 0 whereas 13 divided by 3 returns 1.

``````mapx = mapx % 10;
``````

In this case, we’d be letting the map move up to 10 pixels before the screen moved back. We would also be overwriting our `mapx` variable. This isn’t something we want to save- We only want to use this value when drawing our map tiles. Let’s replace `mapx` and `mapy` with `mapx % 10` and `mapy % 10` in our `arduboy.drawBitmap()` function. This way, we can continue to keep track of the map’s X/Y position, no matter how big and still confine the map to be drawn on the screen.

``````void drawworld() {
const int tileswide = WIDTH / TILE_SIZE;
const int tilestall = HEIGHT / TILE_SIZE;

for (int y = 0; y < tilestall; y++) {
for (int x = 0; x < tileswide; x++) {
Sprites::drawOverwrite(x * TILE_SIZE + mapx % 10, y * TILE_SIZE + mapy % 10, tiles, world[y][x]);

}
}
}
``````

Compiling and running your code will result in the ability to move the map but not too far from the screen! It even works going backwards! But do you notice that there is a lot of black space when moving the map around? To fix this, we should increase how many images we draw across and tall by 1 to fill in that space.

``````  const int tileswide = WIDTH / TILE_SIZE + 1;
const int tilestall = HEIGHT / TILE_SIZE + 1;
``````

Compile your code and test it out!

Alright, there’s another problem… When moving around, you can’t even see the entirety of the bottom and right-most tiles. This is because we are only allowing the player to see `10` pixels of those tiles at a time. To stop it from cutting, let’s change the 10 to 16. Actually, no. Let’s change the `10` to `TILE_SIZE` so that we can see the entire tile.

``````void drawworld() {
const int tileswide = WIDTH / TILE_SIZE + 1;
const int tilestall = HEIGHT / TILE_SIZE + 1;

for(int y = 0; y < tilestall; y++) {
for(int x = 0; x < tileswide; x++) {
arduboy.drawBitmap(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles[world[y][x]], TILE_SIZE, TILE_SIZE, WHITE);

}
}
}
``````

Now that we confine the map to the screen the BIGGEST problem that I’ve ignored is that the map’s tiles don’t actually change, so you can’t actually pan around the world. You’ll only be looking at the same section of the map!

Let’s do that, now!

6 Likes

# 5. Cycling the map

To understand how cycling through the map works, let’s verify a few things, including our X/Y coordinates. Let’s print those out.

In the `gameplay()` function, remove the `arduboy.setCursor()` and `arduboy.print()` functions since we don’t need that, anymore.

In the `drawworld()` functions, let’s draw a black rectangle and then print our `mapx` and `mapy` coordinates after drawing the tiles in the `for` loops.

OH, we should also add a black 16x16 square to the middle of the screen to represent the player.

``````  arduboy.fillRect(WIDTH / 2 - 8, HEIGHT / 2 - 8, 16, 16, BLACK);

arduboy.fillRect(0, 0, 48, 8, BLACK);
arduboy.setCursor(0, 0);
arduboy.print(mapx);
arduboy.print(",");
arduboy.print(mapx);
``````

Let’s run these quick changes and observe what happens when we move the screen around. Here’s the full code so far:

``````//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 mapx = 0;
int mapy = 0;

const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,

//Tree
0xff, 0x1f, 0x5b, 0x3f, 0xeb, 0xdd, 0xff, 0xf7, 0xbb, 0xef, 0xfd, 0x7f, 0xe3, 0xcb, 0xe3, 0xff,
0xff, 0xc7, 0x96, 0xc7, 0xff, 0xff, 0xef, 0xfd, 0xff, 0xe3, 0xcb, 0xe3, 0xff, 0xff, 0x7b, 0xff,

//Stone
0xff, 0xdf, 0x7b, 0x3f, 0x9f, 0x6f, 0x77, 0xab, 0xdb, 0xd7, 0xcd, 0x5f, 0xbf, 0x77, 0xff, 0xff,
0xff, 0xc1, 0xdc, 0xd3, 0xaf, 0x9f, 0xae, 0xb0, 0xbb, 0xbd, 0xbd, 0xba, 0xd7, 0xcc, 0x63, 0xff,
};

#define WORLD_WIDTH    14
#define WORLD_HEIGHT  7
#define GRASS      0
#define WATER      1
#define TREES      2
#define STONE      3
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ TREES, GRASS, GRASS, WATER, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, GRASS, GRASS, TREES },
{ GRASS, WATER, WATER, WATER, GRASS, WATER, GRASS, GRASS, GRASS, GRASS, GRASS, STONE, GRASS, GRASS },
{ GRASS, GRASS, GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS },
{ STONE, GRASS, GRASS, STONE, TREES, WATER, WATER, WATER, GRASS, WATER, WATER, GRASS, TREES, GRASS },
{ GRASS, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, TREES, WATER, GRASS, GRASS, STONE, TREES },
{ GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, TREES, TREES, TREES, GRASS, GRASS, WATER, WATER },
{ GRASS, WATER, WATER, TREES, GRASS, WATER, WATER, TREES, TREES, GRASS, GRASS, GRASS, GRASS, STONE }
};

#define TILE_SIZE  16
void drawworld() {
const int tileswide = WIDTH / TILE_SIZE + 1;
const int tilestall = HEIGHT / TILE_SIZE + 1;

for (int y = 0; y < tilestall; y++) {
for (int x = 0; x < tileswide; x++) {
Sprites::drawOverwrite(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles, world[y][x]);

}
}

arduboy.fillRect(0, 0, 48, 8, BLACK);
arduboy.setCursor(0, 0);
arduboy.print(mapx);
arduboy.print(",");
arduboy.print(mapy);
}

void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
if (arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}

void playerinput() {
if (arduboy.pressed(UP_BUTTON)) {
mapy += 1;
}
if (arduboy.pressed(DOWN_BUTTON)) {
mapy -= 1;
}
if( arduboy.pressed(LEFT_BUTTON)) {
mapx += 1;
}
if (arduboy.pressed(RIGHT_BUTTON)) {
mapx -= 1;
}
}

void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");

playerinput();
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();

}
``````

Notice that every time we move the screen 16 pixels in any direction, the screen jumps back. If you move slowly and watch, you’ll notice that the tiles align perfectly, even if there is a jump. The only thing that is wrong is that that the next tiles are not displayed and instead, the same tiles are displayed over and over.

In order to display the next tiles, we need to figure out which tiles we are going to draw. We must keep track every time the map jumps around. We could do this with another variable, but we could also calculate this based off of `mapx` and `mapy` .

Remember from Part 7, whenever we divide an `int` in C++, it only returns whole numbers! Dividing gets rid of the remainder. We could divide the `mapx` and `mapy` by 16 to find out how many times we’ve jumped the map around.

In the `drawworld()` function, we should change what we’re printing out:

``````  arduboy.print(mapx / TILE_SIZE);
arduboy.print(",");
arduboy.print(mapy / TILE_SIZE);
``````

Run this code and move the map around!

Remember that we are drawing the top-right of the map, so moving left and down is what we really want to focus on. When we use the left and down buttons to move, notice that it does what we want except the result of our calculations are negative… This isn’t a big deal! There is a way to reverse it by subtracting the values from 0.

``````  arduboy.print(0 - mapx / TILE_SIZE);
arduboy.print(",");
arduboy.print(0 - mapy / TILE_SIZE);
``````

Now that we can calculate these values to keep track of how many times we jump the map around, we can add that to our `Sprites::drawOverwrite()` function to offset which `tile` from `world` we are going to draw.

Currently, we are just using the `x` and `y` variables. Let’s create new `const int` variables called `tilex` and `tiley` to hold the values of `x` and `y` plus the calculations, then use those instead of `x` and `y` .

``````  for (int y = 0; y < tilestall; y++) {
for (int x = 0; x < tileswide; x++) {
const int tilex = x - mapx / TILE_SIZE;
const int tiley = y - mapy / TILE_SIZE;
Sprites::drawOverwrite(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles, world[tiley][tilex]);

}
}
``````

Just to clarify, to get the tile bitmap of the tile that is being drawn, we need to get the value inside of `world` at ( `tilex` , `tiley` ), then use that value as the index of the `tiles` array.

Now, what I want you to do is compile your code and put it onto the Arduboy. (I’m including mine right below this.) Use the bottom and right buttons to move around. You should see the screen scroll, but what happens if you keep going and view parts of the map that don’t exist, yet?

``````//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 mapx = 0;
int mapy = 0;

const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,

//Tree
0xff, 0x1f, 0x5b, 0x3f, 0xeb, 0xdd, 0xff, 0xf7, 0xbb, 0xef, 0xfd, 0x7f, 0xe3, 0xcb, 0xe3, 0xff,
0xff, 0xc7, 0x96, 0xc7, 0xff, 0xff, 0xef, 0xfd, 0xff, 0xe3, 0xcb, 0xe3, 0xff, 0xff, 0x7b, 0xff,

//Stone
0xff, 0xdf, 0x7b, 0x3f, 0x9f, 0x6f, 0x77, 0xab, 0xdb, 0xd7, 0xcd, 0x5f, 0xbf, 0x77, 0xff, 0xff,
0xff, 0xc1, 0xdc, 0xd3, 0xaf, 0x9f, 0xae, 0xb0, 0xbb, 0xbd, 0xbd, 0xba, 0xd7, 0xcc, 0x63, 0xff,
};

#define WORLD_WIDTH    14
#define WORLD_HEIGHT  7
#define GRASS      0
#define WATER      1
#define TREES      2
#define STONE      3
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ TREES, GRASS, GRASS, WATER, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, GRASS, GRASS, TREES },
{ GRASS, WATER, WATER, WATER, GRASS, WATER, GRASS, GRASS, GRASS, GRASS, GRASS, STONE, GRASS, GRASS },
{ GRASS, GRASS, GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS },
{ STONE, GRASS, GRASS, STONE, TREES, WATER, WATER, WATER, GRASS, WATER, WATER, GRASS, TREES, GRASS },
{ GRASS, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, TREES, WATER, GRASS, GRASS, STONE, TREES },
{ GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, TREES, TREES, TREES, GRASS, GRASS, WATER, WATER },
{ GRASS, WATER, WATER, TREES, GRASS, WATER, WATER, TREES, TREES, GRASS, GRASS, GRASS, GRASS, STONE }
};

#define TILE_SIZE  16
void drawworld() {
const int tileswide = WIDTH / TILE_SIZE + 1;
const int tilestall = HEIGHT / TILE_SIZE + 1;

for (int y = 0; y < tilestall; y++) {
for (int x = 0; x < tileswide; x++) {
const int tilex = x - mapx / TILE_SIZE;
const int tiley = y - mapy / TILE_SIZE;
Sprites::drawOverwrite(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles, world[tiley][tilex]);
}
}

arduboy.fillRect(0, 0, 48, 8, BLACK);
arduboy.setCursor(0, 0);
arduboy.print(mapx);
arduboy.print(",");
arduboy.print(mapy);
}

void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
if (arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}

void playerinput() {
if (arduboy.pressed(UP_BUTTON)) {
mapy += 1;
}
if (arduboy.pressed(DOWN_BUTTON)) {
mapy -= 1;
}
if( arduboy.pressed(LEFT_BUTTON)) {
mapx += 1;
}
if (arduboy.pressed(RIGHT_BUTTON)) {
mapx -= 1;
}
}

void gameplay() {
arduboy.setCursor(0, 0);
arduboy.print("Gameplay\n");

playerinput();
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();

}
``````

Isn’t it cool that you can navigate outside of our `world` array? Like I said near the end of Part 8, what’s being displayed is data from other variables and other parts of the Arduboy’s memory.

# 6. Bounding the map

Next, what we should do is prevent the player from seeing all this extra stuff outside of our `world` . We also should prevent them from moving the black square outside of it, as well! Let’s start by choosing to only draw the tiles that are in range during our `drawworld()` function’s `for` loops.

Remember, we are already taking into consideration which tiles are being drawn from `world` with `tilex` and `tiley` . What we could do is draw the tile `if` `tilex` / `tiley` are positive (greater than `-1` ) and less than the `WORLD_WIDTH` / `WORLD_HEIGHT` .

``````if (tilex >= 0 && tiley >= 0 && tilex < WORLD_WIDTH && tiley < WORLD_HEIGHT) {
Sprites::drawOverwrite(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles, world[tiley][tilex]);
}
``````

Now, to make sure the player cannot move outside of our `world` , we need to realize that when the world map is at (0, 0), the player is not at (0, 0). That is because we’re drawing it in the middle of what’s on screen. Incidentally, we are also drawing the character in the `drawworld()` function. Let’s remove that `arduboy.fillRect()` call and do in a better place.

Let’s make a new function right above `drawworld()` and call it `drawplayer()` . We want to also reference it inside of `gameplay()` , right after `drawworld()` . Remember, if we draw something to the screen before `drawworld()` , then it will be drawn prior, and drawn under the tiles. We want to see the player on top of them.

Alongside `drawworld()` , we should define size of our player as 16 using `#define PLAYER_SIZE 16` . We can use that when drawing our rectangle similar to what we did earlier:

``````arduboy.fillRect(WIDTH / 2 - PLAYER_SIZE / 2, HEIGHT / 2 - PLAYER_SIZE / 2, PLAYER_SIZE, PLAYER_SIZE, BLACK);
``````

That is a little hard to read, so let’s actually make another definition! Let’s store the rectangle’s X offset and rectangle’s Y offset and use those values when drawing our rectangle.

``````#define PLAYER_SIZE      16
#define PLAYER_X_OFFSET    WIDTH / 2 - PLAYER_SIZE / 2
#define PLAYER_Y_OFFSET    HEIGHT / 2 - PLAYER_SIZE / 2
void drawplayer() {
arduboy.fillRect(PLAYER_X_OFFSET, PLAYER_Y_OFFSET, PLAYER_SIZE, PLAYER_SIZE, BLACK);
}
``````

Storing the `PLAYER_X_OFFSET` and `PLAYER_Y_OFFSET` can come in handy when figuring out if the player’s character is touching the edge of the `world` .

For instance, if we are letting the map slide down the screen, we don’t want the top of it to slide below the top of the player’s square. The top of the map is at `mapy` and the top of the player’s square is at `PLAYER_Y_OFFSET` . This works with the X variables, too. Let’s update the `playerinput()` functions to include these restrictions.

``````void playerinput() {
if (arduboy.pressed(UP_BUTTON)) {
if (mapy < PLAYER_Y_OFFSET) {
mapy += 1;
}
}
if (arduboy.pressed(DOWN_BUTTON)) {
mapy -= 1;
}
if (arduboy.pressed(LEFT_BUTTON)) {
if (mapx < PLAYER_X_OFFSET) {
mapx += 1;
}
}
if (arduboy.pressed(RIGHT_BUTTON)) {
mapx -= 1;
}
}
``````

Testing this code, you will see that it works, but only on the top and left-side of the map.

For the bottom of the map, we only want the down button to move the map up if the bottom of the map is still below the bottom of the player’s square. These can be calculated with `mapy + TILE_SIZE * WORLD_HEIGHT` and `PLAYER_Y_OFFSET + PLAYER_SIZE` You can do this for the X axis, too!

``````void playerinput() {
if (arduboy.pressed(UP_BUTTON)) {
if (mapy < PLAYER_Y_OFFSET) {
mapy += 1;
}
}
if (arduboy.pressed(DOWN_BUTTON)) {
if (PLAYER_Y_OFFSET + PLAYER_SIZE < mapy + TILE_SIZE * WORLD_HEIGHT) {
mapy -= 1;
}
}
if (arduboy.pressed(LEFT_BUTTON)) {
if (mapx < PLAYER_X_OFFSET) {
mapx += 1;
}
}
if (arduboy.pressed(RIGHT_BUTTON)) {
if (PLAYER_X_OFFSET + PLAYER_SIZE < mapx + TILE_SIZE * WORLD_WIDTH) {
mapx -= 1;
}
}
}
``````

Here is our 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 mapx = 0;
int mapy = 0;

const unsigned char tiles[] PROGMEM  = {
// width, height,
16, 16,

//Grass
0xff, 0x7f, 0xfb, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xf7, 0xff, 0xfd, 0xff, 0xff, 0xf7, 0x7f, 0xff,
0xdf, 0xff, 0xff, 0xfb, 0x7f, 0xff, 0xff, 0xff, 0xef, 0xfe, 0xff, 0xff, 0xfb, 0xff, 0x7f, 0xff,

//Water
0x08, 0x10, 0x10, 0x08, 0x10, 0x08, 0x10, 0x10, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x40, 0x40, 0x20, 0x00, 0x01, 0x02, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02, 0x21, 0x40, 0x40,

//Tree
0xff, 0x1f, 0x5b, 0x3f, 0xeb, 0xdd, 0xff, 0xf7, 0xbb, 0xef, 0xfd, 0x7f, 0xe3, 0xcb, 0xe3, 0xff,
0xff, 0xc7, 0x96, 0xc7, 0xff, 0xff, 0xef, 0xfd, 0xff, 0xe3, 0xcb, 0xe3, 0xff, 0xff, 0x7b, 0xff,

//Stone
0xff, 0xdf, 0x7b, 0x3f, 0x9f, 0x6f, 0x77, 0xab, 0xdb, 0xd7, 0xcd, 0x5f, 0xbf, 0x77, 0xff, 0xff,
0xff, 0xc1, 0xdc, 0xd3, 0xaf, 0x9f, 0xae, 0xb0, 0xbb, 0xbd, 0xbd, 0xba, 0xd7, 0xcc, 0x63, 0xff,
};

#define WORLD_WIDTH    14
#define WORLD_HEIGHT  7
#define GRASS      0
#define WATER      1
#define TREES      2
#define STONE      3
int world[WORLD_HEIGHT][WORLD_WIDTH] = {
{ TREES, GRASS, GRASS, WATER, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, GRASS, GRASS, TREES },
{ GRASS, WATER, WATER, WATER, GRASS, WATER, GRASS, GRASS, GRASS, GRASS, GRASS, STONE, GRASS, GRASS },
{ GRASS, GRASS, GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS },
{ STONE, GRASS, GRASS, STONE, TREES, WATER, WATER, WATER, GRASS, WATER, WATER, GRASS, TREES, GRASS },
{ GRASS, GRASS, GRASS, GRASS, TREES, GRASS, GRASS, GRASS, TREES, WATER, GRASS, GRASS, STONE, TREES },
{ GRASS, GRASS, GRASS, WATER, STONE, GRASS, GRASS, TREES, TREES, TREES, GRASS, GRASS, WATER, WATER },
{ GRASS, WATER, WATER, TREES, GRASS, WATER, WATER, TREES, TREES, GRASS, GRASS, GRASS, GRASS, STONE }
};

#define PLAYER_SIZE      16
#define PLAYER_X_OFFSET    WIDTH / 2 - PLAYER_SIZE / 2
#define PLAYER_Y_OFFSET    HEIGHT / 2 - PLAYER_SIZE / 2
void drawplayer() {
arduboy.fillRect(PLAYER_X_OFFSET, PLAYER_Y_OFFSET, PLAYER_SIZE, PLAYER_SIZE, BLACK);
}

#define TILE_SIZE  16
void drawworld() {
const int tileswide = WIDTH / TILE_SIZE + 1;
const int tilestall = HEIGHT / TILE_SIZE + 1;

for (int y = 0; y < tilestall; y++) {
for (int x = 0; x < tileswide; x++) {
const int tilex = x - mapx / TILE_SIZE;
const int tiley = y - mapy / TILE_SIZE;
if (tilex >= 0 && tiley >= 0 && tilex < WORLD_WIDTH && tiley < WORLD_HEIGHT) {
Sprites::drawOverwrite(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles, world[tiley][tilex]);
}
}
}

arduboy.fillRect(0, 0, 48, 8, BLACK);
arduboy.setCursor(0, 0);
arduboy.print(mapx);
arduboy.print(",");
arduboy.print(mapy);
}

void titlescreen() {
arduboy.setCursor(0, 0);
arduboy.print("Title Screen\n");
if (arduboy.justPressed(A_BUTTON)) {
gamestate = GAME_PLAY;
}
}

void playerinput() {
if (arduboy.pressed(UP_BUTTON)) {
if (mapy < PLAYER_Y_OFFSET) {
mapy += 1;
}
}
if (arduboy.pressed(DOWN_BUTTON)) {
if (PLAYER_Y_OFFSET + PLAYER_SIZE < mapy + TILE_SIZE * WORLD_HEIGHT) {
mapy -= 1;
}
}
if (arduboy.pressed(LEFT_BUTTON)) {
if (mapx < PLAYER_X_OFFSET) {
mapx += 1;
}
}
if (arduboy.pressed(RIGHT_BUTTON)) {
if (PLAYER_X_OFFSET + PLAYER_SIZE < mapx + TILE_SIZE * WORLD_WIDTH) {
mapx -= 1;
}
}
}

void gameplay() {
playerinput();
drawworld();
drawplayer();

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();

}
``````

Upload your code to your Arduboy and play around with it! If you want a challenge while waiting for the next tutorial, you should try to make a bigger map and put a cool design into it.

# Next Tutorial

This isn’t really a game, but we’ve got the hard part done so it’ll come along really quickly! In the next tutorial, we’ll draw our dinosaur, create playerstates, read the player’s position, create buildings, demolish buildings… and so much more!

# 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.

10 Likes

This is so awesome, thank you. Instead of wracking my brain trying to figure out exactly how drawing the world works, could you elaborate a little bit on that `drawworld()` function?

Specifically the part where you’re plotting the x and y coordinates in the `drawOverwrite` function. I get the `x * TILE_SIZE` part, it’s basically moving to the right every 16 pixels to plot out the next tile. It’s the buffer part that stumps me though, when you’re adding `+ mapx % TILE_SIZE`. What exactly is this determining? How many tiles to draw on the opposite side of the player?

For example, when `mapx` is 116, 116 mod 16 = 4. How is that addition of 4 helping to determine which tiles to draw?

It has nothing to do with which tiles are drawn,
but rather where on the screen those tiles are drawn.

Doing:

``````for(int y = 0; y < tilestall; ++y)
{
for(int x = 0; x < tileswide; ++x)
{
arduboy.drawBitmap(x * TILE_SIZE, y * TILE_SIZE, tiles[world[y][x]], TILE_SIZE, TILE_SIZE, WHITE);
}
}
``````

Draws the map flat onto the screen with no offset.
The player character can walk around and the tiles will change,
but the map as drawn on the screen never moves.

Using `+ mapx` and `+ mapy` offsets the map so it moves when the player presses the directional buttons,
but it ends up drawing part of the map offscreen once it gets past a certain amount.

Using `% TILE_SIZE` limits `mapx` and `mapy` to be between 0 and -15,
so instead of drawing the map offscreen it only ever offsets the map by a few pixels.

The important thing to remember is that the loop isn’t drawing the whole map, it’s only drawing part of the map.
`x * TILE_SIZE + mapx % TILE_SIZE` and `y * TILE_SIZE + mapy % TILE_SIZE` aren’t map coordinates - they’re screen coordinates.

It would be a lot easier to understand what’s going on if you could see what would get drawn ‘beyond the screen’ through this technique.
Unfortunately you can’t see that because there’s no screen to draw to beyond the screen boundaries,
however what you can do is shrink the part of the map being drawn to get a similar effect…

Try replacing `drawworld` with this:

``````void drawworld() {
const int tileswide = WIDTH / TILE_SIZE - 1;
const int tilestall = HEIGHT / TILE_SIZE - 1;

for (int y = 0; y < tilestall; y++)
for (int x = 0; x < tileswide; x++)
{
const int tilex = x - mapx / TILE_SIZE;
const int tiley = y - mapy / TILE_SIZE;

if (tilex >= 0 && tiley >= 0 && tilex < WORLD_WIDTH && tiley < WORLD_HEIGHT)
Sprites::drawOverwrite(TILE_SIZE + x * TILE_SIZE + mapx % TILE_SIZE, TILE_SIZE + y * TILE_SIZE + mapy % TILE_SIZE, tiles, world[tiley][tilex]);
}

arduboy.fillRect(0, 0, 48, 8, BLACK);
arduboy.setCursor(0, 0);
arduboy.print(mapx);
arduboy.print(",");
arduboy.print(mapy);
arduboy.print('\n');
arduboy.print(mapx % TILE_SIZE);
arduboy.print(",");
arduboy.print(mapy % TILE_SIZE);
}
``````

Essentially I’ve changed

``````const int tileswide = WIDTH / TILE_SIZE + 1;
const int tilestall = HEIGHT / TILE_SIZE + 1;
``````

To

``````const int tileswide = WIDTH / TILE_SIZE - 1;
const int tilestall = HEIGHT / TILE_SIZE - 1;
``````

Which decreases the number of tiles drawn on each axis by 2,
and changed this

``````x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE
``````

to this

``````TILE_SIZE + x * TILE_SIZE + mapx % TILE_SIZE, TILE_SIZE + y * TILE_SIZE + mapy % TILE_SIZE
``````

so I’m offsetting the drawing by the size of one whole tile.
It means the player rectangle is a bit out of sync, but that’s not important.
What’ important is that if you run it you’ll see how the map keeps ‘snapping back’ to a previous position.
That ‘snapping back’ is caused by the modulo limiting the offset to 0 to -15 pixels.

I’ve also added some code to write `mapx & TILE_SIZE` and `mapy % TILE_SIZE` on the screen so you can see how the values count from 0 to -15 over and over again.

Also, try removing the `+ mapx % TILE_SIZE` and `+ mapy % TILE_SIZE` afterwards.
You’ll see the tiles change, but the actual map as drawn on the screen never moves.

(If not I could draw a diagram that labels all the measurements but then I’d have to fish out the scanner.)

1 Like

Thank you so much for this. Shrinking the size of the screen did make it clearer, since I was confused about how the actual drawing was happening. And I think the modulo makes more sense now - it’s causing the smoother effect of the map moving because it’s keeping the offset smaller than `mapx` and `mapy`? Or am I still thinking of that wrong?

And when you mention “drawing part of the map offscreen”, do you mean actually drawing tiles that you can’t see on the screen or removing tiles that are no longer within the screen view?

No need to bust out your scanner, I’m sure I’ll get this after reading through your post and dissecting it some more.

Sort of.

The non-smooth map moving is what happens when the tiles are drawn to the same location on screen every time.

The smooth map moving is what happens when the tiles are offset by a value from 0 to -15.
(The offset is the remainder of the map offset when divided by the tile size.)

If `mapx` and `mapy` were positive then they’d represent the player’s position in world coordinates and `mapx % tileWidth` and `mapy % tiley` would actually be the player’s offset into the particular tile that they’re standing on, but crait has chosen the approach of moving the map rather than moving the player.

I mean that technically the code tries to draw tiles (or part of the tiles) outside of the screen’s boundaries,
but obviously it can’t actually draw outside the screen because there’s nothing to draw to,
(it’s like a painter trying to paint outside his canvas - he just ends up waving his brush in the air)
but the `Sprites` code accounts for that and ignores pixels that would be drawn ‘outside the screen’.

I had a quick go anyway:

So you can see the `+ mapx % TILE_SIZE` part actually causes the map to start being rendered a few pixels outside the top left corner of the screen.

If it were just `+ mapx` then after the first tile the map representation would end up being drawn further and further outside the screen until it wasn’t actually being rendered on-screen at all.

The diagram on the left is supposed to illustrate how the code isn’t actually drawing the whole map,
it’s looking at a rectangular section and only drawing the tiles touched by that section.

(The grids are really imprecise because I’m too impatient to draw accurate grids, and I couldn’t be bothered to grab my graph paper.)

I also found something online that might help to illustrate how the screen is only rendering a portion of the map:

There’s another thread on here where I discussed tile rendering in detail,
I can dig that out too if your interested in tile maps,

Edit:

I don’t directly explain everything, but the diagrams might be useful for understanding tile rendering.

Thank you! It’s making more sense. One of the diagrams you drew in that other thread you linked to is really helpful. I’m going to digest it some more and I’ll be back with more questions, if I still can’t get it straight.

At the end of the day I understand what it’s doing, I’m just struggling to understand exactly (mathematically) what every number in this function is doing. Maybe that isn’t necessary.

2 Likes

The important thing to remember is that you’re working with multiple coordinate systems.

`x * TILE_SIZE` and `y * TILE_SIZE` are screen coordinates.
`mapx` and `mapy` are map offsets in world coordinates.
Then `% TILE_SIZE` is used to get the player’s offset within a tile (sort of),
which is then used to offset the rendered representation of the world (the map is not the territory).

You could try working out the numbers on paper (maybe with a calculator) to see how they behave.
Often just stepping through the code manually makes it easier to understand, and that’s probably the closest you can get tp being able to do that.

You could also try this:

``````constexpr uint8_t TILE_SIZE = 16;

void drawworld()
{
const int tileswide = WIDTH / TILE_SIZE + 1;
const int tilestall = HEIGHT / TILE_SIZE + 1;

for (int y = 0; y < tilestall; y++)
{
for (int x = 0; x < tileswide; x++)
{
const int tilex = x - mapx / TILE_SIZE;
const int tiley = y - mapy / TILE_SIZE;
if (tilex >= 0 && tiley >= 0 && tilex < WORLD_WIDTH && tiley < WORLD_HEIGHT)
{
Sprites::drawOverwrite(x * TILE_SIZE + mapx % TILE_SIZE, y * TILE_SIZE + mapy % TILE_SIZE, tiles, world[tiley][tilex]);
}
}
}

arduboy.fillRect(0, 0, 48, 8, BLACK);
arduboy.setCursor(0, 0);

// Map coordinates
arduboy.print(mapx);
arduboy.print(',');
arduboy.print(mapy);
arduboy.print('\n');

// Map coordinate remainder
arduboy.print(mapx % TILE_SIZE);
arduboy.print(',');
arduboy.print(mapy % TILE_SIZE);
arduboy.print('\n');

// Render coordinate without modulo
arduboy.print(1 * TILE_SIZE + mapx);
arduboy.print(',');
arduboy.print(1 * TILE_SIZE + mapy % TILE_SIZE);
arduboy.print('\n');

// Render coordinate with modulo
arduboy.print(1 * TILE_SIZE + mapx % TILE_SIZE);
arduboy.print(',');
arduboy.print(1 * TILE_SIZE + mapy % TILE_SIZE);
arduboy.print('\n');
}
``````

Smasher.hex (26.4 KB)

Hopefully you’ll be able to see that not using the modulo increases the size indefinitely, while using the modulo just provides the 0 to -15 offset (the player’s offset into the tile they’re currently on).

2 Likes

I’m getting this now :). I did working through this stuff manually but was doing something wrong with the modulo, so it wasn’t matching up with the numbers being printed. With all these numbers being printed it started making sense, I was doing the process wrong with the additions and modulo stuff. I appreciate your patience.

1 Like

If you’re on Windows, the calculator actually has a `Mod` button (in both scientific and programmer mode).

It’s a shame regular calculators don’t tend to have it.

Ah, ok, that makes sense.

No problem.
Answering people’s questions and explaining things is something I do a lot.
If you have any more questions, don’t hesitate to ask.

I still think you missed your calling in life (a teacher!). Actually, its not too late.

I would, but it would mean spending N years getting teaching qualifications,
and then I’d have to mark students’ work and put up with their bad behaviour,
and try to make the students not hate me.

Maybe I’ll become a tutor instead. (£20-£40 an hour is a nice incentive.)

4 Likes

5 posts were split to a new topic: Tile Collisions?

Hello! Thank you for the tutorial! First time coder here.
I’ve have done your tutorial but decided it would be fun to have the map be created randomly. I have it working but I have a puzzle I don’t understand. Here is my code based mostly from yours:

``````  for (int x = 0; x < WORLD_HEIGHT; x++) {
for (int y = 0; y < WORLD_WIDTH; y++) {
world[x][y] = random(0, 1);
}
}
``````

So I’m trying to change every value of the array “world” to either 0 or 1 (which would be 0=grass and 1=water).

When I do random(0, 1) it gives me grass for ALL tiles
But if I do random(0, 2) it works – it gives me grass and water. I don’t even have a “2” ready to go yet.

What is going on? I don’t understand. Thank you for your help!

1 Like

`random()` stupidly accepts a min value inclusive and a maximum value exclusive.

So if you want it to return 0 and 1s, you need to do `random(0, 2)` as you have worked out by trial and error.

3 Likes

Ah ok got it! Thank you

2 Likes

hello, quick question, what happens if i create the “const ins variables” at the beggining of the code instead of inside the functions? it’s easier for me to keep track of everything on that way, but don’t have idea if that affects something on the arduboy memory

It’s contrary to the expectations of a layman, but having an inclusive lower bound and an exclusive upper bound occurs a lot in computer science for various reasons.

I read a thing once (I think it was by Edsger Dijkstra) explaining a number of useful properties that arrangement has.

That gives you a ‘global variable’ instead of a ‘local variable’.

Global variables exist for the whole of the program so they have to be allocated in RAM and thus can be more costly to access (because RAM has to be accessed with an address and usually uses more machine code instructions to access).

Local variables only exist for the duration of the scope that they’re in (e.g. the duration of the function, the duration of an `if` block…) and thus can be kept in things called ‘registers’, which are a kind of faster, easier to access memory.

(Some CPUs use what’s referred to as a ‘load-store architecture’, which means they have to load the data from RAM into registers before they can operate on the data, and then they have to store the data back into RAM.
The loading and storing requires extra machine code instructions. I’m fairly sure the Arduboy’s CPU is one of these.)

In what way?

If you only need a variable for the duration of one function then it makes sense to only care about it when you’re reading that function’s code.
The rest of the time you can simply forget it exists.
You don’t need to remember every single variable.

The variables you put at the top of the code, the global variables,
should be the things that have to exist for the entire duration of the program,
or at least exist for more than one function.
E.g. the player, enemies, the game state.

Hi there,

I am new to the arduboy and these tutorials helped me a lot. Thanks for that.
I customised my DInosmasher a bit but it lacks a lot of features.

Do you know when/if the rest of the tutorial will be out ?
Take care

1 Like

Tell me please, when i try to replace the black square with a sprite i have nothing showing on the map. But that nothing can move around the map. So, how to make the image visible?
my try:

``````void drawplayer() {
const unsigned char PROGMEM noname[] =
{
// width, height,
16, 16,
// TILE 00
0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xb9, 0xb1, 0xb1, 0xb9, 0x83, 0x1f, 0xff, 0xff, 0xff, 0xff,
0xff, 0x07, 0x73, 0x7b, 0x7b, 0x78, 0x3f, 0x9f, 0xdf, 0x9f, 0xbf, 0x38, 0x79, 0x39, 0xb3, 0x87,
};
}
``````