[Basic] Tilemap representation in array

I'm sorry for this basic question.

To exercise, I tried to generate a basic tilemap in 1 array, but my tiles don't line up along the x-axis as they are supposed to. I can somehow accept why it doesn't work but I can't solve the problem.

Here's the code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
            
for int screen = 0; screen < 4; ++screen {
    for int y = 0; y < tilesY; ++y {
        for int x = 0; x < tilesX; ++x {

        int value = 0;

        if y == 0 {
            value = 1
        }
                        
        grid[screen * tilesY * tilesX + y * tilesX + x] = value;
                        
        }
    }
}


On the first screen the y is correctly drawn at the bottom row. But when I change screens in X, the row is drawn on a different y coordinate each screen. ( 1 row lower every screen, until it wraps to the top row, again )
When I change screens in Y the row is drawn on the same coordinate.

I know this is a basic question of the layout of an array but I can't visualize how I should correctly do this in 1 array. Or should I make an array of width*height tiles for each screen?

I'm sorry if this is too simple a question. :blush:

Edited by elle on
This code is correct.
If your four screens are 3 wide and 2 tall:
1
2
ABC GHI MNO STU
DEF JKL PQR VWX


Then it will put these screen in array like this:
1
ABCDEFGHIJKLMNOPQRSTUVWX


You can easily debug this put putting characters in array:
1
2
3
4
5
6
7
8
9
int ch = 'A';
for (int screen = ... ) {
  for (int y = ... ) {
    for (int x =  ...) {
      grid[...] = ch;
      ch++;
    }
  }
}

And then look up how grid array looks in memory.

Show the code you are using to render from grid array (reading part).
I managed to visualize the issue. My drawing code shows 17x9 tiles every screen.

grid[0*tilesX+17] = 1;
grid[0*tilesX+18] = 1;
...

I'd want these tiles to appear on the first row on the next screen to the right (y=0), but instead they appear on the second row of the first screen. (aka they "wrap")

Can I layout my array so the tiles can be accessed more intuitively?

This is the situation now:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
...

^
|
screen
^
|
screen


I'd like it to be:

screen -> screen -> ...


It might not solve it, but even then I'd be a step closer for sure?

Edit: Or should I access the array differently?

Edited by elle on
1
2
grid[0*tilesX+17] = 1;
grid[0*tilesX+18] = 1;
This is same as:
1
2
grid[0*tilesX+y*17+0] = 1;
grid[0*tilesX+y*17+1] = 1;


where y=1. Thus you are setting values on second row of screen.
If you want to have them on first row, you need to use value y=0:
1
2
grid[0*tilesX+0*17+0] = 1;
grid[0*tilesX+0*17+1] = 1;

=>
1
2
grid[0*tilesX+0] = 1;
grid[0*tilesX+1] = 1;

Edited by Mārtiņš Možeiko on
But what is the general way to access the first row of the second screen to the right?

By the way, when I only set one tile, I can access it on different screens. I guess I'd better use something else than an ordinary array, or is it possible to do it this way?
For second screen you do this:
1
grid[1*tilesY*tilesX + y*tilesX + x]


For N'th screen you do this:
1
grid[N*tilesY*tilesX + y*tilesX + x]

Same code as for filling grid array in your initial post.

Edited by Mārtiņš Možeiko on
Thank you for your time, but the following doesn't show a tile on the first row of the second screen but rather on the second screen, 0th column, 7th row.

I wanted to trade some performance and flexibility for something more straightforward (at least for now) but it's not really working out, apparently I don't understand this completely yet.

1
grid[1*tilesY*tilesX + 0*tilesX + 0]


I want to begin with just one Vector2 to represent the location on the map, but it appears to be harder than the sparse storage. (at least to me)
There is no way "grid[1*tilesY*tilesX + 0*tilesX + 0]" will show tile on 7th row, unless your rendering code is broken.

Just draw the memory layout on paper.
1
2
ABC GHI
DEF JKL

So tilesY = 2, tilesX = 3, number of screens = 2.

What is tile on first column (x=0), first row (y=0) of second screen (s=1)? It is tile G.

(s*tilesY*tilesX + y*tilesX + x) = (s*2*3 + y*3 + x) = (1*6 + 0*3 + 0) = 6

How array is layed out it memory?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 A B C D E F G H I J K L
\-+-/ \-+-/ \-+-/ \-+-/
  |     |     |     |
  |     |     |     +-- second row ----\
  |     |     |                        +---- second screen
  |     |     + ------- first row  ----/
  |     |
  |     +-------------- second row ----\
  |                                    +---- first screen
  +-------------------- first row  ----/

What is character at index=6 ?
1
2
ABCDEFGHIJKL
012345678901

It is "G"!

Try plugging different values in this equation and see how it works. Step through code that fills grid array and see what index it access for every iteration. And draw everything on paper (just use smaller values for tilesX and tilesY variables instead of 17x9).

Edited by Mārtiņš Možeiko on
This makes sense and I get it now, but I want to be able to access the tile via only the x&y coordinates without knowledge of the screen it's on in the "rendering" code like in HH.

1
2
3
4
5
6
7
8
9
        for (int y = -4; y < 5; y++) {
            for (int x = -8; x < 9; x++) {

                uint32 tileX = camera.x + x;
                uint32 tileY = camera.y + y;

                int tile = grid[tileY * tilesX + tileX]

                if (tile == 1) { }
What do you mean - only use x and y coordinates? Then how will you know where to access screen in grid array?

Grid array must be accessed with "grid[offsetOfScreen + y * tileX + x]" formula. Where offsetOfScreen is index in array where screens starts. In this case it is "screen * tileY * tilesX" - which has screen index.

And if y and x can be negative then you need to offset them so they start with 0, otherwise the equation to access grid array won't work - it will start accessing wrong tiles.

Edited by Mārtiņš Možeiko on
In HH, we only have a notion of x and y coordinates, yet, one 'chunk' spans multiple screens.

I want the array to be as straightforward as possible, in the HH tilemap generating code for example, we could just do
1
2
3
4
5
6
7
if y == 0 || y == tilesY - 1 {
    if x == tilesX/2 {
        tile = 0
    } else {
        tile = 1
    }
}


And in HH we loop over the tiles to draw like this:
1
2
3
4
5
6
7
8
9
      for (int y = -4; y < 5; y++) {
            for (int x = -8; x < 9; x++) {

                uint32 tileX = camera.x + x;
                uint32 tileY = camera.y + y;

                int tile = grid[tileY * tilesX + tileX]

                if (tile == 1) { }

Edited by elle on
I figured out the problem because I understand it now, thanks in part to you.

The bug wasn't obvious to me, because I was comparing my code with the HH one, and there it looks simpler than it is because of a large part of the code hidden in separate functions.

Anyway, instead of accessing grid[y * tilesX + x] in the drawing code, I should've replaced the tilesX which is the amount of tiles in x in 1 screen with the amount of tiles in x in the whole grid.

Obvious in hindsight, and for sure for most of you reading this.

Thanks for your time. :)