Smooth scrolling and map storage

Started by
5 comments, last by feti 16 years, 5 months ago
When writing a 2d tile-based game, if the walkable area is too vast, it's impossible to load everything at once. So, I am taking some tips from this fellas tutorial: http://www.gamedev.net/reference/articles/article727.asp What he does is divides his land up into segments and draws the segment he's in and 8 surrounding segments. My questions are: 1. How do I determine which segments I need to load when the player takes a step. If they head north west, the possibilities are that I have to load new data for the left and the top of the player at the same time. 2. Do I store map related data in such a way that I load bits of information every few steps, or do I store the data in such a way that it's easier to manipulate for me, but requires me to read more of the data and less frequently?
Advertisement
Firstly, I would say that its unlikely that your maps are indeed so large that they cause any signifigant dent in your computer's memory. Take a 4-layer, 16-bit indexed map at 1024x1024, and you're still looking at only 8 megabytes. Unless you're on a platform where memory is really tight (IE consoles/portables/PocketPC/Phones, then its not really an issue.

Not that there's anything wrong with trying to keep memory use down though, its a noble pursuit. Furthermore, the map segments you describe fit nicely into other spacial optimizations beside simply culling out extraneous map data, such as collision culling, entity processing, and other things you can skip over because they're not inside the "active" portion of the world.

Ideally, you want to organize your map files into segments as well in order to speed up loading by keeping relevant data close together, since disk IO will almost certainly be your bottleneck when streaming data.

I would (and have) organize the mape file like so:

For each map, break it into MxN segments of a size equal to the onscreen viewport.
For each segment, include layer data from the bottom up, as well as entity/object data, and any collision data that is not computed at run-time.
Store tile data in top-bottom, left-right order. Segments should be stored the same way.
Build offsets into the file to make seeking specific segments quick and easy(This could be a table in the header, or neighbor data in each segment, among other approaches)


As for calculating what sections are active, simply keep the player coordinates in world-space (as they should be anyhow) and divide their current position by the size of each segment in X and Y respectively. This gives you the segment that the player is in, and from there, you simply select the 8 segments surrounding the player.

Its just a little simple math, no magic required.

throw table_exception("(? ???)? ? ???");

Since this is an RPG and there are objects (monsters and such) running around, I should load those all up at the start, regardless of location correct? Then just keep spawning them if I choose to do so.

I'd like to keep the environment interactive so to speak. I don't think it would be smart from my perspective to only load monsters and such when I am about to view them on screen. For example, if I load all monsters up and then I get to one on screen and battle it, then I run off and come back in 30 minutes, I want the monsters hit points to be as they were when I left. And if the monster has AI, maybe it'll track me down if I'm hiding behind a rock 100 tiles over where I'm completely out of sight.

Is it practical to keep in memory all possible objects for a given map? It's an RPG, and I'm thinking zelda size worlds as an entire region. So I mah have 100 screens up, 100 screens to the right worth of monsters in memory, all interacting. I hope this is practical, or if you could possibl offer another solution for objects to keep state, but load and unload them at will.
I would probably have two lists of monsters, one containing active monsters/objects and another containing the inactive ones.

Don't load them all at once, load them as encountered into the active list. Then, when you get too far away and/or their AI puts them in a non-attacking state, place them on the inactive list. Next time you come around, check the inactive list and move them into the active list if you find them. This will preserve their status between encounters while also keeping a "short list" of those enemies/objects that need to be processed.

It makes little sense to actually process entities that are not close by because you'll never see it, and by processing far away entities it also implies that you have all the relevant data loaded -- For example, to move an enemy 100 units away, it implies that you have collision data for the area surrounding those 100 units.

A 3x3 screen area is probably effective enough for what defines an "active" entity, but there's no reason you couldn't do 5x5 or even 7x7 if you wanted to push the boundary for active entities further out (aside from memory/streaming).

When an enemy is killed, remove it from both lists and it will re-spawn anew the next time you pass by its spawn trigger. Alternatively, keep it on the inactive list in a "dead" state and periodically run through the inactive list restoring these dead entities to their default state. This can be used to implement timed re-spawns, random re-spawns, and other types.

throw table_exception("(? ???)? ? ???");

I've come up with a basic map data file format. Basically the first few lines are header information about the map and whatnot, including the amount of tiles for the map.

The tile data is fixed-length. One tile per line. I think it's about 166characters. Because it's fixed length, I can seek to any tile in the file and grab a chunk of them at once with ease. So far this seems like the best idea.

Now, for the map file, I also want to provide object information like monsters, manipulatable stuff on the screen like a rock, which wouldn't be a standard tile, since it's got interactive controls such as being pulled or whatnot. Now, in my map file I'm wondering if I should also list all the objects as well, and in the headers of the map file provide the # of objects that are specified in the file so I can once again easily skip through.

I'm thinking each individual object, since it'll have related scripting code to control it will be its own data file, but the map file may specify which one to load and map specific infromation such as whether or not it should respawn if destroyed, and if so, how long of a respawn time, etc.

Does this sound kosher, or am I missing a minute detail? I haven't begun parsing any of this yet because I want to make sure it's just right before I go and hash out any code.
Quote:Original post by feti
I've come up with a basic map data file format. Basically the first few lines are header information about the map and whatnot, including the amount of tiles for the map.

The tile data is fixed-length. One tile per line. I think it's about 166characters. Because it's fixed length, I can seek to any tile in the file and grab a chunk of them at once with ease. So far this seems like the best idea.


So, my understanding is that the 'tile data' is information about, say, a grass tile, or a dirt tile -- filename, collision and other properties, correct?

If that understanding is correct, then it sounds reasonable.

Quote:Now, for the map file, I also want to provide object information like monsters, manipulatable stuff on the screen like a rock, which wouldn't be a standard tile, since it's got interactive controls such as being pulled or whatnot. Now, in my map file I'm wondering if I should also list all the objects as well, and in the headers of the map file provide the # of objects that are specified in the file so I can once again easily skip through.


That's an acceptable way to do it, yes. Personally my basic map files look something like this:
HEADER: MAP_HEIGHT MAP_WIDTH SECTOR_HEIGHT SECTOR_WIDTHTILEINFO: NUM_TILES TILE_DATA[NUM_TILES]SECTORS[MAP_HEIGHT][MAP_WIDTH]: TILE_INDICES[LAYERS][SECTOR_HEIGHT][SECTOR_WIDTH] OBJECTS:   NUM_OBJECTS  OBJECT_DATA[NUM_OBJECTS] ENTITIES:  NUM_ENTITIES  ENTITY_DATA [NUM_ENTITIES]

I've included TILEINFO above, based on what you've described, but personally I like to break tile info out into a 'tileset' as they are commonly used across map files, and I prefer to work with them that way.

LAYERS can be assumed constant (eg: all maps have 3 layers), defined per-map (eg: this map has 2 layers, another has 3 layers) or per sector (eg: This sector has 2 layers, another has 4 layers).

I also make a distinction between objects (non-tile scenery that supports scripting, such as your rock) and entities (these support AI behaviors as well as optional scripting, such as NPCs or wandering baddies.)

Quote:I'm thinking each individual object, since it'll have related scripting code to control it will be its own data file, but the map file may specify which one to load and map specific information such as whether or not it should respawn if destroyed, and if so, how long of a respawn time, etc.


If that's how you're comfortable doing it, then thats fine. I would point out that, in addition to one-off custom objects, there are generally two broad categories of objects: Those that have many instances which behave similarly (eg - a type of monster or class of NPCs) and those that have many instances which always behave differently (eg - a treasure chest). Both of which can be handled nicely via object templates (which can work much like C++ inheritance and/or templates).

Be wary that having each object on each map defined in a unique file will create a ton of potentially small files which not only looks cluttered, but which will actually waste the user's disk space -- 4kb is a typical sector size on many hard disks, and most file systems only support 1 file per sector. Even though a file may contain only a few bytes, it occupies 4KB of disk space. New standards on large modern drives allow for up to 4MB sectors (thought that's not yet common) -- just be aware of how much disk is being wasted per file.

Quote:Does this sound kosher, or am I missing a minute detail? I haven't begun parsing any of this yet because I want to make sure it's just right before I go and hash out any code.


Nothing really jumps out at me as bad, assuming my assumption at top is correct. The main thing I'd like to stress is that data should be organized *per sector* -- meaning that all data relevant to a sector, whether it be tiles, objects, entities, collision data, or what-have-you, should be grouped together in the sector data. This is simply because the sector is the top-level structure that you will be frequently loading from file.

The format I've outlined above is fairly basic. The format I use now support arbitrarily stacked tiles, collision geometry and specifies layers per-cell (I use cell to refer to a vertical stack of tiles).

throw table_exception("(? ???)? ? ???");

Good point on the objects. Here's what how I vision my map/object editor.

Basically you'll get a blank opengl viewport. On the side you can load a tileset, which is just the texture of all the sprites. You can choose to use them as individual tiles and stick them on the map (which would make sense if you're using map tile sprites). But if you want to make objects, you could also do that.

To make a basic object data file, here's what I figure a user might do. They'd open the tileset that contains the sprites for the object. Then'd they would select the sprite image as a whole rectangle, drag it onto the opengl viewport and it would create an opengl rectangle with the texture mapped to it. That that point it is an object soley because it's bound to a rectangle that gets billboarded when the screen loads, it's not drawn as a tile in standard mode before switching to ortho mode.

That's all I have in my head for now. I'm a bit fuzzy on how the designer would specify a hitbox for the object. Let's say it's a tree. The trunk we would want to be collidable, but the higher portion of the trunk and leaves would be too high in 3d space, so I'd want to be able to walk behind it. So I'd have to give coordinates which portion can be walked behind, so when rendering the frame we can determine when to set the depth property of the tree so it's drawn over, or whether or not we're doing collision.

I still have absolutely no clue how I'm going to handle the depth property and the collision box for objects. I just have no clue what's involved. It all changes based on if a player moves in tile units, or moves freely in 4 or 8 directions by pixel. I'm guessing that changes the entire way the collision system will work, because it's no longer a simple tile check if they're not moving in tile units.

This topic is closed to new replies.

Advertisement