# Make Your Own Arduboy Game: Part 9 - Mapping DinoSmasher

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

@NoobGeek,
Welcome to the Arduboy Forum!

First:
When you post code in this forum, please enclose it in markdown code tags:

Insert your code starting on the following line.
On the line following you code, put another three backticks.
The backtick character is commonly on the key below the Esc key at the top left of a U.S. keyboard. If you can’t find it on your keyboard, you can copy and paste from the tags here:

```cpp
The first line of your code
More code
The last line of your code
```

You’ll have to be more specific and/or provide more code showing how you’re trying to display and move your sprite. I wrote a simple sketch that draws and allows you to move your sprite using the D-pad. It may provide information that helps you.

#include <Arduboy2.h>

Arduboy2 arduboy;

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

int16_t xPos = 0;
int16_t yPos = 0;

void setup() {
arduboy.begin();
arduboy.setFrameRate(45);
}

void loop() {
if(!arduboy.nextFrame()) {
return;
}

arduboy.pollButtons();
arduboy.clear();

if (arduboy.justPressed(UP_BUTTON)) {
--yPos;
}
if (arduboy.justPressed(DOWN_BUTTON)) {
++yPos;
}
if (arduboy.justPressed(LEFT_BUTTON)) {
--xPos;
}
if (arduboy.justPressed(RIGHT_BUTTON)) {
++xPos;
}

Sprites::drawOverwrite(xPos, yPos, noname, 0);

arduboy.display();
}

NoobGeek1.hex (19.5 KB)

1 Like

I had a closer look at @crait’s “completed code” and now know what you’re trying to do.

The spite that you’ve created for the player is just data. You need to draw it with one of the Sprites functions. Since the bitmap array you’ve provided doesn’t include a mask, the drawOverwrite() function is likely the best to use.

You need to put your noname array in the sketch as “global” data. For my testing, I put it before the tiles array

[...]
int mapx = 0;
int mapy = 0;

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

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

Then, you need to replace the fillRect() function in drawplayer() with the function to draw the sprite

#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);
Sprites::drawOverwrite(PLAYER_X_OFFSET, PLAYER_Y_OFFSET, noname, 0);
}

You should now have your noname sprite drawn instead of a black rectangle.

Without masking, all of the pixels in your sprite are drawn over the background. So, the next thing you’ll probably want to do is create a sprite with a mask and use drawPlusMask() to draw it.

Edit:
Here’s the array (I renamed it to player) and drawplayer() functions with the outside of the image masked

const unsigned char PROGMEM player[] = {
// width, height,
16, 16,
// FRAME 00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xb8, 0xfe, 0xb4, 0xfe,
0xb4, 0xfe, 0xb8, 0xfe, 0x80, 0xfc, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xf8, 0x70, 0xfc, 0x78, 0xfc, 0x78, 0xfc, 0x78, 0xff, 0x3f, 0xff, 0x1f, 0x7f,
0x1f, 0x3f, 0x1f, 0x7f, 0x3f, 0x7f, 0x38, 0xff, 0x78, 0xfe, 0x38, 0xfe, 0x30, 0x7c, 0x00, 0x78
};

void drawplayer() {
}

NoobGeek2.hex (26.5 KB)

2 Likes

Thanks, it worked! I am surprised that you answered my question so quickly, and with a detailed answer. I am very grateful to you for that! From now on I will ask questions in the correct form. And I will try even more to delve into the software part before asking the community! Thanks again!

3 Likes

2 posts were split to a new topic: How do I create and control multiple objects (bullets)

There’s no part 10 is there :’( @crait

Or you can:

• Look up C++ tutorials online
• Read through some of the other forum topics, particularly in the help sections since there’s lots of people asking common “How do I do X?” programming questions dotted around