Thread: Tile map loading/saving

  1. #1
    ---
    Join Date
    May 2004
    Posts
    1,379

    Tile map loading/saving

    I have been putting a lot of effort into making a tile engine but I'm stuck on the map loading and saving. I want to save the map in a file so that I dont have 50*50 entries of a struct for each tile. The reason why I'm posting here is because I don't know a lot about binary file input/output. The way I have my engine load the tiles is by referencing the x and y position in a large bitmap file (of all tiles). Here is the structure I'm using.
    Code:
    typedef struct{
        unsigned int ID;      // Tile ID
        unsigned int layer;  // Tile layer
        unsigned int xpos;  // xpos of tile in bitmap
        unsigned int ypos;  // ypos of tile in bitmap
    }TILE;
    I have read some articles on it but they don't go into too much detail. I'm hoping someone has ideas on whether I should use text files or binary files and how to go about designing them, or better yet, if someone has made a loader of their own that I could take a peek at.

  2. #2
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I ran into the same issue and decided that tile maps are really just arrays that are drawn by doing 'paint by number' or draw bitmap B at position x,y. So they really just boil down to simple arrays. So you can use _read, _open, _write, etc, for disk access on the array. Create your own type of header and then write the map data. Sort of like a raw bitmap file.

  3. #3
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    I wouldnt store the x,y coords. Just use the x,y index intot the array as the x,y coords and do the same thing when storing in the file.

  4. #4
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    Quote Originally Posted by Perspective
    I wouldnt store the x,y coords. Just use the x,y index intot the array as the x,y coords and do the same thing when storing in the file.
    He said "xpos of tile in bitmap", not the position of the tile in the world.
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  5. #5
    Registered User Dante Shamest's Avatar
    Join Date
    Apr 2003
    Posts
    970
    I use text files with the following format.

    Code:
    width of map
    height of map
    number of layers
    
    tiles arranged by IDs as how they would appear in the map
    For example, one of my map files looks like this.

    Code:
    14
    10
    1
    
    1 1 1 1 1 1 1 1 1 1 2 2 2 2
    1 0 0 0 0 0 0 0 0 1 2 2 2 2
    1 0 0 0 0 0 0 0 0 1 2 2 2 2  
    1 0 0 0 0 0 0 0 0 1 1 1 1 1
    1 0 0 0 0 0 0 0 0 0 0 0 0 1
    1 0 0 0 0 0 0 0 0 0 0 0 0 1
    1 0 0 0 0 0 0 0 0 1 1 1 1 1
    1 0 0 0 0 0 0 0 0 1 2 2 2 2
    1 0 0 0 0 0 0 0 0 1 2 2 2 2
    1 1 1 1 1 1 1 1 1 1 2 2 2 2
    You might think it's hard to parse such information, but it's incredibly easy if you're using C++ stream classes. Just use cin and the >> operator. It's also very easy to check if there's a parsing error, just use the ! operator.

    So reading the first 3 lines is trivial:

    Code:
      std::ifstream file( filename );
    
      if( file.is_open() == false )
        return false;
    
      unsigned int width, height, depth;
      if( !(file >> width ) ) return false;
      if( !(file >> height) ) return false;
      if( !(file >> depth ) ) return false;
    Now that you know the width, height and depth of the map, reading the tile IDs is also simple:

    Code:
      for( unsigned int z=0; z<depth;  ++z )
      for( unsigned int y=0; y<height; ++y )
      for( unsigned int x=0; x<width;  ++x )
        if( !(file >> temp[x][y][z].id) )
          return false;
    I prefer text files to binary files because it's human-readable, and you don't have to program a fancy tile editor. If you have Notepad, you can edit the tile values easily.

    Hope that helps.

  6. #6
    ---
    Join Date
    May 2004
    Posts
    1,379
    Yes I think a text file is a simpler way for me to go. Maybe in the future I will change it. I actually thought of something similar to Dante's idea last night. Thanks for your ideas guys.

  7. #7
    ---
    Join Date
    May 2004
    Posts
    1,379
    For anyone interested here is what I decided to do.
    It's so simple I should have thought of it earlier.
    Code:
    int CMap::LoadMap(char *filename){
      FILE *fp;
      int **temp;
      int ch;
      
      /* Allocate space for 2D array */
      temp = new int*[y_tiles];
      for(int i=0;i<y_tiles;i++){
        temp[i] = new int[x_tiles];
      }  
    
      /* Open map file */
      if((fp=fopen(filename,"r")) == NULL){
        printf("Couldn't open %s",filename);
      }
      
      /* Read map file */
      for(int i=0;i<y_tiles;i++){
        for(int j=0;j<x_tiles;j++){
          temp[i][j] = getc(fp);
          
        }
        ch = getc(fp); //catches '\n'
      }    
      
      /* Assign map data */
      for(int i=0;i<y_tiles;i++){
        for(int j=0;j<x_tiles;j++){
          switch(temp[j][i]){
            case 'm':
              MAP[j][i].layer = 1;
              MAP[j][i].xpos  = 96;
              MAP[j][i].ypos  = 0;
              break;
            case 'g':
              MAP[j][i].layer = 1;
              MAP[j][i].xpos  = 0;
              MAP[j][i].ypos  = 0;
              break;
            case 'w':
              MAP[j][i].layer = 1;
              MAP[j][i].xpos  = 32;
              MAP[j][i].ypos  = 0;
              break;
            case 't':
              MAP[j][i].layer = 1;
              MAP[j][i].xpos  = 128;
              MAP[j][i].ypos  = 0;
              break;
            case 'd':
              MAP[j][i].layer = 1;
              MAP[j][i].xpos  = 64;
              MAP[j][i].ypos  = 0;
              break;
            default:
              printf("Error parsing map. Check map format\n");
              return 1;
            }  
          
        }
      } 
     
      /* Clean up */
      fclose(fp);
      for(int i=0;i<y_tiles;i++){
        delete[] temp[i];
      }
      delete temp;   
      
      return 0;
    }
    map text file:
    m = mountain
    g = grass
    w = water
    d = dirt
    t = tree
    Code:
    mmmmmmmmmmmmmmmmm
    mmmmmmmmmmmmmmmmm
    mmggggggwwwwwwwmm
    mmggggggwwwwwwwmm
    mmggggggtwwwwwwmm
    mmggggggtttwwwwmm
    mmggggggtttttwwmm
    mmggggggggddddwmm
    mmggggggggggdddmm
    mmgggggggggggddmm
    mmgggggggggggggmm
    mmgggggggggggggmm
    mmgggggggggggggmm
    mmgggggggggggggmm
    mmgggggggggggggmm
    mmgggggggggggggmm
    mmmmmmmmmmmmmmmmm
    What I have to do now is change my DrawMap method to relate to the layers
    Last edited by sand_man; 04-11-2005 at 07:22 PM.

  8. #8
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I've got a tilemap class up and running and I just use the data as a simple array. It is just a class that encapsulates access to a 1D array - loading and saving is as simple as doing block reads and writes to disk. My editor then creates the file and creates the header for the tile map which I read in at load time. This is a proprietary format but it means that people must use my editor in order to create valid maps for the game.

    The structure is very simple and is akin to raw files.

    Header
    Data
    Footer

    The footer contains information that identifies the map file as being valid for the engine.

  9. #9
    ---
    Join Date
    May 2004
    Posts
    1,379
    That's the sort of thing I was thinking about originally but I don't want to spend too much time on the map loading yet. After I have all the basics set up I can improve things like that later.

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    Code:
    struct MapHeader 
    {
      DWORD mhWidth;
      DWORD mhHeight;
      char      mhVerMajor;
      char      mhVerMinor;
      //For future expansion
      BYTE     Reserved[64];
    };
    
    class TileMap
    {
       protected:
         MapHeader  m_pMapInfo;
         DWORD *m_pMapData;
    
       public:
         TileMap(void):m_pMapData(NULL) 
         {
           memset ((MapHeader *)m_pMapInfo,0,sizeof(m_pMapInfo));
         }
         virtual ~TileMap(void)
         {
            if (m_pMapData)
            {
               delete [] m_pMapData;
               m_pMapData=NULL;
            }
         }
    
         bool Load(std::string strFilename)
         {
            int handle=_open(strFilename.c_str(),_O_BINARY | _O_RDONLY,_S_IREAD);
            if (handle==-1) return false;
    
           read (handle,(MapHeader *)m_pMapInfo,sizeof(MapHeader));
           
           //Size in bytes
           DWORD datasize=m_pMapInfo.mhWidth*m_pMapInfo.mhHeight;
    
           //Allocate memory
          //Check for re-allocation 
          if (m_pMapData)
          {
             delete [] m_pMapData;
          }
          m_pMapData=new DWORD[datasize];      
          if (!m_pMapData) return false;
           
           //Read map data
           read (handle,(DWORD *)m_pMapData,datasize*sizeof(DWORD));
    
           close(handle);
         }
    
    //More functions here
    
    };
    Very simple loading example in this class. Not necessary to make it overly complex. This loads a header and then the data. But you can change it to do whatever you want.

  11. #11
    Registered User LogicError's Avatar
    Join Date
    Aug 2003
    Location
    г. Магнитогорск
    Posts
    76
    Umm.. I had this kind of problem too.. Maybe my way isn't the best.. still works.. All I do is save the x,y coords, file, parameters into a textfile like this:

    0,0(wall.bmp,0,0,1)
    0,1(door.bmp,1,0,0)

    The parameters can include whether the object is a wall (you can't walk through), or maybe it takes you to another map once you walk on it..)

    Tell me what you think

  12. #12
    Crazy Fool Perspective's Avatar
    Join Date
    Jan 2003
    Location
    Canada
    Posts
    2,640
    >>Tell me what you think

    you could make loading a little easier if you did something like this.

    1 door.bmp
    2 wall.bmp
    ...etc...
    0,0,2,0,0,1
    0,1,1,1,0,0

  13. #13
    Registered User
    Join Date
    Mar 2004
    Posts
    220
    Some expansion: You might want to consider triangle, hexagon, octagon, and pentagon shaped tiles. The mathematics used in working with these formats of tiles are _somewhat_ complex compared to rectangle based tiles, however, it's easy once you use them frequently and get used to working with them in different ways.

    Grr, and take out the line breaks in that text file!! :P, you'll savor those 8 bits one of these days I can promise you! That is if you get into embedded development sometime hehehe.

    Also, you will appreciate binary reading once you realize that you'll only ever be using n ID variations. So if you only support 10 Tiles, all you would need is 4 bits for that map file section. Half a reduction, worth it isn't it?

    If you want to make the translation from text to binary thinking / coding, I would highly suggest commenting every variation you have of your variables that will be put into binary at the top of your source file, and then looking 10 pages up if you are really stuck on a value that you can't remember. Macros can be used to check for any invalid values of your binary notation as well. I'm suggesting macros for validation purposes and NOT coding purposes by the way(e.g #define treeTerrain 0x01 or w/e, very bad practice), I say this because eventually you'll have so many macros that the compiler will spend more time on preprocessor directives / whatever macro engine you're using, that you would be better off writing your terrain IDs for instance in hex in the first place. It's also nice if you code in lower level like this so that you can actually see the code, at times I've seen a bunch of cool variations/optimizations that I could do from working with the raw hex values.
    Last edited by Tronic; 04-13-2005 at 01:56 PM.
    OS: Windows XP Pro CE
    IDE: VS .NET 2002
    Preferred Language: C++.

  14. #14
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    What Perspective is using is exactly what I use. Each bitmap in the list is associated with an index which I call an ID. This breaks the map down into numbers or ID's. Load and save that and you have your map.

    Your texture list's are totally seperate from your maps. The way you are doing it, by hard-coding too much information into the map file, you will severely limit yourself as to what you can do.

    A tile does not care where it is, what map it is a part of, whether or not you can walk on it, etc, etc.

    A tile map only cares about tile IDs.

    A movement map only cares about movement booleans or values - when you seperate the movement values from the tiles, again you can specify that ANY tile at ANY time at ANY location can be changed from a valid move area to invalid.

    Tile ID's greater than oh say...255 could identify animated tiles. The sky is the limit.

    And just before you think we don't know we are talking about:

  15. #15
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    I see no need to complicate the loading and saving of the map with some assinine binary standard. Just write the bytes to a file doing a block write and read using a block read directly into an array.

    You will thank yourself for doing so. I don't think hard disk space is a consideration here.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Smooth walking on tile based map system
    By abraham2119 in forum C Programming
    Replies: 8
    Last Post: 07-10-2009, 10:33 AM
  2. Polynomials and ADT's
    By Emeighty in forum C++ Programming
    Replies: 20
    Last Post: 08-19-2008, 08:32 AM
  3. Need testers for editor
    By VirtualAce in forum Game Programming
    Replies: 43
    Last Post: 07-10-2006, 08:00 AM
  4. New editor updates
    By VirtualAce in forum Game Programming
    Replies: 8
    Last Post: 11-05-2005, 03:26 PM
  5. tile map bounds checking
    By Natase in forum Game Programming
    Replies: 1
    Last Post: 10-08-2003, 11:00 AM