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.

Beginners > Questions to Test the Knowledge of Mortal Men/Women! - Vol 1

#13976 - Xax - Fri Dec 26, 2003 9:40 pm

These are some questions I had before I set to design my first engine. I could no doubt eventually figure them out on my own, but rather than waste hundreds of hours experimenting, I thought I'd see if anyone else already knows the answers. If you have anything to share at all, please do! =)

1 -- Controlling Animated Sprites
I'm wondering if I can use just one sprite to represent a door, in both opened and closed states. Can I use an animated sprite, and just freeze-and-unfreeze animations at my convenience? Or do all sprites have to animate at the same interval? It seems like a good way to use just 1 sprite for 8 frames, instead of using 8 seperate sprites. This is what I mean:
    A) The first frame of the animation shows the door closed, and the animation freezes.
    B) When the player opens the door, the next 3 frames animate to show the door opening, then the next frame, which shows the door fully opened, freezes.
    C) When the player closes the door, the remaining 3 frames animate to show it closing, then loop back to the first frame.
2 -- Using a Master Tileset
Different maps will re-use various tiles. Rather than having certain tiles taking up space in various tilesets, is it possible to have a master tileset in ROM with all the tiles in the game, and have the engine copy whatever tiles it needs to create a tileset for a particular map on-the-fly? Well, I figure it's possible, but how much time would it take to compose a full 1024-tile tileset on-the-fly? Would it be faster (let alone possible) to be regularly reading from the master tileset and allocating and deallocating tiles on the current map's tileset as needed?

3 -- Using a Master Map and Transitions
I've seen someone mention the possibility of "gluing" maps together, so that I can use multiple maps to make one gigantic map. Assuming that each individual map is using up most of its available resources (tiles, sprites, etc.), roughly how long would a transition to a similar map take? 1 second? 5? If I were to go with the master tileset idea in question #2 (using either method mentioned), and I carried over all of the tiles and sprites on the screen at the time that I transition to another map, roughly how long would that take? Note that I'm not making a fast-twitch action game; the screen will scroll relatively slowly, and the combat is turn-based. So even if transitions took a good 5 seconds or thereabouts, I would consider that acceptable.

4 -- Display Scrolling Based on Player Direction
And to finish, a relatively simple question. I'm considering having the display reposition itself depending on which way the player is facing. For example, if the player faces Left, the display quickly scrolls so that the player is near the Right edge of the screen, allowing the player to see further Left. If he suddenly turns Northward, the display quickly scrolls so that the player is centered near the Lower edge of the screen, allowing him to see further North. Has anyone seen or tried something like this? Is it at all disorienting?

Whew! Lots of questions. Hopefully someone out there will actually read through the whole thing (or at least one of the questions) and can grace me with their vast knowledge! Thanks in advance to those brave men and women!

Matt


Last edited by Xax on Fri Dec 26, 2003 10:59 pm; edited 1 time in total

#13983 - DekuTree64 - Fri Dec 26, 2003 10:21 pm

1: A good way to do this is to make an animation scripting type system where you tell it which frame to display and for how long. That way you can just have it show the closed door forever (just declare one interval to mean forever, like 255 if you don't need long intervals, so you could just using single bytes, or -1 if you don't mind losing half your range to negative numbers, because you most likely won't need the full range of a 16 or 32-bit number), but if you run into the door, set it to play a different animation where it shows the frames of the door opening with short intervals between them, and then set the interval to your forever value on the last frame so it stays there.

2: My RPG currently uses a system where you can just load 2 tilesets into char blocks 1 and 2, but then I realized I might as well allow for as many as I want, since it really doesn't matter at all wether they're char-block-aligned or not. Just load tiles in sequentially and adjust the map tile-to-tileset tile table by the number of tiles loaded in prior to the current set.
Of course, it's easier to use one large tileset, because you can just DMA it in and be done with it. Plus you don't have to make your own map editor. Mine works with tile numbers that are unrelated to the tilesets themselves, so like if you have tileset 1 with 50 tiles, and tileset 2 with 100, then tile number 55 will refer to the 5th tile of set2.
I have heard of using a single 'pool' of every tile in the game and dynamically loading in whichever ones you need. It was in an article by Rafael Baptista. I don't remember where I dled it though, so PM me your email or something if you can't find it anywhere.

3: Mine splits the map up into 16x16 tile blocks and LZ77 compresses them. Then it decompresses the 4 closest map blocks to EWRAM and copies strips of tiles in just offscreen as the map scrolls, loading new map blocks as needed too. That way there's no limit to the size of maps. Also, you can just set up OAM entries for whichever sprites are onscreen each frame so there are basically no sprite limits either.
Oh, and I just have a table that says wether a type of tile is walkable or not for collisions, so no need for a collision map. Plus you can make copies of the same tile for walkable and non-walkable and just point the entries in the map tile-to-tileset tile table to the same tileset tiles, so it doesn't use any extra VRAM. The only problem is you have to stop all randomly wandering offscren sprites because you only have the 4 closest parts of the map loaded, and therefore only have that much of the collision map to check things with.

4: I prefer to have the player right in the middle of the screen all the time. A fairly slow scroll over to where you're more like 1/3 or 1/4 from the edge of the screen would be ok though, so you can see farther ahead without getting disoriented. Just mess with it until it's right once you have some game done to see what it's like.

All that said, keep it simple if it's your first engine. Actually don't even make an engine, just think of how to make a game, and then engine is just part of that. Trying to make it all reusable and non-game-specific will only get you into trouble when you actually try to do something with it. You need the experience of actually putting it to use to be able to design a generic engine, so just make it work however you have to. And the less high-tech stuff you try to do from the start, the better you can keep your focus on the game as a whole.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#13987 - Xax - Fri Dec 26, 2003 11:24 pm

Wow! Thank you very much for your response, Deku. I understood most of it, and gleaned much useful information. I have 2 short questions.

Quote:
I have heard of using a single 'pool' of every tile in the game and dynamically loading in whichever ones you need. It was in an article by Rafael Baptista. I don't remember where I dled it though, so PM me your email or something if you can't find it anywhere.

I've read Baptista's article on memory management, but don't recall reading about tile pools. That's the only article I could find, aside from some unrelated articles on GameDev. Is that the article you were referring to?

Quote:
Mine splits the map up into 16x16 tile blocks and LZ77 compresses them. Then it decompresses the 4 closest map blocks to EWRAM and copies strips of tiles in just offscreen as the map scrolls, loading new map blocks as needed too. That way there's no limit to the size of maps. Also, you can just set up OAM entries for whichever sprites are onscreen each frame so there are basically no sprite limits either.

Very cool. Is there any noticible processing or slow down when your game loads the map blocks? Also, I would think that you would need to load the 8 closest map blocks (for the maps N, NE, S, SE, S, SW, W, NW of the current map). But you say you just need to load 4?

Thanks again!

Matt

#13988 - poslundc - Sat Dec 27, 2003 12:21 am

Xax wrote:
1 -- Controlling Animated Sprites
I'm wondering if I can use just one sprite to represent a door, in both opened and closed states. Can I use an animated sprite, and just freeze-and-unfreeze animations at my convenience? Or do all sprites have to animate at the same interval?


This is entirely up to how you choose to implement both your sprites and your animations. Using the "same sprite" can mean several different things: are you using the same OAM entry? The same location in VRAM? etc. There is also the possibility of integrating doors into your background map(s).

You are probably aware that sprites do not "animate" themselves, so there may technically be no difference between setting up an "animation" and advancing to the the next frame, and just altering the door's state variable and swapping in a different sprite.

Quote:
2 -- Using a Master Tileset
Different maps will re-use various tiles. Rather than having certain tiles taking up space in various tilesets, is it possible to have a master tileset in ROM with all the tiles in the game, and have the engine copy whatever tiles it needs to create a tileset for a particular map on-the-fly? Well, I figure it's possible, but how much time would it take to compose a full 1024-tile tileset on-the-fly? Would it be faster (let alone possible) to be regularly reading from the master tileset and allocating and deallocating tiles on the current map's tileset as needed?


Short answer (to the first question) is yes, and while I don't know how many games would do that type of implementation, plenty of games dynamically shuffle the tile VRAM as necessary.

Time shouldn't be an issue so long as you manage the transitions from one screen to the next. The most straightforward way of doing this is to fade the screen to black when entering a new area or loading a new map. Make whatever changes you need to make to VRAM, then fade the screen back up. Whether it takes one frame or ten frames of darkness to load everything in won't make a difference when you consider a single frame is 1/60 of a second.

My game distinguishes between maps and tilesets in such a way that I can have a tileset for field elements, a tileset for water elements, a tileset for cottage elements, etc. Then in my map editor I pick and choose however many tilesets I want to use in the map, and just make sure I don't run out of tiles or palette entries. Then on the GBA side, when I load up a map I have a tileset loader that loads all of the necessary tilesets into VRAM, and combines the colour palettes of each one to optimize palette use.

This way I can reuse and combine my various tilesets from one map to the next, and always be frugal with my colour use. For example, one outdoor map may have a waterfall, so it will require both the field tileset and the water tileset. Another outdoor map may have roads but no waterfall, so I can use the field tileset and the road tileset and the colours that would have gone to the waterfall can instead go to the roads.

Quote:
3 -- Using a Master Map and Transitions
I've seen someone mention the possibility of "gluing" maps together, so that I can use multiple maps to make one gigantic map. Assuming that each individual map is using up most of its available resources (tiles, sprites, etc.), roughly how long would a transition to a similar map take? 1 second? 5? If I were to go with the master tileset idea in question #2 (using either method mentioned), and I carried over all of the tiles and sprites on the screen at the time that I transition to another map, roughly how long would that take? Note that I'm not making a fast-twitch action game; the screen will scroll relatively slowly, and the combat is turn-based. So even if transitions took a good 5 seconds or thereabouts, I would consider that acceptable.


Again, fade to black (or something) and use a loader when switching from one map to the next.

For scrolling around, your map data itself (ie. which tile goes where) should use as little VRAM as possible (ie. for text backgrounds, use 256x256) and you can dynamically load tile entries in as you scroll.

Quote:
4 -- Display Scrolling Based on Player Direction
And to finish, a relatively simple question. I'm considering having the display reposition itself depending on which way the player is facing. For example, if the player faces Left, the display quickly scrolls so that the player is near the Right edge of the screen, allowing the player to see further Left. If he suddenly turns Northward, the display quickly scrolls so that the player is centered near the Lower edge of the screen, allowing him to see further North. Has anyone seen or tried something like this? Is it at all disorienting?


It depends on how often and quickly the player turns.

What might be more effective is an adaptive camera system of sorts (similar to what my game is using). In such a system, you establish buffer zones and don't start scrolling until the player reaches a certain distance from where the camera already is. You basically let the player be in charge, and it's the camera's duty to follow the player as if on a tether, but only moving when it absolutely has to.

You could expand on this principle for your game, so that if the camera starts moving to catch up to the player, it doesn't stop moving until it is also illuminating the way ahead, whichever direction that may be.

(Yoshi's Island does something similar to this, where the camera's first duty is to follow Yoshi but when it finally comes to a resting state it is always with Yoshi on the left or right side of the screen, depending on which way he is facing. It doesn't truly act as a tether, though, because if you make him switch between facing left and right the screen starts shifting immediately, instead of waiting for Yoshi to move outside of the camera's current locus of interest. This makes it very noticeable/disorienting if you are standing on the same spot but flipping left and right a lot.)

Dan.

#13992 - tepples - Sat Dec 27, 2003 1:13 am

poslundc wrote:
Whether it takes one frame or ten frames of darkness to load everything in won't make a difference when you consider a single frame is 1/60 of a second.

Unless your loader is as bad as that of The Lord of the Rings for Super NES, which takes three to five seconds to switch maps.

Quote:
(Yoshi's Island does something similar to this, where the camera's first duty is to follow Yoshi but when it finally comes to a resting state it is always with Yoshi on the left or right side of the screen, depending on which way he is facing. It doesn't truly act as a tether, though, because if you make him switch between facing left and right the screen starts shifting immediately, instead of waiting for Yoshi to move outside of the camera's current locus of interest. This makes it very noticeable/disorienting if you are standing on the same spot but flipping left and right a lot.)

I used something like that in one of my old PC demos. Warning: this is untested code, designed to illustrate a point.
Code:

enum {
  FACING_E = 0,
  FACING_NE, FACING_N, FACING_NW,
  FACING_W, FACING_SW, FACING_S, FACING_SE
};

/* a low-res trig table, used for motion and camera control */
const signed char facing_cos[8] =
{ 32, 23, 0, -23, -32, -23, 0, 23 };
const signed char facing_sin[8] =
{ 0, 23, 32, 23, 0, -23, -32, -23 };

void move_camera(void)
{
  int c = facing_cos[player.facing];
  int s = facing_sin[player.facing];

  /* position camera a bit ahead of player */
  camtarget.x = player.x - 120 + c;
  camtarget.y = player.y - 80 - s;

  /* first order low pass filter on camera coordinates for pedagogic
     simplicity; production code would use a 3rd order linear-preserving filter */
  camera.x = (camtarget.x + camera.x * 3 + 2) >> 2;
  camera.y = (camtarget.y + camera.y * 3 + 2) >> 2;
}

_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#13995 - Xax - Sat Dec 27, 2003 2:51 am

poslundc wrote:
There is also the possibility of integrating doors into your background map(s).

Yeah, I guess there would be no real difference between using sprites and using tiles for scenary animation, since in either case you're just feeding new images into the same slot in VRAM in order to animate, right?

Quote:
Time shouldn't be an issue so long as you manage the transitions from one screen to the next. [...] Whether it takes one frame or ten frames of darkness to load everything in won't make a difference when you consider a single frame is 1/60 of a second.

I was hoping to not have to resort to fading the screen to black, at least for as long as the tiles on the screens before and after the transition are the same. If we're talking about the transition taking less than 1 second, then fading shouldn't be necessary, correct? If it were to take longer than a 10-20 frames, I may just display a small clock/hourglass-type animation in for those instances.

Quote:
Then on the GBA side, when I load up a map I have a tileset loader that loads all of the necessary tilesets into VRAM, and combines the colour palettes of each one to optimize palette use.

I like this idea a lot. Maybe this is a dumb question, but does your engine examine the individual tiles to come up with the palette, or do you just "plug-in" a tileset's palette?

Quote:
What might be more effective is an adaptive camera system of sorts (similar to what my game is using). In such a system, you establish buffer zones and don't start scrolling until the player reaches a certain distance from where the camera already is. You basically let the player be in charge, and it's the camera's duty to follow the player as if on a tether, but only moving when it absolutely has to.

The game that I have in mind has larger sprites than I'm used to seeing on GBA games, so my concern is that the camera shows the player what's up ahead to compensate for lack of a larger view. I'll experiment with it and see if I can come up with something that is smooth and isn't jarring. Buffer zones would work great if objects in my game were smaller than they are, but I think if the adaptive view doesn't work out, I'll keep the player in the center of the screen.

Thanks very much for your input, Dan!

Matt

#13996 - DekuTree64 - Sat Dec 27, 2003 2:52 am

Xax wrote:
I've read Baptista's article on memory management, but don't recall reading about tile pools. That's the only article I could find, aside from some unrelated articles on GameDev. Is that the article you were referring to?

http://www.gamasutra.com/features/20010509/baptista_02.htm
The second half of that page explains how that system works. The version I have includes the code to it though, so you can see how it's actually implemented.

Quote:

Very cool. Is there any noticible processing or slow down when your game loads the map blocks? Also, I would think that you would need to load the 8 closest map blocks (for the maps N, NE, S, SE, S, SW, W, NW of the current map). But you say you just need to load 4?

Nope, just 4. Since one block is enough to fill the screen, you only need 2 wide and 2 tall. You can't have like one on the left, another in the middle, and one on the right, so there's no point in doing it that way (though I had planned for 9 blocks originally). I'd have to go back and look at my code to fully explain it (long time since I wrote it) but it involves a lot of shifting and ANDing to figure out which tile strip from which block to load into which strip in the VRAM.
There aren't any slowdowns for loading new blocks. It's only 1K to decompress each time to get too close to the edge of the loaded 'area' of the map (tile indices are 16-bit, so 16x16 blocks are 512 bytes, and you have to load 2 since you need to change the top and bottom blocks if you're moving left or right, or left and right if you're moving up and down), and then 11 or 16 16x16 tiles (screen is 10 tall and 15 wide, so you need to copy one more than that), which are 4 8x8 GBA tiles each, so 40 or 60 lookups in the map-to-tileset table (it's actually just the 'map' output by the gfx converter program that has the GBA tile entries (index/flip flags/palette), so if your tileset bmp is 16 pixels wide, just multiply the map tile index by 4, and the next 4 u16's are the screen entries for this 16x16 tile), so the time isn't too much of a concern. I used to just copy in the entire screen block's worth every frame from a full size decompressed map made of 8x8 tiles using the actual GBA screen entries in ROM, but needless to say that soaked a ton of space, so I made the new system with 16x16 tiles, which quartered it, plus the compression helps too, and that has worked great for me ever since.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#13998 - poslundc - Sat Dec 27, 2003 4:20 am

Xax wrote:
Quote:
Time shouldn't be an issue so long as you manage the transitions from one screen to the next. [...] Whether it takes one frame or ten frames of darkness to load everything in won't make a difference when you consider a single frame is 1/60 of a second.

I was hoping to not have to resort to fading the screen to black, at least for as long as the tiles on the screens before and after the transition are the same. If we're talking about the transition taking less than 1 second, then fading shouldn't be necessary, correct? If it were to take longer than a 10-20 frames, I may just display a small clock/hourglass-type animation in for those instances.


Ten frames worth of garbage (ie. incorrect) data in VRAM will definitely be visible to the user. Heck, a single frame of anomalous graphics is often noticeable, because even though it happens for just 1/60 of a second it stands out because it looks different, and doesn't fit in with the correct, smooth motion of your gameplay.

If you need to change the stuff in VRAM, you definitely should definitely not be displaying it while you do so, unless you really don't mind the jolt it creates for the player. Either turn off the changing display elements, obscure them with something to distract the user, or fade down and up.

Note than when I suggest to fade I don't mean you have to take one second to fade down and another to fade up. Fade in 10 or 15 frames if you want. Heck, you've only got 32 discrete levels you can fade anyway.

Quote:
Quote:
Then on the GBA side, when I load up a map I have a tileset loader that loads all of the necessary tilesets into VRAM, and combines the colour palettes of each one to optimize palette use.

I like this idea a lot. Maybe this is a dumb question, but does your engine examine the individual tiles to come up with the palette, or do you just "plug-in" a tileset's palette?


As I recall, the way I did it each tileset has a palette specific to that tileset, and all of the actual tile bitmap data stored in the ROM is in reference to that palette. When a tileset is loaded, each colour of the palette is either located in the current palette (if it's already being used) or appended to the end of it, and a hash table is generated for all of the colours in that palette. Then when the tile bitmap data is being loaded in, the values are hashed from the tileset's original colour indices to the indices of the global palette. (This is for 256-colour mode; 16-colour mode is slightly different since you need to treat the whole palette as a set of 16 different 16-colour palettes, so you can't merge quite as optimally.)

Dan.

#13999 - Xax - Sat Dec 27, 2003 4:51 am

poslundc wrote:
Xax wrote:
I was hoping to not have to resort to fading the screen to black, at least for as long as the tiles on the screens before and after the transition are the same. If we're talking about the transition taking less than 1 second, then fading shouldn't be necessary, correct?

Ten frames worth of garbage (ie. incorrect) data in VRAM will definitely be visible to the user. Heck, a single frame of anomalous graphics is often noticeable, because even though it happens for just 1/60 of a second it stands out because it looks different, and doesn't fit in with the correct, smooth motion of your gameplay.

But what I have in mind shouldn't result in garbage being visible. For example, let's say the character is walking in the middle of a forest, and I'm on the edge of the map, so I load the next one. The tiles and sprites that are on the screen before and after the transition are the same (and remain in the same locations in VRAM), so the area of the tileset containing those tiles on the screen would remain. I can still see the same trees, animals, etc. that I saw before the transition. I would only shuffle the tiles that aren't currently being displayed. So that means no garbage on the screen, and no need to fade, unless there's something I'm missing or not understanding. If there is, please show me! =)

Matt

#14019 - poslundc - Sat Dec 27, 2003 3:18 pm

That's fine; the only problem you might run into is that if you are trying to load in a tileset for a new area while still displaying the tileset for the old area then you obviously can't change EVERYTHING, only the elements that aren't in use for the current "section" of map you are in.

If that's compatible with your game design, then go for it. My only other caution is that if you ARE going to be loading in more than a frame's worth of data it'd be nice to find a way to split it across several frames so that the controls don't get sluggish.

Dan.

#14021 - crossraleigh - Sat Dec 27, 2003 5:18 pm

At the start of the topic you said you would like to glue hardware maps together; DekuTree64 then suggested gluing minimap blocks; poslundc then suggested fading when changing (software) maps and dynamically loading new tiles; you are now suggesting dynamically loading tilesets.

The problem is ambiguous naming: 'tiles', 'tilesets', 'maps', 'map blocks' are all used almost interchangeably.

There are two options for map scrolling: loading a new minimap periodically (like Zelda: A Link to the Past), or smoothly scrolling by dynamically loading new data. For the first option, the full 'software' map is divided into small 32x32 hardware maps. For the second option, there are a few different ways to do it. One, as DekuTree has said, is creating tiny 16x16 maps that can be swapped in and out during V-blank. Another is loading a new line of tile indices from the software map to the offscreen part of the hardware map. I suppose another is swapping out the tiles in the tileset, but it's not very practicable because those tile can't be used on the visible map; I think the only reason it has been brought up is because of the said ambiguity.

For an example of the Zelda style loading, darkcloud has some source on his website. Deku's method is ideal if (as in his case) you need to load compressed data; I don't know that he has any public source though. The SGADE has some very nice smooth scrolling code that loads new lines of map data offscreen.

As far as speed, (if my calculations are right) to load a 256x256 map takes about 3.7% of a V-blank:
Code:
32 32 *   # Tiles
2 *       # Bytes per index
4 /       # Bytes per word
6 *       # Cycles to DMA a word
83776 /   # Cycles per V-blank


Edit: Fixed (hopefully) my completely screwed up calculations.

#14036 - poslundc - Sun Dec 28, 2003 12:16 am

crossraleigh wrote:
As far as speed, (if my calculations are right) to load a 256x256 map takes about 3.7% of a V-blank:


Keep in mind that for many systems you can't just DMA the map from source to destination, especially if you are only copying a small chunk out of a larger map. Usually there is some processing involved.

And I agree that it should not be speed-prohibitive, but it can be. I was originally using 1024x1024 rot-maps and intermittently I would try to update the entire thing in one go. There just wasn't enough VBlank to make it practical, though, and I ended up switching down to 512x512.

Dan.