Jump to content

  • Log In with Google      Sign In   
  • Create Account


#ActualServant of the Lord

Posted 23 June 2013 - 12:46 PM

Here's some untested code I just wrote, illustrating how it might be done.
#include <fstream>
#include <sstream>
#include <algorithm> //For std::replace().

//Loads the entire file, newlines and all, and returns its contents as a string.
//Returns an empty string if the file doesn't exist.
std::string LoadFileAsString(const std::string &filepath)
{
	//Open the file.
	std::ifstream file(filepath.c_str());
	
	//If it failed to open, report the error in whatever way your program handles errors.
	if(!file)
	{
		std::cerr << "LoadFileAsString(): Failed to load '" << filepath << "'\n" << std::endl;
		return std::string();
	}
	
	//Read the entire filestream into the stringstream.
	std::ostringstream stringStream;
	stringStream << file.rdbuf();

	//Get the string from the stringstream.
	return stringStream.str();
}

//If you have C++11 enabled on your compiler, you can use std::stoi() instead of using this function.
int StringToInt(const std::string &str)
{
	int value;
	std::stringstream stringStream(str);
	stringStream >> value;

	return value;
}

void TileMap::SetTile(unsigned tileX, unsigned tileY, const sf::Image &tileset, unsigned tilesetLocX, unsigned tilesetLocY)
{
	if(tileX < m_MapWidth) return;
	if(tileY < m_MapHeight) return;
	
	//Get the subrect for the texture.
	sf::Texture texture;
	texture.loadFromImage(tileset, sf::IntRect((tileNumX * m_TILESIZE_X), (tileNumY * m_TILESIZE_Y),
											   m_TILESIZE_X, m_TILESIZE_Y));
	
	//Assign the texture to the tile.
	unsigned tileIndex = (tileY * m_MapHeight) + tileX;
	m_Map[tileIndex].setTexture(texture); 
}

bool TileMap::LoadTileMap(const std::string &filepath, const sf::Image &tileset)
{
	//Load the map.
	std::string fileBuffer = LoadFileAsString(filepath);
	if(fileBuffer.empty())
	{
		std::cerr << "TileMap::LoadTileMap(): Failed to load the map file. Filepath: '" << filepath << "'\n" << std::endl;
		return false;
	}
	
	//Make sure our tile array is the right size.
	m_Map.resize(m_MapWidth * m_MapHeight);
	
	//Push back an extra space for parsing convience, because we are using spaces to mark the end of each pair.
	fileBuffer.push_back(' ');
	
	//The left and right sides of pairs.
	std::string leftSide, rightSide;
	
	//Which tile we're currently on.
	unsigned tileIndex = 0;
	
	//Iterate over the entire string.
	bool reachedDivider = true;
	bool alreadyReachedComma = false;
	for(size_t i = 0; i < fileBuffer.end(); i++)
	{
		//std::isspace() detects spaces, tabs, newlines, etc... These type of characters are collectively known as 'whitespace'.
		if(std::isspace(fileBuffer[i]))
		{
			//If this is the first whitespace character we've reached since the last pair, then we process the pair.
			if(!reachedDivider)
			{
				//Convert the strings to integers. You can't just cast a char to an int - that only gives you the index of the char in the ASCII chart.
				unsigned tilesetLocX = StringToInt(leftSide);
				unsigned tilesetLocY = StringToInt(rightSide);
						
				//Convert our tile index to a 2D position. See this link for how to convert from 1D to 2D indices:
				//http://www.gamedev.net/topic/627686-2d-tileset-index-to-sub-rectangular-position-calculation/
				unsigned tileX = (tileIndex % m_MapWidth);
				unsigned tileY = (tileIndex / m_MapWidth);
				
				//Set the tile in the map, with the tileset.
				this->SetTile(tileX, tileY, tileset, tilesetLocX, tilesetLocY);
				
				//Move to the next tile.
				++tileIndex;
				
				//Reset the string buffers, so we can reuse them for the next pair.
				leftSide.clear();
				rightSide.clear();
			}
			
			//Because we just set 'beginNewPair' to true, even if it was already true, and then
			//just continue, we implicitely handle multiple spaces or newlines in a row.
			reachedDivider = true;
			alreadyReachedComma = false;
		}
		//This is the divider between the left side of the pair, and the right side.
		else if(fileBuffer[i] == ',')
		{
			alreadyReachedComma = true;
		}
		else if(std::isdigit(fileBuffer[i]))
		{
			//This is the left right side of the pair (the tile row in the tileset).
			if(alreadyReachedComma)
			{
				leftSide.push_back(fileBuffer[i]);
			}
			//This is the right right side of the pair (the tile column in the tileset).
			else
			{
				rightSide.push_back(fileBuffer[i]);
			}
			
			//Reset the divider marker, so we're prepared to hit the next divider.
			reachedDivider = false;
		}
		else
		{
			//Do nothing. Ignores invalid characters.
                        //The compiler will optimize out this empty 'else {}', it's only here for documentation.
		}
	}
}
Since I didn't bother compiling it, it might have a mistake or two of its own. If it does, I'll correct it.

#1Servant of the Lord

Posted 23 June 2013 - 12:45 PM

Here's some untested code I just wrote, illustrating how it might be done.

#include <fstream>
#include <sstream>
#include <algorithm> //For std::replace().

//Loads the entire file, newlines and all, and returns its contents as a string.
//Returns an empty string if the file doesn't exist.
std::string LoadFileAsString(const std::string &filepath)
{
	//Open the file.
	std::ifstream file(filepath.c_str());
	
	//If it failed to open, report the error in whatever way your program handles errors.
	if(!file)
	{
		std::cerr << "LoadFileAsString(): Failed to load '" << filepath << "'\n" << std::endl;
		return std::string();
	}
	
	//Read the entire filestream into the stringstream.
	std::ostringstream stringStream;
	stringStream << file.rdbuf();

	//Get the string from the stringstream.
	return stringStream.str();
}

//If you have C++11 enabled on your compiler, you can use std::stoi() instead of using this function.
int StringToInt(const std::string &str)
{
	int value;
	std::stringstream stringStream(str);
	stringStream >> value;

	return value;
}

void TileMap::SetTile(unsigned tileX, unsigned tileY, const sf::Image &tileset, unsigned tilesetLocX, unsigned tilesetLocY)
{
	if(tileX < m_MapWidth) return;
	if(tileY < m_MapHeight) return;
	
	//Get the subrect for the texture.
	sf::Texture texture;
	texture.loadFromImage(tileset, sf::IntRect((tileNumX * m_TILESIZE_X), (tileNumY * m_TILESIZE_Y),
											   m_TILESIZE_X, m_TILESIZE_Y));
	
	//Assign the texture to the tile.
	unsigned tileIndex = (tileY * m_MapHeight) + tileX;
	m_Map[tileIndex].setTexture(texture); 
}

bool TileMap::LoadTileMap(const std::string &filepath, const sf::Image &tileset)
{
	//Load the map.
	std::string fileBuffer = LoadFileAsString(filepath);
	if(fileBuffer.empty())
	{
		std::cerr << "TileMap::LoadTileMap(): Failed to load the map file. Filepath: '" << filepath << "'\n" << std::endl;
		return false;
	}
	
	//Make sure our tile array is the right size.
	m_Map.resize(m_MapWidth * m_MapHeight);
	
	//Push back an extra space for parsing convience, because we are using spaces to mark the end of each pair.
	fileBuffer.push_back(' ');
	
	//The left and right sides of pairs.
	std::string leftSide, rightSide;
	
	//Which tile we're currently on.
	unsigned tileIndex = 0;
	
	//Iterate over the entire string.
	bool reachedDivider = true;
	bool alreadyReachedComma = false;
	for(size_t i = 0; i < fileBuffer.end(); i++)
	{
		//std::isspace() detects spaces, tabs, newlines, etc... These type of characters are collectively known as 'whitespace'.
		if(std::isspace(fileBuffer[i]))
		{
			//If this is the first whitespace character we've reached since the last pair, then we process the pair.
			if(!reachedDivider)
			{
				//Convert the strings to integers. You can't just cast a char to an int - that only gives you the index of the char in the ASCII chart.
				unsigned tilesetLocX = StringToInt(leftSide);
				unsigned tilesetLocY = StringToInt(rightSide);
						
				//Convert our tile index to a 2D position. See this link for how to convert from 1D to 2D indices:
				//http://www.gamedev.net/topic/627686-2d-tileset-index-to-sub-rectangular-position-calculation/
				unsigned tileX = (tileIndex % m_MapWidth);
				unsigned tileY = (tileIndex / m_MapWidth);
				
				//Set the tile in the map, with the tileset.
				this->SetTile(tileX, tileY, tileset, tilesetLocX, tilesetLocY);
				
				//Move to the next tile.
				++tileIndex;
				
				//Reset the string buffers, so we can reuse them for the next pair.
				leftSide.clear();
				rightSide.clear();
			}
			
			//Because we just set 'beginNewPair' to true, even if it was already true, and then
			//just continue, we implicitely handle multiple spaces or newlines in a row.
			reachedDivider = true;
			alreadyReachedComma = false;
		}
		//This is the divider between the left side of the pair, and the right side.
		else if(fileBuffer[i] == ',')
		{
			alreadyReachedComma = true;
		}
		else if(std::isdigit(fileBuffer[i]))
		{
			//This is the left right side of the pair (the tile row in the tileset).
			if(alreadyReachedComma)
			{
				leftSide.push_back(fileBuffer[i]);
			}
			//This is the right right side of the pair (the tile column in the tileset).
			else
			{
				rightSide.push_back(fileBuffer[i]);
			}
			
			//Reset the divider marker, so we're prepared to hit the next divider.
			reachedDivider = false;
		}
		else
		{
			//Do nothing. Ignores invalid characters.
                        //The compiler will optimize out this empty 'else {}', it's only here for documentation.
		}
	}
}

PARTNERS