Data Structure for Maps?
Hi
I''m wondering if anyone could enlighten me of a good way to store a Map of a tile based game and the graphic for the tiles?
The most basic way I can think of is having two arrays:
byte Map[maxX][maxY];
Bitmap Tiles[MaxTileTypes];
Then you could draw a tile by doing something like this (Pseudo C/Java/Pascal code):
void drawTile(x,y) {
TileType = Map[x,y]; // get type of tile from map
Bmp = Tiles[tiletype]; // get tile bitmap from an array
drawBitmap(bmp,x,y) // draw bitmap on screen
}
Both the Map and the Tiles array could easily be created in some editor and loaded on game start. Also this concept could be expanded to provide multi-layered maps by making the map array an array of linked lists or something. This gives a little more flexibility.
But all this arrays and hard-coded constants is bugging me! What if I wanted to add or remove some tiles from my tile library (the ''Tiles'' array)? This would possibly alter the order of the tiles in the tile array and then force me to redo all previously saved levels because they will be totally ruined???
There must be a better way!
Regards
nicba
Here''s how I do tile grids:
--
// UNTESTED CODE
// Tile Grid
unsigned char *tileGrid;
// Initialization:
unsigned short gridSize = mapXSize * mapYSize;
tilegrid = new unsigned char [gridSize];
--
Then, address the appropriate square with x+(y*mapXSize).
The main point is that the array is allocated dynamically, and you can deallocate and reallocate each time you load a new level...
Yeh, it''s a little hard to get used to the addressing, but I did...
--
// UNTESTED CODE
// Tile Grid
unsigned char *tileGrid;
// Initialization:
unsigned short gridSize = mapXSize * mapYSize;
tilegrid = new unsigned char [gridSize];
--
Then, address the appropriate square with x+(y*mapXSize).
The main point is that the array is allocated dynamically, and you can deallocate and reallocate each time you load a new level...
Yeh, it''s a little hard to get used to the addressing, but I did...
Why not just make a Tile class? Your map would then be a 2D array of Tiles. The tile class would store pointers to the correct Bitmap and any overlay Sprites that need to be draw there as well. The only other thing you would need is an array for the Bitmaps and Sprites, but you will only need it to set the pointers in newly created Tile object and then you can leave it alone. Using a Tile class is much more flexible too, since you can easily add extra info to your tiles (like visibility if fog of war is on or anything else you can imagine...)
Check out the GPI project today!
Check out the GPI project today!
Hi
Thank you for your answers.
To VGASteve:
Yes. Doing allocation of maps/levels dynamically is a good idea. But I originally left it out of my post because that wasn''t really the problem. The problem is the container (array, list, big bitmap or whatever) that holds your tile bitmaps. You didn''t specify how you intend to draw the tiles with the correct bitmaps.
To chippydip:
Yes. I have also thought of using a class for each tile. And I propaly do so in my game. Using pointers to bitmaps at runtime is a very good idea. Then I can add and remove bitmaps to the list of all the available bitmaps without worying of destroying indexes in the map.
But pointers can''t be stored to a disk. So here is my next question:
What should I then write in my *.map files to indentify the type of tile on a given (x,y) coordinate in the map? Should I still use constants for each type of tile? Or should I instead name each type of tile with a string, as for example ''STONEFLOOR'', ''GRASS'' and so on?
How do you ensure that the map/level editor and the game "agrees" on which tiles can be used in the game and the identifiers used to describe each type of tile is consistent between the game and the editor?
For example if i place a ''grass'' tile on (x,y) in the editor, the game should be able to read this from the map file and not place a ''stone'' tile on that coordinate instead.
Regards
nicba
Thank you for your answers.
To VGASteve:
Yes. Doing allocation of maps/levels dynamically is a good idea. But I originally left it out of my post because that wasn''t really the problem. The problem is the container (array, list, big bitmap or whatever) that holds your tile bitmaps. You didn''t specify how you intend to draw the tiles with the correct bitmaps.
To chippydip:
Yes. I have also thought of using a class for each tile. And I propaly do so in my game. Using pointers to bitmaps at runtime is a very good idea. Then I can add and remove bitmaps to the list of all the available bitmaps without worying of destroying indexes in the map.
But pointers can''t be stored to a disk. So here is my next question:
What should I then write in my *.map files to indentify the type of tile on a given (x,y) coordinate in the map? Should I still use constants for each type of tile? Or should I instead name each type of tile with a string, as for example ''STONEFLOOR'', ''GRASS'' and so on?
How do you ensure that the map/level editor and the game "agrees" on which tiles can be used in the game and the identifiers used to describe each type of tile is consistent between the game and the editor?
For example if i place a ''grass'' tile on (x,y) in the editor, the game should be able to read this from the map file and not place a ''stone'' tile on that coordinate instead.
Regards
nicba
I would suggest using constants, the computer does not need to store 16bytes of data to destinguish a stone texture from a grass texture, a 1 or 2 byte field will do for it.
As for the editor agreeing with the game on what each graphic is, just use the same data files between the two. This way any modifiactions to the data files will be reflected by both.
Hope that helps.
OneEyeLessThanNone
As for the editor agreeing with the game on what each graphic is, just use the same data files between the two. This way any modifiactions to the data files will be reflected by both.
Hope that helps.
OneEyeLessThanNone
quote: Original post by OneEyeLessThanNone
I would suggest using constants, the computer does not need to store 16bytes of data to destinguish a stone texture from a grass texture, a 1 or 2 byte field will do for it.
As for the editor agreeing with the game on what each graphic is, just use the same data files between the two. This way any modifiactions to the data files will be reflected by both.
Hope that helps.
OneEyeLessThanNone
Yes it helps. Thank you for answering. I think I have figured out how to do it now.
The reason I suggested using strings instead of constants is that I plan to use the same set of tiles for more than one map. So the identifiers for each tile must never change once it has been assigned to a specific tile type. Even if you add new tile types to the set or edit the existing ones.
I thought that maybe giving each tile a descriptive name would make this easier to achieve, but after seening your post and thinking things over a second time I realized that the user never needed to see or modify these identifiers. It could all be handled by the map editor. So probality I go for the constants after all.
Thanks for all the answers.
Regards
nicba
Identifiers or constants:
One of the things I often see overlooked in almost all samples of codes is the enum type. It''s really useful for combining constants in a sensible way.
At its simplest form:
In this case, each of these defines are given numbers, the first starting with 0 and going up from there. This is great because in your code you can refer to an index as tile_array[TILE_OAK_TREE] and never have to worry about where exactly it is going. You would do a LoadBitmap(&tile_array[TILE_OAK_TREE], "oaktree.bmp") so that it would be loaded into TILE_OAK_TREE and from there on you can refer to it anytime you need to. I really like using enums to handle indexing into arrays. I often use them like this
This lets me add new objects after OBJECT_KEY and it will expand the array for me. I just need to be sure that OBJECT_LIST_SIZE is always last in the list. The other benefit is that I now always have the size of the array for loop code:
One of the things I often see overlooked in almost all samples of codes is the enum type. It''s really useful for combining constants in a sensible way.
At its simplest form:
typedef enum { TILE_GRASS_1 = 0, TILE_GRASS_2, TILE_GRASS_3, TILE_GRASS_4, TILE_WATER_1, TILE_CHEST_OPEN, TILE_CHEST_CLOSED, TILE_OAK_TREE,} TileTypes;
In this case, each of these defines are given numbers, the first starting with 0 and going up from there. This is great because in your code you can refer to an index as tile_array[TILE_OAK_TREE] and never have to worry about where exactly it is going. You would do a LoadBitmap(&tile_array[TILE_OAK_TREE], "oaktree.bmp") so that it would be loaded into TILE_OAK_TREE and from there on you can refer to it anytime you need to. I really like using enums to handle indexing into arrays. I often use them like this
typedef enum { OBJECT_CLOCK = 0, OBJECT_DOOR, OBJECT_MAN, OBJECT_KEY, OBJECT_LIST_SIZE, // this must always be last} ObjectList;struct ObjectType g_ObjectArray[OBJECT_LIST_SIZE];
This lets me add new objects after OBJECT_KEY and it will expand the array for me. I just need to be sure that OBJECT_LIST_SIZE is always last in the list. The other benefit is that I now always have the size of the array for loop code:
for (int i = 0; i < OBJECT_LIST_SIZE; i++){ if (object_list != NULL) { free(object_list);<br> object_list = NULL;<br> }<br>}<br></PRE><br><br>This code will always free all the objects no matter how many I have in the enumerated type list.<br><br>Additionally, you can specify a numeric equivalance often:<br><br><PRE><br>typedef enum<br>{<br> // zone 1 objects<br> ZONE1_CLOCK = 0,<br> ZONE1_DOOR,<br> ZONE1_MAN<br> // zone 2 objects<br> ZONE2_HUMAN = 20,<br> ZONE2_PERSON,<br> ZONE2_TREE,<br> // zone 3 objects<br> ZONE3_DOG = 40,<br> ZONE3_ROBBER,<br> // ..<br><br><br></PRE><br><br>After each number is set, the following constants are an increment of the above. ZONE2_PERSON == 21, ZONE3_ROBBER == 41. This is a good scheme if you are refering to objects in zones in a file and don''t want to have to shuffle numbers around too much when you add a new bitmap - you just make all ZONE2 objects in the object_list[20 .. 39] range..<br><br>Anyways..<br><br> </i>
I've thought about various ways to implement tiles and this is what I came up with...
I'm intending this to be for a game similar to Jagged Alliance/X-com but with more emphasis on character development.
I use a linked list approach so I can stack as many or as few tiles on top of each other as I want. The npc* could (and probably should) be a byte that indexes a particular non-player character in an array.
struct tile_type{
_8pos alt,//altitude or y-offset
gfx,//index to gfx
flags;//misc flags
restrict_type restrictions;//line of sight/walking
tile_type* next;//next tile in list
};
class base_tile{
private:
_8pos myflags,//misc flags
mygfx;//gfx index
restrict_type myrestricts;//line of sight/walking
npc* mynpc;//npc at this spot?
item* myitem;//item?
tile_type* mycurrent,//for list
myfirst,
myprevious;
...
Hopefully the formatting wasn't too screwed up.
And then I would have an array of base_tile s:
base_tile map[MAX_Y][MAX_X];
Edited by - Jman on 4/11/00 4:11:53 PM
I'm intending this to be for a game similar to Jagged Alliance/X-com but with more emphasis on character development.
I use a linked list approach so I can stack as many or as few tiles on top of each other as I want. The npc* could (and probably should) be a byte that indexes a particular non-player character in an array.
struct tile_type{
_8pos alt,//altitude or y-offset
gfx,//index to gfx
flags;//misc flags
restrict_type restrictions;//line of sight/walking
tile_type* next;//next tile in list
};
class base_tile{
private:
_8pos myflags,//misc flags
mygfx;//gfx index
restrict_type myrestricts;//line of sight/walking
npc* mynpc;//npc at this spot?
item* myitem;//item?
tile_type* mycurrent,//for list
myfirst,
myprevious;
...
Hopefully the formatting wasn't too screwed up.
And then I would have an array of base_tile s:
base_tile map[MAX_Y][MAX_X];
Edited by - Jman on 4/11/00 4:11:53 PM
You might want to consider a more flexible method for storing the tile indexes in a file. If you use a simple enum then the program (and editor, etc.) will need to be rebuilt any time a new type of tile is added to the system. Instead, why not have a header section in your *.map file that defines constants for the bitmap resources that will be used in that map. It might look something like this:
0 "grass.bmp"
1 "stone.bmp"
2 "forest.bmp"
... etc ...
<map tile entries go here...>
The map tile entries would then include the number (0..n) for the desired bitmap. In effect you are storing both the array of bitmaps and the arrray of tiles. This method will allow you to add tiles whenever you want without changing your code yet doesn''t require all the overhead of a tile name for each map tile (and it should be easier to reassemble both data structures with this format)
Also, the header does not need to include the file name of the bitmap. It could include other information if you decided to use a more complex resource management system, but the basic priciple remains the same... just provide whatever info you need to read in the correct tile image.
Check out the GPI project today!
0 "grass.bmp"
1 "stone.bmp"
2 "forest.bmp"
... etc ...
<map tile entries go here...>
The map tile entries would then include the number (0..n) for the desired bitmap. In effect you are storing both the array of bitmaps and the arrray of tiles. This method will allow you to add tiles whenever you want without changing your code yet doesn''t require all the overhead of a tile name for each map tile (and it should be easier to reassemble both data structures with this format)
Also, the header does not need to include the file name of the bitmap. It could include other information if you decided to use a more complex resource management system, but the basic priciple remains the same... just provide whatever info you need to read in the correct tile image.
Check out the GPI project today!
Hi fellow Dane...I'm making a tilebased game (and an editor) at the moment. Here is my fileformat.
It is not fancy but it works...
int number_of_tilescreens //number of bmp´s with tiles.
for each number_of_tilescreens
{
int TileXSize //size of tiles in particular bmp.
int TileYSize
int Transparency //if =0 blit from bmp using no trans.
char[256] filename //bmp filename
}
int number_of_layers
for each number_of_layers
{
int MapSizeX
int MapSizeY
int TileSizeX
int TileSizeY
}
The tiledata:
for each layer I just save bunch of shorts at the end of the file...The data can easily be extracted by using the data above...
short Gfx_screen
short Tilenr
I don't use arrays to store my tiledata in memory.
I load the tiledata into memory-chunks freed by malloc().
This makes it more difficult to find a specific tile but not much.
-- There IS something rotten in the state of Denmark --
Edited by - granat on 4/13/00 1:08:14 AM
Edited by - granat on 4/13/00 1:12:51 AM
It is not fancy but it works...
int number_of_tilescreens //number of bmp´s with tiles.
for each number_of_tilescreens
{
int TileXSize //size of tiles in particular bmp.
int TileYSize
int Transparency //if =0 blit from bmp using no trans.
char[256] filename //bmp filename
}
int number_of_layers
for each number_of_layers
{
int MapSizeX
int MapSizeY
int TileSizeX
int TileSizeY
}
The tiledata:
for each layer I just save bunch of shorts at the end of the file...The data can easily be extracted by using the data above...
short Gfx_screen
short Tilenr
I don't use arrays to store my tiledata in memory.
I load the tiledata into memory-chunks freed by malloc().
This makes it more difficult to find a specific tile but not much.
-- There IS something rotten in the state of Denmark --
Edited by - granat on 4/13/00 1:08:14 AM
Edited by - granat on 4/13/00 1:12:51 AM
-------------Ban KalvinB !
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement