SFML community forums
Help => Graphics => Topic started by: Nybble on September 26, 2013, 05:17:37 am
-
I am so frustrated trying to figure out why every time I render a "tile map" it slows down game play so much, I used a simple nested for loop to draw tiles like so:
for(int y = 0; y < mapHeight; y++)
for(int x = 0; y < mapWidth; x++)
{
v.push_back(tile);
// same as v[y]
}
I thought this was the problem so I copied and pasted the code to for tile map from this LINK: http://www.sfml-dev.org/tutorials/2.0/graphics-vertex-array.php
and yet again same thing happens, I have a simple question:
is it supposed to slow down the overall game play the more you draw? it makes no sense to me because if I draw a map lets say 40x40 it slows down a little, then if I draw 80x80 it pretty much slows down twice as much and so on so forth...
Is it maybe a bug with my entity update and/or rendering logic? I don't think it is, is simple update/rendering logic.
If you guys want to see some code, I would happily post it, I don't think it is needed, this happens regardless of how I approach it...
-
I think we're going to have to see code. All you've really told us is "I did what X said and it doesn't work," which isn't much for us to go on.
In principle of course drawing more stuff is going to slow things down, but a 2x slowdown should be pretty hard to achieve without doing something very wrong. When you say 40x40 and 80x80, do you mean 1600 tiles and 6400 tiles? Because that might slow things down a lot, but I have a hard time imagining you really need to draw thousands of tiles on the screen at any one time.
-
You don't seem to understand the tutorial on vertex arrays. Copying and pasting random pieces of code together won't solve your problem. I don't know why you have a push_back inside your rendering loop... or why you need a nested loop to draw your tilemap every frame at all... You don't need to post more code. It is obvious from those few lines why your rendering is slow.
Everything is all explained pretty thoroughly on the vertex array tutorial page. Spend the time and read it and most importantly, understand what is said there. I could just copy and paste pieces of it in this post, but why bother when it is written there already...
-
Alright so lets just take the code from the "example tile map", lets just say I'm using this code.
const int level[] =
{
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
};
// create the tilemap from the level definition
TileMap map;
if (!map.load("tileset.png", sf::Vector2u(32, 32), level, 16, 8))
even using this small "map" slows gameplay a lot...
void Render(PencilTool& renderer)
{
Window.clear(sf::Color(0, 135, 206, 250));
for( size_t i = 0 ; i < objectList.size(); i++)
{
objectList->Render(renderer);
}
Window.display();
}
and the "PencilTool" is just this:
void Draw(sf::RenderWindow& window, sf::Sprite& object, const float& x, const float& y, const int16_t& x2, const int16_t& y2, const int16_t& w, const int16_t& h)
{
object.setPosition(x,y);
object.setTextureRect(sf::IntRect(x2,y2,w,h));
window.draw(object);
}
pretty generic stuff, that is all it does, and I obviously update object's position etc...
I never said "I did what X said and it doesn't work,"... clearly I said I used the code from here: http://www.sfml-dev.org/tutorials/2.0/graphics-vertex-array.php after failing badly on my own, but anyways, I appreciate your help.
@ binary1248 wth are you talking about? I said after I THOUGHT my method was the problem (clearly wasn't the issue) I tried the vertext tut code, no push_back or stl etc and it does the EXACT same thing so how is the push_back the problem when the problem exist even without it?
-
So... you used push_back in your original rendering code, it was slow, you replaced it with the code from the SFML tutorial and it was also slow, so you deduce that using push_back wouldn't cause your rendering to be slow as well? If it is really the case that your original code and the SFML code run without any difference in performance, then something is really wrong with your game loop. The point is that your original code is supposed to be slow unless you draw so little it doesn't make a difference.
I don't know how you measure performance but as Ixrec already said, the more you draw, the more your GPU has to work, so it will naturally be slower. The question however is, is your application GPU or CPU bound. If it is CPU bound then you need to look elsewhere in your application for the culprit. This is probably the case since drawing using the tutorial code doesn't seem to speed things up.
I'm curious, what is slow for you? You always say "slow" but never really define it in terms of FPS values. 1000 FPS might still be slow for some people, if e.g. they knew that they could run it at 3000 FPS if done right.
Did you try running portions of your code in a sandbox project? It helps to test the performance of individual subsections of your code, as opposed to replacing parts and coming to conclusions using deductive reasoning.
Oh and by the way, try not to pass primitive data types by const reference... it just looks wrong.
-
If all you do is paste/link the vertexarrays tutorial instead of showing us complete code we can test ourselves, all we can do is assume you're either failing to understand the tutorial or making some huge mistake elsewhere. And the former is far more likely. So show us your complete code (the version of it that supposedly matches the tutorial) and then we can try to help.
And like binary said, more testing and more precise numbers would help a lot. You still haven't given us much to work with.
-
Ok here it is:
class MyEntity
{
public:
MyEntity() : sprite(m_texture)
{
m_texture.loadFromFile("lucas.png");
sprite.setTextureRect(sf::IntRect(0,0,40,40));
sprite.setPosition(0, 0);
}
virtual void draw(sf::RenderWindow& target) const
{
target.draw(sprite);
}
sf::Sprite sprite;
sf::Texture m_texture;
};
class TileMap : public sf::Drawable, public sf::Transformable
{
public:
bool load(const std::string& tileset, sf::Vector2u tileSize, const int* tiles, unsigned int width, unsigned int height)
{
// load the tileset texture
if (!m_tileset.loadFromFile(tileset))
return false;
// resize the vertex array to fit the level size
// populate the vertex array, with one quad per tile
for (unsigned int i = 0; i < width; ++i)
for (unsigned int j = 0; j < height; ++j)
{
// get the current tile number
int tileNumber = tiles[i + j * width];
m_vertices.setPrimitiveType(sf::Quads);
m_vertices.resize(width * height * 4);
// find its position in the tileset texture
int tu = tileNumber % (m_tileset.getSize().x / tileSize.x);
int tv = tileNumber / (m_tileset.getSize().x / tileSize.x);
// get a pointer to the current tile's quad
sf::Vertex* quad = &m_vertices[(i + j * width) * 4];
// define its 4 corners
quad[0].position = sf::Vector2f(i * tileSize.x, j * tileSize.y);
quad[1].position = sf::Vector2f((i + 1) * tileSize.x, j * tileSize.y);
quad[2].position = sf::Vector2f((i + 1) * tileSize.x, (j + 1) * tileSize.y);
quad[3].position = sf::Vector2f(i * tileSize.x, (j + 1) * tileSize.y);
// define its 4 texture coordinates
quad[0].texCoords = sf::Vector2f(tu * tileSize.x, tv * tileSize.y);
quad[1].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tv * tileSize.y);
quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, (tv + 1) * tileSize.y);
quad[3].texCoords = sf::Vector2f(tu * tileSize.x, (tv + 1) * tileSize.y);
}
return true;
}
private:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
// apply the transform
states.transform *= getTransform();
// apply the tileset texture
states.texture = &m_tileset;
// draw the vertex array
target.draw(m_vertices, states);
}
sf::VertexArray m_vertices;
sf::Texture m_tileset;
};
and main:
int main()
{
MyEntity entity;
// create the window
sf::RenderWindow window(sf::VideoMode(800, 600), "Tilemap");
// define the level with an array of tile indices
const int level[] =
{
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
0, 1, 0, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
0, 1, 1, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
0, 0, 1, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
2, 0, 1, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
0, 0, 1, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
};
// create the tilemap from the level definition
TileMap map;
if (!map.load("tileset.png", sf::Vector2u(32, 32), level, 16, 8))
return -1;
window.setFramerateLimit(60);
float x = 0, y = 0;
// run the main loop
while (window.isOpen())
{
// handle events
sf::Event event;
while (window.pollEvent(event))
{
if(event.type == sf::Event::Closed)
window.close();
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
x+=1; //y+=1;
entity.sprite.setPosition(x,y);
}
// draw the map
window.clear();
window.draw(map);
entity.draw(window);
window.display();
}
return 0;
}
now if I change if (!map.load("tileset.png", sf::Vector2u(32, 32), level, 16, 8))
the 16 to say 116 it will draw more of the texture horizontally and slow down the "entity" as it moves compare to when it was 16, same thing if I add vertically etc...
also if I comment out the part that draws the "map" it speeds up the movement of the entity....
-
This isn't a performance problem. This is related to the fact that your game world updates are tied to the frame rate and not actual passed time. You need to use sf::Clock to make your application frame rate independent. There are many threads about this already:
https://www.google.com/search?q=SFML+timestep+site:en.sfml-dev.org
http://gafferongames.com/game-physics/fix-your-timestep/
-
Well I didn't show the code here but I do use a timestep... here is the code:
class Cfps
{
private:
int rate;
float startTime;
float lastTime;
int Fps;
int frames;
float speed;
sf::Clock fpsClock;
public:
Cfps();
Cfps(unsigned int _rate);
int getFps();
float getSpeed();
void update();
};
//==================================================================
/// Cfps
//==================================================================
Cfps::Cfps(unsigned int _rate)
{
rate = _rate;
startTime = 0;
lastTime = 0;
Fps = 0;
frames = 0;
speed = 0;
}
Cfps::Cfps()
{
rate = 60;
startTime = 0;
lastTime = 0;
Fps = 0;
frames = 0;
speed = 0;
}
//----------------------------------------------------------------
int Cfps::getFps() {return Fps;}
//==================================================================
float Cfps::getSpeed() {return speed;}
//==================================================================
void Cfps::update()
{
sf::Time ticks = fpsClock.getElapsedTime();
//one second has passed ( 1000 miliseconds )
if (startTime + 1000 < ticks.asMilliseconds())
{
startTime = ticks.asMilliseconds();
Fps = frames;
frames = 0;
}
speed = ( (ticks.asMilliseconds() - lastTime) / 1000 * rate );
lastTime = ticks.asMilliseconds();
frames++;
}
Cfps appFps(60);
then I do this in the main loop before update:
appFps.update();
same result.....
I think you are right though, what I need to do is multiply movement by delta time, I think...
-
So... what exactly is this Cfps class supposed to do? You update it every frame so that it computes new values but where do you use these values? Also I don't get why you can't just use .getElapsedTime() and/or .restart() instead of all those extra variables. You should also enable conversion warnings. You implicitly convert values all over the place which might not lead to what you would expect.
It doesn't have to be this complicated. Assuming your application will always run at 60 FPS is also something you shouldn't do. As soon as you get frames that take a bit longer every now and then, your updates will all break.