Tuesday, January 15, 2008

Haven't Forgotten

Hi folks!

No, I haven't forgotten about the scroll algorithm, nor has it been too challenging for me. I've been crunching time and code for my day job (security virtual machine app). So, tonight has been my first night since my previous posting to actually work on my idea. So far, so good.

What I discovered staring at the tiles on my bathroom wall, was that since the DS background is 64x64 tiles, I can divide that area up into 4 regions of 32x32 tiles,

0 1
2 3

With 0 = 32x32, 1 = 32x32, 2 = 32x32, and 3 = 32x32.

Thus, if I make my game, tile maps on 32x32 boundaries, when I hit a boundary limit on scrolling, I can simply blast a whole map into the region that is out of view, but will be coming into view. In order to blast the proper map to the proper region, I've come up with a scheme. As, I also stated in my previous posting, I highly doubt if this is an original thought here, but I haven't seen any game world scrolling code anywhere on the internet, and I looked around to see. Post to me, let me know if this is a common approach, or if there is an easier, faster, more elegant method.

The scheme...

1) Define my world size, that is, how big of an area I wish to be able to scroll around. For this example I've picked 1280x1024 pixels. Remember, the tile size that I'm going to use is 8x8 pixels. So, doing the math (ahhh...if you don't like math, then stop now, game programming isn't going to be your cup of tea...well, you can do it, if you don't like it, but know that you will be doing LOTS of it, all the time)...

1280 pixels / 8 (1 tile) = 160 pixels / 32 pixels (region size) = 5
1024 pixels / 8 (1 tile) = 128 pixels / 32 pixels (region size) = 4

Thus, my world map will consist of an array 5x4 of world tiles.

World Map (tile entries)
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20

2) Remember, I said I was going to divide the DS background into 4, 32x32 regions,
0 1
2 3

I will keep a table that correlates (I wanted to say, 'maps,' but didn't want to be too confusing...oh but, you said it anyway!) World Map to the background region it should be copied to.

MapToRegion
0 1 0 1 0
2 3 2 3 2
0 1 0 1 0
2 3 2 3 2

There's a pattern there and thus, you can easily derive a formula to find the region to copy to, when you know the map you wish to copy, but I think a look up table might be faster...who knows, get it working first and then you can do all the size vs speed trade offs.

So, looking at my World Map table if I were in tile map 14, I would just have to plug that entry into my MapToRegion table and I find that the region, tile map 14 should be copied to is, 1. Why/how? Because the 14th entry in the MapToRegion table is equal to 1. If I chose, tile map 7, then the region would be 3.

I know, it still may not be clear yet, I will continue describing my approach. The DS visible screen size is 32x24 tiles (32x8 = 256, 24x8 = 192 pixels). If I were to take that screen and place it at the beginning of my World Map, "that's a complete dramatization, you can't really do that." I know, but imagine that if that whole 5x4 area were laid out on a table, like a map, and you had a spy glass, that could see only 32x24 tiles at a time, if the spy glass started on tile 1, you would see all of 1 in the 'x' direction and 3/4 of it in the 'y' direction. Huh? Seeing 3/4 in the 'y' direction? Remember, a tile is 32x32, and the spy glass (screen) is 32x24, so 24/32 = .75 = 3/4...ahh, repetition is the essence of pedagogy.

We have our 5x4 map, and our spy glass, but lets say that our table isn't big enough to hold the complete map laid out in all its 5x4 glory. We've got a teeny, tiny, table that can only hold a map of 2x2. Does the tiny 2x2 table remind you of anything? "Correcto mundo, check out the big brain on...," ahhh...what's your name?
That's right, the DS background, divided into our 4 background regions...

0 1
2 3

So, since our table can only hold 2x2 tiles, I can fold the map up into quarters, and eights, and...NOT! What if, I just cut each tile out and place them separately on the floor, when I need to see a section I can pick it up and move it to the table. In essence, this is what you are doing when working with the DS and scrolling around your world. The floor is the map area, the table is the background area, and the spy glass is the vram. You have limited space so you must manage it all.

From the World Map, I move 1 into region 0, 2 into region 1, 6 into region 2, and 7 into region 3. Thus, looking at my table I have...

1 2
6 7

The spy glass can currently see only tile 1. Now, if I start to move the spy glass across the map to the right, changing the 'x' coordinates, but keeping the 'y' the same, we are doing a simple horizontal scroll to the right, when I scroll 1 pixel right, the right border of the spy glass is now, gone past our 32 boundary and into the other region which is tile 2. I'm now just seeing a sliver of tile 2. However, if I continue to scroll right I see more of tile 2, and less of tile 1. When, I get to the right border of tile 2, meaning my spy glass is seeing completely tile 2, if I were to continue to scroll right, over the boundary, what the DS does is loop, so I would see tile 1 again. You can't do to much with a game world that size, actually you can, you just won't be scrolling...anyway, continuing with my scheme, when I detect a boundary crossing, I pick up from the floor the next tile, and put it in the proper region on the table.

How do I know which tile, and which region? "I wish someone would tell him, so I could go to sleep!"

By doing a little math. Since the World Map is 5x4 in size, that means, if you look back at the math above, my world is
1280x1024 pixels, when the map is completely unfurled. When, I scroll the spy glass if I keep track of the pixel count, adding when I scroll to the right, and subtracting when I scroll left, I can determine when I have hit boundaries evenly divisible by 32, that means, the division is a whole number, no remainders.

This algorithm is only for scrolling to right, and down, just to give you a jist, a taste, wet your whistle..."GET ON WITH IT!"

const int nMapEntries_x = 5;
const int nRegionSize = 32;

int nMapToRegion[] = {
0, 1, 0, 1, 0,
2, 3, 2, 3, 2,
0, 1, 0, 1, 0,
2, 3, 2, 3, 2,
};

int nMap = 0;
int nRegion = 0;

// determine right coord boundary hit
if(nWorldPos_rx % nRegionSize == 0)
{
// determine the map we're in
nMap = nWorldPos_rx / nRegionSize + 1;

// what region is this
nRegion = nMapToRegion[nMap-1];
}

// determine bottom coord boundary hit
if(nWorldPos_by % nRegionSize == 0)
{
// determine the map we're in
nMap = nWorldPos_by / nRegionSize * nMapEntries_x + 1;

// what region is this
nRegion = nMapToRegion[nMap-1];
}

Try it out on paper...you will see, you can easily determine which map and region. And, determining left and up scroll is similar.

Next time...

I will explain the algorithm for calculating the nMap in case some don't get it.
I also promised last blog posting to have scanned in diagrams for those that understand more easily with drawings, so I will scan in some drawings this weekend and post. In the mean time, I will code up my scheme and see if it works.

Till next time...Peace out! Can we go to sleep now!

No comments: