Sign in to follow this  
the-trav

top down 2d scrolling on a large map

Recommended Posts

Ok, so I've made a few fairly limited games before, both 2d and 3d.

I'm playing around with the idea and code for a sort of simplistic notrium clone / extension.

For those who haven't played it notrium is a sci fi survival game, with a top down 2D WASD movement and mouse aimed melee or ranged attacks.


Notrium broke its maps up into square sectors, so far as I can tell it is simple enough to load an entire sector into memory and transition to a new sector at the edges.

I would prefer, if possible, to have a much larger, possibly infinite / complete world so far as the player is concerned (no map edge transitions)

I'm thinking about the best way to do it, and I was hoping to get some advice / feedback from more experienced devs before I spent too long prototyping several bad solutions (I've already spent at least a few weeks playing with the first idea I had)

For context I've been implementing a sort of 2d in 3d setup so far (using actual models for characters, but just quads for terrain) using Java Monkey Engine (java opengl engine, it provides me a scene graph, an asset manager and bullet physics for collisions)



I don't want to load the entire world into memory, so I figure some sort of streaming is called for. So far I've got a grid of tiles loaded (about 10x10) pretty basic, the current plan is to re-position this grid every frame (keep it in the center of the screen), and then set the tile textures to whatever is appropriate for that position.


This way I don't need a scene graph with hundreds of thousands of quads in it, which should save on memory space right?


The other unknown I've got is whether really large numbers for position will mess with things at all. Is it worth doing the maths each render to calculate things as positions relative to the player? Or should I just stick with global coords that don't change?

Share this post


Link to post
Share on other sites
You are still going to have some form of record in memory, or frequently accessed from a file, to know what tile needs to be loaded next.

If we are talking about a 2d world, you load into the material buffer only the material you need to survive for the moment and then procedurally load the material as you find its needed.

So for a simple example, if i have half the world as ice texutres, the other half as grass, if i am located in the grass part of the world i wouldnt need to load the ice textures. As i move closer to the ice segments of the world, i could load up the ice textures, and as i move away from the grass areas, unload the grass textures.

Share this post


Link to post
Share on other sites
The solution you're looking for is called streaming -- all the big-world AAA games do it, from free-roaming MMORPGs, to GTA, to Halo 3. In first-person games where one can look around freely there is the added complication of being able to see all the way to the horizon, so they actually have to load a whole lot more of the world than you'll need to. They solve that problem by loading far-away sections of the map with lower geometric detail, which is usually accomplished by mip-mapping the height-field, and loading very low-resolution textures to cover them.

Luckily for you, being an overhead game gives you lots of room to make simplifying assumptions -- for example you know exactly how much of the world is within the camera's view and dealing with different level-of-details for geometry and textures shouldn't be necessary (perhaps if you can zoom way, way out).

A simple solution is to break the game world up into small square (or perhaps hexagonal if the world is more free-formed) sections -- they don't need to be the size of the screen or anything, just small enough to make them manageable without being so small that they're a burden.

In my tile-based worlds, I've found that square sections of 4x4 or 16x16 tiles tend to work best. I make these sections the leaves of a quad-tree structure or nodes of a graph and simply draw the whole thing as long as its within a certain distance of the player. The active distance is usually a square box with sides 3-4 times the width of the screen (or a radius twice the width of the screen, in the case of hexagonal sections) centered on the player's current section.

Having the active sections fall outside the strict view of the screen has a couple benefits: First, since each section will first load when it is out-of-sight, there's plenty of time to get it fully loaded before the player will ever see it. Second, entities in those sections will also be active long before the player walks into them, which helps make the player feel like the world doesn't stop just beyond where they can see -- NPCs will approach from of-screen, unseen enemies will rush you, and weaklings will flee out of view.

You only hold in memory those sections that are nearby and stream in new sections from disk -- since you'll be loading section-by-section you need to optimize your map file format for this access pattern, if you store the map on disk as a great-big 2D array, your disk will be seeking all over kingdom-come slowing your effective read speed, thrashing the disk's cache, and helping any mechanical disk to wear out prematurely. Each section should contain its geometry, and must somehow reference the necessary texture data. You could simply have each section contain a complete list of references to each texture it needs, but a more-efficient idea might be to define "regional" texture palettes (towns, grasslands, deserts, etc) which are shared between many map sections (sections would need to be able to reference multiple texture palettes, but the majority would reference only one, and perhaps 90% would be satisfied by just two if appropriate palette sizes are chosen -- in any case, those "extra" sections would almost certainly be needed by bordering sections, so a reference-counted scheme will pretty-much ensure minimal memory use. Throw those regional texture palettes into an LRU-cache and you can eliminate redundant loads to free up disk I/O bandwidth. Then you're golden.

The only somewhat-difficult thing to do is that this loading must happen in the background, otherwise the game may stutter each time new sections are loaded -- this stutter may be so small as to be unnoticeable but, but relying on that to always remain the case is not a very robust design. The better solution is to implement background-loading by putting it all in a separate thread. A less-robust but thread-free solution would be to put load requests into a list, sort the list for the most-urgently-needed sections each frame, and load one (or a handful) of them each frame (or few frames) at a slow enough rate that the stutter never becomes noticeable.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this