views:

177

answers:

9

I am working on a project where the game world is irregularly shaped (Think of the shape of a lake). this shape has a grid with coordinates placed over it. The game world is only on the inside of the shape. (Once again, think Lake)

How can I efficiently represent the game world? I know that many worlds are basically square, and work well in a 2 or 3 dimension array. I feel like if I use an array that is square, then I am basically wasting space, and increasing the amount of time that I need to iterate through the array. However, I am not sure how a jagged array would work here either.

Example shape of gameworld

X
XX
 XX   X XX
 XXX XXX
  XXXXXXX
XXXXXXXX
 XXXXX XX
   XX   X
  X

Edit: The game world will most likely need each valid location stepped through. So I would a method that makes it easy to do so.

+1  A: 

Use a data structure like a list or map, and only insert the valid game world coordinates. That way the only thing you are saving are valid locations, and you don't waste memory saving the non-game world locations since you can deduce those from lack of presence in your data structure.

Ben Burnett
+1  A: 

You could present the world as an (undirected) graph of land (or water) patches. Each patch then has a regular form and the world is the combination of these patches. Every patch is a node in the graph and has has graph edges to all its neighbours.

That is probably also the most natural representation of any general world (but it might not be the most efficient one). From an efficiency point of view, it will probably beat an array or list for a highly irregular map but not for one that fits well into a rectangle (or other regular shape) with few deviations.

An example of a highly irregular map:

x
 x   x
  x x    x
   x     x
    x xxx
     x
    x
   x
  x

There’s virtually no way this can be efficiently fitted (both in space ratio and access time) into a regular shape. The following, on the other hand, fits very well into a regular shape by applying basic geometric transformations (it’s a parallelogram with small bits missing):

xxxxxx  x
 xxxxxxxxx
  xxxxxxxxx
   xx   xxxx
Konrad Rudolph
+1  A: 

The easiest thing is to just use the array, and just mark the non-gamespace positions with some special marker. A jagged array might work too, but I don't use those much.

FrustratedWithFormsDesigner
I agree - just use a `2D` `n x m` array which holds `MapSquare` object references. If it holds a null, then there is nothing there. In this case I prefer a matrix over a graph because it feels more natural.
Hamish Grubijan
+4  A: 

There's computational overhead and complexity associated with sparse representations, so unless the bounding area is much larger than your actual world, it's probably most efficient to simply accept the 'wasted' space. You're essentially trading off additional memory usage for faster access to world contents. More importantly, the 'wasted-space' implementation is easier to understand and maintain, which is always preferable until the point where a more complex implementation is required. If you don't have good evidence that it's required, then it's much better to keep it simple.

Dan Bryant
In my example 66% of the map would be wasted space. At this point though, I am not far enough along to know if I need a sparse representation.
Aaron M
In that case, see kevingessner's answer. You can initially code your map-using primitives based on an array. If you need to, you can later use a different implementation to save memory, though you probably won't need to and will likely get better performance with a simple array.
Dan Bryant
I ran some simple tests on an object I created. It looks like the performance to loop through it is fine for what I need, especially since it doesn't require real-time capabilities
Aaron M
+4  A: 

You could use a quadtree to minimize the amount of wasted space in your representation. Quad trees are good for partitioning 2-dimensional space with varying granularity - in your case, the finest granularity is a game square. If you had a whole 20x20 area without any game squares, the quad tree representation would allow you to use only one node to represent that whole area, instead of 400 as in the array representation.

Jordan Lewis
The problem with a quadtree, is that the gameworld could become 3 dimensional. I would prefect a solution that would work well for either 2 or 3 dimensions
Aaron M
You mean 400 in the last sentence, right?
Mark Byers
Whoops, thanks Mark.
Jordan Lewis
@Aaron M: The 3D version of a quadtree is called an octree: http://en.wikipedia.org/wiki/Octree
Mark Byers
And Aaron, you can use an octtree if you need to extend to 3 dimensions. It's simply an extension of the quadtree that allows for above and below neighbors as well as left and right neighbors.
Jordan Lewis
@Aaron then in that case you can use an Octree which is the 3D big brother of the quadtree http://en.wikipedia.org/wiki/Octree.Quadtree is used for Google Earth and Google Maps so they are also not that bad at 3D until you need 2 things in the same tree to be at the same X, Y coordinate (and depending on why they are both there it may still not be an issue). Each Location can have a Z coordinate as a property for visual representation.
Rangoric
As @Rangoric mentions, a quadtree will work in many cases- if you're truly modeling a game on a lake, a quadtree is perfect. If you're doing caverns that twist and turn and go under and over one another then an octree will be preferable. However if you keep your tree generation and traversal code separate from the rest of the game code, it should be trivial to "upgrade" it.
dash-tom-bang
+4  A: 

Use whatever structure you've come up with---you can always change it later. If you're comfortable with using an array, use it. Stop worrying about the data structure you're going to use and start coding.

As you code, build abstractions away from this underlying array, like wrapping it in a semantic model; then, if you realize (through profiling) that it's waste of space or slow for the operations you need, you can swap it out without causing problems. Don't try to optimize until you know what you need.

kevingessner
A: 

Another way would be to store an edge list - a line vector along each straight edge. Easy to check for inclusion this way and a quad tree or even a simple location hash on each vertice can speed lookup of info. We did this with a height component per edge to model the walls of a baseball stadium and it worked beautifully.

Michael Dorgan
+1  A: 

One other option that could allow you to still access game world locations in O(1) time and not waste too much space would be a hashtable, where the keys would be the coordinates.

Cam
A: 

There is a big issue that nobody here addressed: the huge difference between storing it on disk and storing it in memory.

Assuming you are talking about a game world as you said, this means it's going to be very large. You're not going to store the whole thing in memory in once, but instead you will store the immediate vicinity in memory and update it as the player walks around.

This vicinity area should be as simple, easy and quick to access as possible. It should definitely be an array (or a set of arrays which are swapped out as the player moves). It will be referenced often and by many subsystems of your game engine: graphics and physics will handle loading the models, drawing them, keeping the player on top of the terrain, collisions, etc.; sound will need to know what ground type the player is currently standing on, to play the appropriate footstep sound; and so on. Rather than broadcast and duplicate this data among all the subsystems, if you just keep it in global arrays they can access it at will and at 100% speed and efficiency. This can really simplify things (but be aware of the consequences of global variables!).

However, on disk you definitely want to compress it. Some of the given answers provide good suggestions; you can serialize a data structure such as a hash table, or a list of only filled-in locations. You could certainly store an octree as well. In any case, you don't want to store blank locations on disk; according to your statistic, that would mean 66% of the space is wasted. Sure there is a time to forget about optimization and make it Just Work, but you don't want to distribute a 66%-empty file to end users. Also keep in mind that disks are not perfect random-access machines (except for SSDs); mechanical hard drives should still be around another several years at least, and they work best sequentially. See if you can organize your data structure so that the read operations are sequential, as you stream more vicinity terrain while the player moves, and you'll probably find it to be a noticeable difference. Don't take my word for it though, I haven't actually tested this sort of thing, it just makes sense right?

Ricket