Hello everybody,
I've started working on my own platformer game with the help of SFML after getting slightly tired of SDL and I've ran into my first 'big' problem that I have had no success solving on my own so far. When I woke up this morning I decided I wanted to implement at least a proper Level class which loads and draws the map based on the level it's loaded from (which is a .txt file). The problem is that the FPS is now at max 8 FPS because I load all images, set the texture of the sprite to this image, set their position AND draw it every update call unlike I did previously (only drawing in the game loop). The issue, however, is that I want to not hardcode the amount in an array like I did in the Game class but store it in a vector properly but I have no idea how to properly do this.
The level (.txt) I am testing:
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
9 1 1 1 9 9 9 9 9 9 2 2 2 2 2 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 2 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 2 1 2 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 9
2 2 2 2 2 2 2 2 2 2 9 9 9 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 9 9 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 9 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 9 9 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
How the level looks:
The current code using to load the levels:
void Level::LoadMap(const char* filename)
{
std::ifstream openfile(filename);
std::string line;
std::vector<int> tempVector;
while (std::getline(openfile, line))
{
for (int i = 0; i < line.length(); i++)
{
if (line[i] != ' ')
{
char value[1] = { line[i] };
tempVector.push_back(atoi(value));
}
}
mapVector.push_back(tempVector);
tempVector.clear();
}
for (int i = 0; i < mapVector.size(); i++)
{
for (int j = 0; j < mapVector[i].size(); j++)
{
std::string randNumb = std::to_string(long double(urand(0, 1)));
switch (mapVector[i][j])
{
case 9:
fileArray[i][j] = "Graphics/Tiles/sky_3.png"; //! Empty sky block
break;
case 0:
fileArray[i][j] = "Graphics/Tiles/sky_" + std::to_string(long double(urand(0, 20) < 16 ? 3 : urand(0, 2))) + ".png";
break;
case 1:
fileArray[i][j] = "Graphics/Tiles/dirt_" + randNumb + ".png";
break;
case 2:
fileArray[i][j] = "Graphics/Tiles/dirt_rock_" + randNumb + ".png";
break;
case 3:
fileArray[i][j] = "Graphics/Tiles/grass_" + randNumb + ".png";
break;
case 4:
fileArray[i][j] = "Graphics/Tiles/grass_ontop_" + randNumb + ".png";
break;
case 5:
fileArray[i][j] = "Graphics/Tiles/ground_" + randNumb + ".png";
break;
case 6:
fileArray[i][j] = "Graphics/Tiles/sand_" + randNumb + ".png";
break;
default:
return;
}
}
}
}
Code to draw the map (called right after window.clear() in Game class' loop:
void Level::DrawMap(sf::RenderWindow &window)
{
sf::Sprite sprite;
sf::Texture image;
Player* player = game->GetPlayer();
for (int i = 0; i < mapVector.size(); i++)
{
for (int j = 0; j < mapVector[i].size(); j++)
{
//! ONLY draw the images if the player is within visibility distance, else there's no point in wasting performance.
if (IsInRange(player->GetPositionX(), j * 50.0f, player->GetPositionY(), i * 50.0f, 600.0f))
{
// Here is the problem
image.loadFromFile(fileArray[i][j]);
sprite.setTexture(image);
sprite.setPosition(j * 50.0f, i * 50.0f);
window.draw(sprite);
}
}
}
}
So that's it for the current way of initializing and drawing the tiles. The previous way - which was NOT having ANY performance issues at ALL - is as following:
int Game::Update()
{
...
sf::Texture imageDirt[2];
sf::Texture imageGrass[2];
sf::Texture imageGround[2];
sf::Texture imageSky[4];
sf::Sprite spriteDirt[15][60];
sf::Sprite spriteGrass[12][60];
sf::Sprite spriteGround[12][60];
sf::Sprite spriteSky[12][60];
for (int i = 0; i < 4; ++i)
{
std::string numberInStr = std::to_string(long double(i));
if (i < 2)
{
imageDirt[i].loadFromFile("Graphics/Tiles/dirt_" + numberInStr + ".png");
imageGrass[i].loadFromFile("Graphics/Tiles/grass_" + numberInStr + ".png");
imageGround[i].loadFromFile("Graphics/Tiles/ground_" + numberInStr + ".png");
}
imageSky[i].loadFromFile("Graphics/Tiles/sky_" + numberInStr + ".png");
}
for (int i = 7; i < 9; ++i)
for (int j = 0; j < 60; ++j)
spriteGrass[i][j].setTexture(imageGrass[urand(0, 1)]);
for (int i = 9; i < 12; ++i)
for (int j = 0; j < 60; ++j)
spriteGround[i][j].setTexture(imageGround[urand(0, 1)]);
for (int i = 12; i < 15; ++i)
for (int j = 0; j < 60; ++j)
spriteDirt[i][j].setTexture(imageDirt[urand(0, 1)]);
for (int i = 0; i < 12; ++i)
for (int j = 0; j < 60; ++j)
spriteSky[i][j].setTexture(imageSky[(i > 7 || urand(0, 20) < 16) ? 3 : urand(0, 2)]);
float boxX = 0.0f, boxY = 0.0f;
//! Filling up skybox
for (int i = 0; i < 12; ++i)
{
for (int j = 0; j < 60; ++j)
{
spriteSky[i][j].setPosition(boxX, boxY);
boxX += 50.0f;
}
boxX = 0.0f;
boxY += 50.0f;
}
//! Filling up grass
boxX = 0.0f;
boxY = 250.0f;
for (int i = 7; i < 9; ++i)
{
for (int j = 0; j < 60; ++j)
{
spriteGrass[i][j].setPosition(boxX, boxY);
boxX += 50.0f;
}
boxX = 0.0f;
boxY += 50.0f;
}
... etc.
//! Sky blocks are not collidable.
for (int i = 0; i < 12; ++i)
for (int j = 0; j < 60; ++j)
gameObjects.push_back(spriteSky[i][j]);
for (int i = 9; i < 12; ++i)
{
for (int j = 0; j < 60; ++j)
{
gameObjects.push_back(spriteGround[i][j]);
gameObjectsCollidable.push_back(spriteGround[i][j]);
}
}
...etc
...
while (window.isOpen())
{
...
window.clear();
for (int i = 0; i < 12; ++i)
for (int j = 0; j < 60; ++j)
window.draw(spriteSky[i][j]);
for (int i = 9; i < 12; ++i)
for (int j = 0; j < 60; ++j)
window.draw(spriteGround[i][j]);
for (int i = 7; i < 9; ++i)
for (int j = 0; j < 60; ++j)
window.draw(spriteGrass[i][j]);
for (int i = 12; i < 15; ++i)
for (int j = 0; j < 60; ++j)
window.draw(spriteDirt[i][j]);
...
window.setView(view);
float fps = 1 / fpsClock.getElapsedTime().asSeconds();
sf::Text text("FPS: " + std::to_string(long double(int(fps))), font, 15);
text.setColor(sf::Color::Black);
text.setPosition(436 + player->GetPositionX(), 15.0f);
window.draw(text);
window.display();
}
return 0;
}
I hope you guys are able to help me out here. If you wish to see the entire code, it's all on GitHub here:
https://github.com/Discover-/Platformer-SFML/commits/.
Thanks in advance!
Greetings,
Discover- / Jasper
P.S. It's a shame there's no spoiler tags, they'd be really useful in cases like this post!