In a 2D RPG, how do you manage the overworld?

Started by
6 comments, last by tom_mai78101 10 years ago

You know that in RPG worlds, the outside world is the overworld. There would be areas connected together like routes/paths, or roads that players can traverse back and forth. If all the areas are connected together, the overworld can get quite big. Big enough that the hardware the game is running on won't be able to load all the areas entirely at once. The most obvious way would be to split the overworld up into chunks and let the game load the chunk the player is currently in.

While I was thinking about the chunks, I noticed that when games are loading the overworld, each chunk it loaded was connected seamlessly together. It's as if the overworld is just too large to fit on the display screen. I wanted to know how to achieve the effect of connecting chunks of areas together seamlessly.

I'm just asking if I'm correct:

  1. Possibly load all chunks nearby and move the overworld by the player's position as offsets. Once the player enters a new chunk, load the unloaded chunks that weren't there previously, keep the old chunk the player had just left from, and unload the chunks that are too far away from the player.
  2. To let players transition from one chunk to the other, the game needs to check to see if the player is walking out of the boundaries, or the player is walking inside the traversable areas that connects the two chunks together.
  3. Anything outside of any chunk must render something that tells the player it's truly not walkable.

Each chunk is to be loaded for each area the player is in. One chunk per area. Does this mean that chunks have a constant size? What should happen if there's an area split into 2 chunks or more? What should I do about that?

When doing the loading/unloading chunks, what sort of system should I use to manage them? I don't think managing an array of chunks is easy, but it's the only thing I can think up that is closest to what I can use. Do you guys use a queue, or a linked list to help the management?

What about chunks with areas like tunnels, caves, or rooms?

I think that's all of the questions I can ask. I just don't know if that's enough for me to solve this.

Advertisement

Presumably all variants are already used in the past. The suitability depends on the game world.

A world with large areas walkable in many / all directions will use some kind of regional proximity to detect whether another couple of tiles need to be loaded. The system I've worked on some years ago was split into square tiles. At least the tile where the player was onto as well as the surrounding 8 tiles were loaded. Okay, this is somewhat coarse, because there was a hysteresis: Crossing a border has triggered loading of tiles but unloading of tiles was deferred. Tiles had a (x,y) index and were placed on a grid. Because the terrain was not walkable everywhere, a map was used to translate from (x,y) to the actual tile address in file, and another map for tiles in RAM.

On the other hand, if the world is given so that tiles are to be entered by portals (used as a generalization for doors, tunnels, passages, canyons, …) so that the walkable directions are limited, then the tiles can be less regular in both form and placement, and the portals can be used directly to address the subsequent tiles. Notice that portals allow a geometric transform, so that the geometry of a subsequent tile need not really be placed adjacently.

Both ways above can be used together if one wants an open world where indoor scenes are placed.

With that in mind, the only "overworld" ever loaded was the map with the file pointers.

When I played U4/U5 on my Apple II it had such a system which loaded in the surrounding chunks as you moved around the map. The world map was like 256x256 tiles, and the game only loaded in the surrounding chunks of 32x32 (U4) or 16x16 (U5) tiles at a time. Even on my old Apple II the disk access was sufficiently fast to only present like a quarter second delay every time you moved enough tiles over for it to load new chunks.

With today's hardware I could easily envision loading a chunk of even 256x256 bytes much faster. Assuming a 64K (256x256) chunk (1 byte per tile) and a 9 ms average seek time coupled with a 120 MB/s hard drive read speed, it'd take around 9.5 milliseconds to load.

Let's go with a 3x3 grid of chunks (1 for the chunk the player is "in" and then the surrounding 8). Even allowing diagonal movement by the player, you'd only ever be loading in 5 chunks at a time. Assuming a worst case scenario of all chunks being non-contiguous on the HDD, 5 times 9.52 ms = 47.6 ms. Unnoticeably fast, I'd say. Remember that the player is probably only traversing 1 tile at a time, so after a load there's 256 tiles to traverse before another load.

This would allow ridiculously large worlds to be stored on disk and paged in as needed. The only hard part would be tracking things that happened outside of a player's loaded set of chunks. Nothing too difficult I would say. It would just require lots of forethought and planning. I.e., how would the game know where a shopkeeper in a chunk that is no longer loaded would be upon the player's return? You could just "freeze" the NPC positions but this is very immersion breaking for the player. You could use a formula, plugging in time of day and yielding some approximate position. You could remember the NPC's last location, figure out how much time has passed, and then determine how far the NPC could move in that time. Add the two together. Throw in some random possibilities like heart attacks, robbers, etc., etc. Lol. Enough discussion there for a complete new thread IMHO.

Anyway, just some thoughts.

Take care!

Florida, USA
Current Project
Jesus is LORD!
Thanks for the feedback.

Regional proximity is something I didn't think up. With your help, I was able to come up with both questions and answers about it:

1. What should I do to load up a non-existing area for the player to "see" but not "walk" to?
2. What should I load up, land or water-based terrain?

My own answer to these questions are, it should load up a default tile for the non-existent area, and it can check to see if the side of the area is land or water. The problem that I find for the latter answer is that the data format is going to be more complex than I had planned. Thus, I might have to ditch some standard file formats and write up a new one, which is going to become "writing framework, not games" sort of mindset.

I will have to plan that part out a bit more. For clarity, I use a simple universal integer data format for everything, and a loader I normally use for that particular data format. I just don't want to do another framework stuff.

Paging areas is something I'm considering, due to the fact that I'm just going to load up a few areas around the area the player is currently in, and unload the other areas for later. For this part, I haven't come up with a solution to organize how the areas are to be loaded in.

You have a full list of areas the overworld is going to contain, and with only a handful of areas going to be loaded in proximity to the player, I have to use a loop that checks all areas to see if they are marked as "proximity to the player" and such. This is the part where I don't see an efficient way of doing this, thus the solution is currently unclear.

Got any hints on this part?

Not sure how you are representing your "overworld", or how you are storing your areas. From the title (2D) I assumed a simple x/y grid of tile data.

Can you elaborate on how you're storing the data that represents your areas/chunks/world?

Florida, USA
Current Project
Jesus is LORD!

Oh, I see that my explanation was perhaps a bit unclear. With "tile" I don't meant a small (e.g. 64x64 pixel) piece of ground as can be found in 2D games. I meant a load unit of the play field, i.e. a chunk that is loaded / unloaded as a whole. Such a load unit will usually consist of many traditional tiles and their superstructure.

In the sense of a seamless world experience, one has to guarantee that at any moment, under all possible movement directions and maximum speed (in a 3D world also the viewing distance is involved), no content is noticeably missed. That means that the borders of the currently loaded play field must not be reached from the current position, all possible movement directions and the maximal movement speed, before adjacent load units that are predictably be needed soon are loaded and integrated.


1. What should I do to load up a non-existing area for the player to "see" but not "walk" to?
2. What should I load up, land or water-based terrain?

I don't understand this.


You have a full list of areas the overworld is going to contain, and with only a handful of areas going to be loaded in proximity to the player, I have to use a loop that checks all areas to see if they are marked as "proximity to the player" and such. This is the part where I don't see an efficient way of doing this, thus the solution is currently unclear.

The full set of possible load units is used to locate a requested data chunk on disk for streaming in. A state is stored with it (unloaded, loading, loaded, unused), and a reference to a description when loaded.

The player's avatar is placed in exactly one area, let's say it has the co-ordinate indices (x,y). The surrounding areas has the indices (x,y+1), (x+1,y+1), (x+1,y), (x+1,y-1), …, and are already loaded. Maybe you need to have also the next outer ring of areas (x,y+2), (x+1,y+2), (x+2,y+2), (x+2,y+1), … to be loaded; that depends on the size of areas and visibility, movement, as already mentioned above. For the sake of explanation let us simply stay with 1 ring needed.

Now when the player moves the avatar in direction of e.g. increasing m (0,1) and hence enters the area (x,y+1), then the areas (x-1,y+2), (x,y+2), (x+1,y+2) need to be loaded just to guarantee that the ring around the freshly entered area gets completed. At the same time the areas (x-1,y-1), (x-1,y-1), (x+1,y-1) left behind the avatar become unused. They are not unloaded immediately because the player may stop and return to the formerly visited area soon. But areas that are way behind due to former area crossings are actually unloaded now.

If you look at this system then you'll notice there is a distance metric based on the co-ordinate indices. It is so that all areas with co-ordinate indices close enough to the area where the avatar is in are already loaded, areas that are not loaded but are newly close enough due to movement get loaded now, and areas that are far enough due to movement get unloaded now. You can use Euclidean distance metric (in fact a circle) or Manhattan distance or whatever; this choice is IMHO not so critical. However, if you use the above algorithm together with a wider metric you don't actually loop through all areas and look for their proximity. Instead the set of candidates is much more restricted.

Thanks again for your response. I think I have it in my mind, picturing how it is going to load the areas. If only they are much simpler in the first place...

Not sure how you are representing your "overworld", or how you are storing your areas. From the title (2D) I assumed a simple x/y grid of tile data.

Can you elaborate on how you're storing the data that represents your areas/chunks/world?



Each area is stored as a PNG image file. Each pixel in the area represents the tiles, the entities, the obstacles, and trigger events the game will use when conditions are fulfilled. In a 2D world, the PNG image is what a player is going to interact and explore in. It's X/Y axis based. All pixels are loaded independently instead of batch loading. This is how the game was designed.

An overworld is an abstract encapsulation of all the areas loaded from the hard drive. Loading/unloading chunks mean loading/unloading area chunks, which may or may not be larger than the PNG area image itself.


1. What should I do to load up a non-existing area for the player to "see" but not "walk" to?
2. What should I load up, land or water-based terrain?

I don't understand this.

They are answered in my post, so I'm going to reexplain them:

1. When a player explores to the edge of an area, the game cannot fully load the tiles that are outside of an area boundaries, or outside and beyond the edges of an area. That means it cannot load something that isn't drawn in the PNG image file. Therefore, the tiles that aren't fully loaded in are called "non-existent areas."

In this case, what should I do to get the game to render some art that represents the non-existent areas? I can try checking to see if the pixel data exists, and if it doesn't, I can try to render some default tiles for the player to see. For the non-existent areas, maybe a simple collision detection can control the game inputs from letting the player to pass through.

Or maybe, just for added fun, intentionally let the player break the game by allowing the player to move outside of the playing field, and let them seek out some good stuffs.

2. When the non-existent areas are loaded into the game, the game will create a default tile that players can see, but unable to let players walk on it. This means, they act like obstacles in the game, just with a default rendering texture. This texture can be anything that represents land or water. I can use a conditional checking to see if the non-existent areas are nearer to the edge of the area that is land, or water. And then, use the correct default texture (Ground, trees or rocks for land) (Water, lily pads, etc. for water) of the terrain tiles.

Okay, I encountered a problem I have never thought about.

When you load two differents areas of different width and height, how do you "connect" the areas together so that it's seamingless from the player's perspective?

I came up with two different methods, but they are both problematic to the point of me reverting my repo many times.

  1. Load the areas up. Calculate one area's width and height, and try to find the offset values so that the path connects.
  2. Load the areas up. Render the two areas so that the path connects. Then update the player's positions between the two areas.

Issues:

  1. For the first one, rendering more than two areas will be very problematic and uncontrollable. There's no definite solutions if this approach is taken.
  2. For the second one, updating the player's positions mean it will update a lot of flags, and can easily glitch out, causing the player to move out of bounds.

Can anyone think up a good approach that I can use?

This topic is closed to new replies.

Advertisement