• Create Account

#ActualServant of the Lord

Posted 27 November 2012 - 02:10 PM

Excellent description of your intents. If you can't describe it, you have a problem - but you described it very succinctly.

Are chunks are composed of tiles? Or objects?

Programming rule #3421: Premature optimization is the root of many evils.
Programming rule #745: KiSS: Keep it Stupidly Simple (alternatively, Keep it Simple, Stupid)

Have you actually tried just making a grid of uniform chunks, and actually seeing if it's too slow for your program?

But that aside, let's see what we can do. It helps simplify things if you put stuff into their own classes or structs.

Your game world is composed of Objects (enemies, walls, the player, moving platforms, spikes, etc...).

Each object has a location in the world, probably in a pixel-like measurement unit.
So: World has 100,000+ objects. Obviously we can't load and process all those at the same time. (Actually with modern computers, we probably could - but we'll pretend we can't).

So you break your world into 'chunks'. World has a grid of smart pointers to Chunks (yes, a grid). Each location on the grid can either be a null smart pointer (thus saving you your precious memory ), or can have a smart pointer to a valid Chunk.

A World:
• Has a grid of chunks.
• Streams chunks in and out around the player.
• Tells chunks to draw, update, etc...
A chunk is just a "bucket" of entities. Any solid who's center is over the chunk's boundaries, belongs to that chunk.
A Chunk:
• Tells the Objects within it to draw, think, etc...
• If an Object Entity walks off of one chunk, that chunk passes ownership of that object to the next chunk.
When 'unloaded', the Chunk still exists, the Objects are destroyed (until the next reload), unless that particular Object is persistent and needs to think even when distant from the player, in which case they aren't drawn and only need to be updated at a much slower update rate.
Almost all walls are persistent, but don't need to think when the player isn't near. Almost all enemies need to think, but don't need to be persistent. Some enemies need to think and be persistent (a special enemy hunting the player from a million miles away, for example).

Maybe you want to specify to only keep an object in memory if it's within a certain range.

Let's convert this directly to pseudo-code:
Object
{
Position
IsPersistent
RangeToKeepInMemory //The distance away the player needs to be before it is destroyed.

bool SaveStateWhenDestroyed() -> (IsPersistant && hasChanged)
bool KeepInMemory(playerDistance) -> (playerDistance > RangeToKeepInMemory)

Save()

Draw()
Update(deltaTime) //Think, animate, etc...
}

Chunk
{
Vector<Object::Ptr> Objects

StreamOut() //Unloads all objects except those it needs to keep in memory.
StreamIn() //Loads all persistent objects, like walls and enemies that don't spawn but have preset locations like bosses.

Draw() -> Draw every object
Update(deltaTime) -> Update every object
}

World
{
Grid<Chunk::Ptr> Chunks;

StreamChunksAround(location);

Draw() -> Draws all chunks nearby.
}


This can be improved upon, but it's a good start and really straightforward and simple. It also does not waste much memory or speed.

You can steal my C++ Grid class here - re-sizable and permits negative indices. If you know the size of your World ahead of time, and it doesn't change during the course of that play, you could use a std::vector of size (width*height) instead.

The whole map<map<Object>> thing just doesn't seem like good design.

#1Servant of the Lord

Posted 27 November 2012 - 02:07 PM

Excellent description of your intents. If you can't describe it, you have a problem - but you described it very succinctly.

Are chunks are composed of tiles? Or objects?

Programming rule #3421: Premature optimization is the root of many evils.
Programming rule #745: KiSS: Keep it Stupidly Simple (alternatively, Keep it Simple, Stupid)

Have you actually tried just making a grid of uniform chunks, and actually seeing if it's too slow for your program?

But that aside, let's see what we can do. It helps simplify things if you put stuff into their own classes or structs.

You have:
- A world (which might be the entire game, or just a single level)
- Objects (enemies, walls, the player, moving platforms, spikes, etc...)

Each object has a location in the world, probably in a pixel-like measurement unit.
So:
World has 100,000+ objects. Obviously we can't load and process all those at the same time. (Actually with modern computers, we probably could - but we'll pretend we can't).

So you break your world into 'chunks'. World has a grid of smart pointers to Chunks (yes, a grid). Each location on the grid can either be a null smart pointer (thus saving you your precious memory ), or can have a smart pointer to a valid Chunk.

A World:
• Has a grid of chunks.
• Streams chunks in and out around the player.
• Tells chunks to draw, update, etc...

A chunk is just a "bucket" of entities. Any solid who's center is over the chunk's boundaries, belongs to that chunk.
A Chunk:
• Tells the Objects within it to draw, think, etc...
• If an Object Entity walks off of one chunk, that chunk passes ownership of that object to the next chunk.
When 'unloaded', the Chunk still exists, the Objects are destroyed (until the next reload), unless that particular Object is persistent and needs to think even when distant from the player, in which case they aren't drawn and only need to be updated at a much slower update rate.
Almost all walls are persistent, but don't need to think when the player isn't near. Almost all enemies need to think, but don't need to be persistent. Some enemies need to think and be persistent (a special enemy hunting the player from a million miles away, for example).

Maybe you want to specify to only keep an object in memory if it's within a certain range.

Let's convert this directly to pseudo-code:
Object
{
Position
IsPersistent
RangeToKeepInMemory //The distance away the player needs to be before it is destroyed.

bool SaveStateWhenDestroyed() -> (IsPersistant && hasChanged)
bool KeepInMemory(playerDistance) -> (playerDistance > RangeToKeepInMemory)

Save()

Draw()
Update(deltaTime) //Think, animate, etc...
}

Chunk
{
Vector<Object::Ptr> Objects

StreamOut() //Unloads all objects except those it needs to keep in memory.
StreamIn() //Loads all persistent objects, like walls and enemies that don't spawn but have preset locations like bosses.

Draw() -> Draw every object
Update(deltaTime) -> Update every object
}

World
{
Grid<Chunk::Ptr> Chunks;

StreamChunksAround(location);

Draw() -> Draws all chunks nearby.