In my experience, basic 2D grid maps are usually done like this:
A map may consist of multiple layers -- but no fewer than 1 layer (multiple layers can allow for parallax scrolling effects, or overlays drawn above the player)
Each layer has at least 2 things: an assigned "tileset", and a 2D grid of tile indexes.
Each tileset is assigned a graphic/texture, as well as any interactive properties of the tiles it contains (ie: is the tile open space or a wall?)
Implementation-wise, I've found it's easiest to load up the tileset into a vector of individual Tile properties. Then the map layers contain a simulated 2D array of pointers to tiles within that tileset vector. The pitfall with this is that the pointers have to stay valid throughout the lifetime of the map, which means the Tiles themselves cannot move in memory... and the Tileset must remain alive for as long as any layers are using them.
The "clip" as you call them can be specified in the tileset information if you need it to be fancy... or if not, you can just have the tile index itself double as the graphic location.
For example, if your graphic has 10 tiles per row.... then tile 0 would be the upper left corner of the graphic, tile 1 would be to its right, etc. Tile 10 would be the left-most graphic on the 2nd row from the top, etc.
Tiles do not need to contain a X,Y coordinate for where they are on the map, as that information is already inferred by their position in the map's 2D array.
Pseudo code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
|
struct Tile
{
int graphicX;
int graphicY;
static const int width = ...;
static const int height = ...;
bool isWall;
//... any other properties you want tiles to have go here
};
class Tileset
{
std::vector<Tile> tiles;
void loadTileset(...)
{
//...
for(int i = 0; ...each tile in file...; ++i)
{
Tile t;
t.graphicX = (i % tiles_per_row) * Tile::width;
t.graphicY = (i / tiles_per_row) * Tile::height;
//... load other properties from the tileset here
tiles.push_back(t);
}
}
// remove move/copy ctors and assignment operators to prevent this class from moving
// around, as that will cause the pointers to go bad.
};
class MapLayer
{
int width;
int height;
std::vector<const Tile*> tiles;
void load(const Tileset& tileset)
{
for( ... each tile in the layer ... )
{
i = read_a_tile_index_from_the_file;
tiles.push_back( tileset.getTile( i ) ); // where 'getTile' returns a pointer to the tile
}
}
};
class Map
{
std::vector<MapLayer> layers;
std::vector< std::unique_ptr<Tileset> > tilesets;
// The tileset allocated dynamically so it doesn't move around in memory
//...
};
|
Take from that what you what