Jump to content
  • Advertisement
Sign in to follow this  
auliszek

Loading tile map into array

This topic is 2389 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,
So I got this method I created in order to load numbers from a file, and then put them into the array. File is built like this: 1st line is length (in number od tiles), 2nd line's width (also in number of tiles) and the 3rd line is a size od a square tile in pixels. Next lines are numbers divided with ":" symbol. The function itself looks like this:
bool C_map::load(string name)
{
if(!al_filename_exists((name+"/"+"teamplate.bmp").c_str())) //sprawdzenie czy plik graficzny z kafelkami istnieje
return false;
teamplate = al_load_bitmap((name+"/"+"teamplate.bmp").c_str()); //za?adowanie pliku graficznego do bitmapy

ifstream map_file(name+"/"+"map.mp");
if(!map_file) //sprawdzenie czy plik dzia?a i jest poprawnie otwierany
return false;
unsigned l = 1;
string line;

unsigned options[3];
while(l <= 3)
{
getline(map_file, line);
//wczytywanie opcji wst?pnych z pliku *.mp do 3. linijki
options[l - 1] = atoi(line.c_str());
l++;
}
tileamount_x = options[0]; //zapisywanie do pól klasy
tileamount_y = options[1]; //informacji o
tile_size = options[2]; //rozmiarach mapy
if((tileamount_x == NULL) || (tileamount_y == NULL) || (tile_size == NULL))
return false;

tile_map = new unsigned*[tileamount_y]; //dynamiczna alokacja
for(unsigned i = 0; i <= tileamount_y; i++) //pami?ci dla tablicy
{ //przechowuj?cej map? kafelkow?
tile_map = new unsigned[tileamount_x]; //
} //

char* fragment; //pomocnicza zmienna do przechowywawnia fragmentów linii
char* line_; //odpowiednik string line
unsigned short a = 0;
unsigned short b = 0;

while(!map_file.eof() && l<=(tileamount_y+3))
{
getline(map_file, line);
line_ = (char*)line.c_str();
fragment = strtok(line_, ":"); //pobranie pierwszej komórki mapy
while((fragment != NULL) && (b <= tileamount_x - 1))
{
tile_map[a] = atoi(fragment); //przypisanie komórek mapy komorkom tablicy, poczawszy od 0,0
fragment = strtok(NULL, ":"); //pobieranie kolejnych komórek
b++;
}
a++;
l++;
}
//koniec wczytywania mapy kafelkow
return true;
}


The problem is, this doesn't exactly work as I supposed it will. In fact it does something absolutely unpredictable for me. It fills only the first row od tile_map array. So tile_map[0][x] fields are filled (and they are dilled exactly the way they were supposed to), but the rest of those fields are untouched. What can be the problem? I tried to change this stuff many times, so it is the final versoin, but still I cannot find any solution at all.
One more thing, declaration inside the class looks like this:
static unsigned **tile_map;

Share this post


Link to post
Share on other sites
Advertisement
You're not resetting "b" in your outer loop. That code is quite dangerous, it appears that you're happily writing over arbitrary memory using this. You also stepping out of bounds when you allocate the last tile_map, as you loop from i = 0 to tileamount_y, this range actually contains tileamount_y + 1 elements.

Generally, when one has an array of size N, the following loop visits each element:

for(int i = 0 ; i < N ; ++i)
{
// Use array
}

Note that the condition is less than, not less or equal.

Let's look at a safer way of implementing this:

#include <fstream>
#include <string>
#include <sstream>
#include <iostream>
#include <vector>

// Mock implementation of al_* library

bool al_filename_exists(const std::string &)
{
return true;
}

struct al_bitmap
{
// Whatever data
};

al_bitmap *al_load_bitmap(const std::string &)
{
// TODO: cleanup
return new al_bitmap;
}

struct MapHeader
{
int width;
int height;
int tileSize;
};

typedef std::vector<std::vector<int> > TileData;

struct Map
{
MapHeader header;
al_bitmap *bitmap;
TileData tiles;
};

bool readInt(std::istream &in, int &value)
{
std::string line;
if(std::getline(in, line))
{
std::stringstream stream(line);
if(stream >> value && stream >> std::ws && stream.eof())
{
return true;
}
}
return false;
}

bool handleHeader(std::istream &in, MapHeader &header)
{
if(!readInt(in, header.width))
{
std::cerr << "Failed to read map header: width\n";
return false;
}

if(!readInt(in, header.height))
{
std::cerr << "Failed to read map header: height\n";
return false;
}

if(!readInt(in, header.tileSize))
{
std::cerr << "Failed to read map header: tile size\n";
return false;
}

return true;
}

bool handleTileData(std::istream &in, const MapHeader &header, TileData &tiles)
{
const char Delimiter = ':';

tiles.reserve(header.height);

std::string line;
int lineNumber = 0;
while(std::getline(in, line))
{
++lineNumber;
std::stringstream stream(line);
std::vector<int> row;

int tile;
char c;
while(stream >> tile >> std::ws >> c)
{
if(c != Delimiter)
{
std::cerr << "Map format error on line " << lineNumber << ": was \'" << c << "\'" << " expected " << Delimiter << '\n';
return false;
}

row.push_back(tile);
}

if(row.size() != header.width)
{
std::cerr << "Map width mismatch on line " << lineNumber << ": was " << row.size() << " expected " << header.width << '\n';
return false;
}

tiles.push_back(row);
}

if(tiles.size() != header.height)
{
std::cerr << "Map height mismatch: was " << tiles.size() << " expected " << header.height << '\n';
return false;
}

return true;
}

bool load(const std::string &directory, Map &map)
{
std::string imageFile = directory + "/teamplate.bmp";

if(!al_filename_exists(imageFile.c_str()))
{
std::cerr << "Failed to load map: bitmap " << imageFile << " doesn't exist\n";
return false;
}

map.bitmap = al_load_bitmap(imageFile.c_str());
if(map.bitmap == NULL)
{
std::cerr << "Failed to load map: load bitmap " << imageFile << " failed\n";
return false;
}

std::string mapFile = directory + "/map.mp";
std::ifstream in(mapFile.c_str());
if(!in)
{
std::cerr << "Map file: " << mapFile << "doesn't exist\n";
return false;
}

if(!handleHeader(in, map.header))
{
return false;
}

return handleTileData(in, map.header, map.tiles);
}

int main()
{
Map map;
if(load("C:/temp", map))
{
std::cout << "Success!\n";
std::cout << "Map width: " << map.header.width << '\n';
std::cout << "Map height: " << map.header.height << '\n';
std::cout << "Tile size: " << map.header.tileSize << '\n';
for(int y = 0 ; y < map.header.height ; ++y)
{
for(int x = 0 ; x < map.header.width ; ++x)
{
std::cout << map.tiles[y][x] << ' ';
}
std::cout << '\n';
}
}
else
{
std::cerr << "Failure...\n";
}
}

This isn't perfect, but at least it won't try to overwrite memory. Note I've throw some nice error reporting in. I've been rather basic and just output it to the console, but you could actually build a more complex result structure than a boolean and record the errors in there, which would allow you to report them in a GUI environment too.

But at the very least log such errors to a file somewhere. Such logs will be invaluable that day your game crashes on someone else's machine.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!