gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > Dynamic tile assignments while scrolling?

#35837 - ImInABand - Fri Feb 11, 2005 8:09 am

This is a topic that ive been pondering, but never really came up with a method of doing. In most tile mode games that use scrolling, the bg's are written and rewritten as the character moves through the level. anybody know of a few methods to accomplish this?

#35864 - tepples - Fri Feb 11, 2005 5:28 pm

Keep track of the visible area of the screen and the valid area of the map, both in world coordinates. Then whenever the visible area of the screen gets too close to the edge of the valid area of the map, write a new strip of map tiles and update the valid area. Is there anything specific that you need explained about this?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#35866 - jma - Fri Feb 11, 2005 5:38 pm

Something to keep in mind is that the entire background is not "rewritten". Only a small fraction of it is. Take a side-scroller (moving from left to right) for example:

Using a single screenblock, increment the BG_n_SCROLL_X register as the player moves right. Once the scroll has been moved 8 pixels, the 20 vertical tiles on the right edge are just updated with the next batch from the level data. The only "difficulties" are in keeping track of which vertical column of tiles should be updated (0-31, then wrap again), when to update them (preferably before the player actually gets to them), and keeping track of where you are in your level data.

The GBA will wrap screen block data, so you don't need to worry about that (thank you, Nintendo), and the scroll registers all work out nicely so you can just keep incrementing them until they overflow (64K, if I remember correctly).

Those are all simple problems, but are typically what people get hung up on.

HTH,
Jeff
_________________
massung@gmail.com
http://www.retrobyte.org

#39156 - ImInABand - Tue Apr 05, 2005 6:38 am

Yes, I came to a conclusion on simple dynamic tile assignment.

I came along with this when i made my text editor [yet unavailable on the web - polishing it first, getting the input configurations set well]

and i came up with a function that i simply call Write, but the way it works, it should rather be HWrite.
Code:

void Write(int ROW, int LETTER0, int LETTER1, int LETTER2, int LETTER3, int LETTER4, int LETTER5, int LETTER6, int LETTER7, int LETTER8, int LETTER9, int LETTER10, int LETTER11, int LETTER12, int LETTER13, int LETTER14, int LETTER15, int LETTER16, int LETTER17, int LETTER18, int LETTER19, int LETTER20, int LETTER21, int LETTER22, int LETTER23, int LETTER24, int LETTER25, int LETTER26, int LETTER27, int LETTER28, int LETTER29, int LETTER30, int LETTER31) {

MapLayer0[ROW*32 + 0] = LETTER0;
MapLayer0[ROW*32 + 1] = LETTER1;
MapLayer0[ROW*32 + 2] = LETTER2;
MapLayer0[ROW*32 + 3] = LETTER3;
MapLayer0[ROW*32 + 4] = LETTER4;
MapLayer0[ROW*32 + 5] = LETTER5;
MapLayer0[ROW*32 + 6] = LETTER6;
MapLayer0[ROW*32 + 7] = LETTER7;
MapLayer0[ROW*32 + 8] = LETTER8;
MapLayer0[ROW*32 + 9] = LETTER9;
MapLayer0[ROW*32 + 10] = LETTER10;
MapLayer0[ROW*32 + 11] = LETTER11;
MapLayer0[ROW*32 + 12] = LETTER12;
MapLayer0[ROW*32 + 13] = LETTER13;
MapLayer0[ROW*32 + 14] = LETTER14;
MapLayer0[ROW*32 + 15] = LETTER15;
MapLayer0[ROW*32 + 16] = LETTER16;
MapLayer0[ROW*32 + 17] = LETTER17;
MapLayer0[ROW*32 + 18] = LETTER18;
MapLayer0[ROW*32 + 19] = LETTER19;
MapLayer0[ROW*32 + 20] = LETTER20;
MapLayer0[ROW*32 + 21] = LETTER21;
MapLayer0[ROW*32 + 22] = LETTER22;
MapLayer0[ROW*32 + 23] = LETTER23;
MapLayer0[ROW*32 + 24] = LETTER24;
MapLayer0[ROW*32 + 25] = LETTER25;
MapLayer0[ROW*32 + 26] = LETTER26;
MapLayer0[ROW*32 + 27] = LETTER27;
MapLayer0[ROW*32 + 28] = LETTER28;
MapLayer0[ROW*32 + 29] = LETTER29;
MapLayer0[ROW*32 + 30] = LETTER30;
MapLayer0[ROW*32 + 31] = LETTER31;

}



It's a bit proprietary in design, but code can always be adjusted. I use 32X32 tile maps so the function rewrites one full horizontal line at a time. Currently I'm using this function mainly in an arcade shooter i am working on.

The way I figure it, My demo-level has 2 bg's used for randomly-generated 'star-field' backgrounds, that scroll at different speeds to simulate depth, one level design bg to scroll at a yet different speed than the star fields, adding to the depth, and one bg for the 'HUD status display'.

For my level bg, i have a "ScreenCNT" variable increasing at the same speed as the level bg itself, 60 units per second [every vsync] and at every 8th ScreenCNT unit, i write the tile line two lines below the bottom of the screen [the screen scrolls top to bottom, so i gave it ample time to write, even though it executes rather quickly and i could perform this write the instant before the line appears onscreen]

The function itself operates simply as such:

Code:
Write(ROW,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#,#);




^^ Where ROW = a number from 0-31 (0 = top bg row, 31 = bottom, respectively) and each # represents the tile's value in memory. it seems a bit redundant to be coding this sort of thing in row by row, but when you put several function 'templates' on top of each other in notepad, its quite easy to build maps

The ScreenCNT variable can be used not only to control map-writes, but to orchestrate level events too :)

#40667 - sgeos - Thu Apr 21, 2005 11:01 am

ImInABand wrote:
Code:

void Write(int ROW, int LETTER0, int LETTER1, int LETTER2, int LETTER3, int LETTER4, int LETTER5, int LETTER6, int LETTER7, int LETTER8, int LETTER9, int LETTER10, int LETTER11, int LETTER12, int LETTER13, int LETTER14, int LETTER15, int LETTER16, int LETTER17, int LETTER18, int LETTER19, int LETTER20, int LETTER21, int LETTER22, int LETTER23, int LETTER24, int LETTER25, int LETTER26, int LETTER27, int LETTER28, int LETTER29, int LETTER30, int LETTER31) {

MapLayer0[ROW*32 + 0] = LETTER0;
MapLayer0[ROW*32 + 1] = LETTER1;
MapLayer0[ROW*32 + 2] = LETTER2;
MapLayer0[ROW*32 + 3] = LETTER3;
MapLayer0[ROW*32 + 4] = LETTER4;
MapLayer0[ROW*32 + 5] = LETTER5;
MapLayer0[ROW*32 + 6] = LETTER6;
MapLayer0[ROW*32 + 7] = LETTER7;
MapLayer0[ROW*32 + 8] = LETTER8;
MapLayer0[ROW*32 + 9] = LETTER9;
MapLayer0[ROW*32 + 10] = LETTER10;
MapLayer0[ROW*32 + 11] = LETTER11;
MapLayer0[ROW*32 + 12] = LETTER12;
MapLayer0[ROW*32 + 13] = LETTER13;
MapLayer0[ROW*32 + 14] = LETTER14;
MapLayer0[ROW*32 + 15] = LETTER15;
MapLayer0[ROW*32 + 16] = LETTER16;
MapLayer0[ROW*32 + 17] = LETTER17;
MapLayer0[ROW*32 + 18] = LETTER18;
MapLayer0[ROW*32 + 19] = LETTER19;
MapLayer0[ROW*32 + 20] = LETTER20;
MapLayer0[ROW*32 + 21] = LETTER21;
MapLayer0[ROW*32 + 22] = LETTER22;
MapLayer0[ROW*32 + 23] = LETTER23;
MapLayer0[ROW*32 + 24] = LETTER24;
MapLayer0[ROW*32 + 25] = LETTER25;
MapLayer0[ROW*32 + 26] = LETTER26;
MapLayer0[ROW*32 + 27] = LETTER27;
MapLayer0[ROW*32 + 28] = LETTER28;
MapLayer0[ROW*32 + 29] = LETTER29;
MapLayer0[ROW*32 + 30] = LETTER30;
MapLayer0[ROW*32 + 31] = LETTER31;

}

Egad! Why doesn't your code look like something like this?
Code:
#define WIDTH   32
#define HEIGHT   32

void write_cell(int p_x, int p_y, int p_letter)
{
   MapLayer0[p_y * WIDTH + p_x] = p_letter;
}

void write_row_raw(int p_x, int p_y, int *p_data, int p_size)
{
   int i;

   for (i = 0; i < p_size; i++)
      write_cell(p_x + i, p_y, p_data[i]);
}

void write_row(int p_row, int *p_data)
{
   write_row_raw(0, p_row, p_data, WIDTH);
}

If you want to pass in a bunch of related values, you probably want to use an array. This is especially so if your variables have names like LETTER4 (ie letter[4]). The safe way to pass arrays around is to pass the size of the array along with it. The write_row() function above assumes that the array you pass it is WIDTH (32) long.

I'm also not entirely sure why you are not using loops. Unrolling loops is something the compiler can do for you, although these functions probably won't be your program's bottleneck.

In response to your original question, think of the display region as cache (ie one screen cell maps to more that one stage cell) and...
If the player moves up, update the offscreen row at the top.
If the player moves down, update the offscreen row at the bottom.
If the player moves left, update the offscreen column to the left.
If the player moves right, update the offscreen column to the right.

Here are some column update functions that work like the row funtions above:
Code:

void write_col_raw(int p_x, int p_y, int *p_data, int p_size)
{
   int i;

   for (i = 0; i < p_size; i++)
      write_cell(p_x, p_y + i, p_data[i]);
}

void write_col(int p_row, int *p_data)
{
   write_col_raw(0, p_row, p_data, HEIGHT);
}


-Brendan

#41188 - LOst? - Tue Apr 26, 2005 4:37 am

ImInABand wrote:

Code:

void Write(int ROW, int LETTER0, int LETTER1, int LETTER2, int LETTER3, int LETTER4, int LETTER5, int LETTER6, int LETTER7, int LETTER8, int LETTER9, int LETTER10, int LETTER11, int LETTER12, int LETTER13, int LETTER14, int LETTER15, int LETTER16, int LETTER17, int LETTER18, int LETTER19, int LETTER20, int LETTER21, int LETTER22, int LETTER23, int LETTER24, int LETTER25, int LETTER26, int LETTER27, int LETTER28, int LETTER29, int LETTER30, int LETTER31) {

MapLayer0[ROW*32 + 0] = LETTER0;
MapLayer0[ROW*32 + 1] = LETTER1;
MapLayer0[ROW*32 + 2] = LETTER2;
MapLayer0[ROW*32 + 3] = LETTER3;
MapLayer0[ROW*32 + 4] = LETTER4;
MapLayer0[ROW*32 + 5] = LETTER5;
MapLayer0[ROW*32 + 6] = LETTER6;
MapLayer0[ROW*32 + 7] = LETTER7;
MapLayer0[ROW*32 + 8] = LETTER8;
MapLayer0[ROW*32 + 9] = LETTER9;
MapLayer0[ROW*32 + 10] = LETTER10;
MapLayer0[ROW*32 + 11] = LETTER11;
MapLayer0[ROW*32 + 12] = LETTER12;
MapLayer0[ROW*32 + 13] = LETTER13;
MapLayer0[ROW*32 + 14] = LETTER14;
MapLayer0[ROW*32 + 15] = LETTER15;
MapLayer0[ROW*32 + 16] = LETTER16;
MapLayer0[ROW*32 + 17] = LETTER17;
MapLayer0[ROW*32 + 18] = LETTER18;
MapLayer0[ROW*32 + 19] = LETTER19;
MapLayer0[ROW*32 + 20] = LETTER20;
MapLayer0[ROW*32 + 21] = LETTER21;
MapLayer0[ROW*32 + 22] = LETTER22;
MapLayer0[ROW*32 + 23] = LETTER23;
MapLayer0[ROW*32 + 24] = LETTER24;
MapLayer0[ROW*32 + 25] = LETTER25;
MapLayer0[ROW*32 + 26] = LETTER26;
MapLayer0[ROW*32 + 27] = LETTER27;
MapLayer0[ROW*32 + 28] = LETTER28;
MapLayer0[ROW*32 + 29] = LETTER29;
MapLayer0[ROW*32 + 30] = LETTER30;
MapLayer0[ROW*32 + 31] = LETTER31;

}




XD

Really, that function call takes 128 bytes on the stack. I've never seen anything like it before hehe.

I wonder how many times you can call this function from within this function before you will get stack overflow?

#41191 - Dwedit - Tue Apr 26, 2005 5:59 am

I'd guess about 248 if IWRAM is free, or if the stack sits in EWRAM, 1985.
_________________
"We are merely sprites that dance at the beck and call of our button pressing overlord."

#41196 - ImInABand - Tue Apr 26, 2005 8:12 am

ive devised several other methods of doing this outside of that horrendous abomination, that thing was the braindchild of insomnia and a quick ay through the problem. using an array method i came up with a much better idea, at first it hadnt occurred to me about teh stack overflow thing [hee hee]

i originally used that function to write rows and display text in a low-memory intensive text editor that im working on, so the stack overflow wouldnt be a problem, since the function would only have been called a max of 30 times, if that.

reminder: I've pretty much taught myself everything i know, so for "feeling through the dark" i think it wasnt too harsh of an idea for what it was being used for.