Hi all.
I'm working on roguelike game, and so my first idea of how to render the map was like this:
void fill(sf::RenderWindow &window, const std::string &map) {
sf::Text text;
text.setFont(font_); // font_ is loaded on app start
text.setCharacterSize(20);
sf::FloatRect backgroundRect;
sf::RectangleShape background;
background.setFillColor(sf::Color(0x00, 0x00, 0x00)); // Always black for simplicity
sf::Color wallColor(0x9E, 0x86, 0x64);
sf::Color floorColor(0x33, 0x33, 0x33);
for (int y = 0; y < 60; ++y) {
for (int x = 0; x < 80; ++x) {
const char ch = data[y * 80 + x];
if (ch == '#') {
text.setColor(wallColor);
} else {
text.setColor(floorColor);
}
text.setString(ch);
text.setPosition(x * 20, y * 20);
backgroundRect = text.getLocalBounds();
background.setPosition(sf::Vector2f(backgroundRect.width, backgroundRect.height));
window.draw(background, text.getTransform());
window.draw(text);
}
}
}
int main() {
sf::RenderWindow window;
window.create(sf::VideoMode(1920, 1200), "test",
sf::Style::Close | sf::Style::Titlebar), settings);
window.setFramerateLimit(30);
const char map[] = "80 cols x 60 lines of chars"; // 80x60 here, basically "# ...... #"
while(window.isOpen()) {
window.clear();
fill(window, map);
window.display();
}
}
Well, it's a little bit more complex but idea is shown. If i comment-out map fill then my app takes about 1% CPU, but if not - it takes 100% CPU all the time. So I guess fill function is not efficient enough. Is there a way to speed up things?
The map is just a matrix (an array if you want) of chars. Different chars (as they represents different entities, like walls, doors, etc) have different attributes (background/foreground color).
Thanks in advance.
Thanks for your reply. Unfortunately that doesn't work for me. I create two tiles (for floor and wall):
struct tile {
sf::Text text;
sf::RectangleShape background;
};
int main() {
// Wall
std::shared_ptr<tile> wall(new tile);
wall->text.setFont(font);
wall->text.setCharacterSize(20);
wall->text.setString('#');
wall->text.setColor(wallColor);
sf::FloatRect backgroundRect = wall->text.getLocalBounds();
wall->background.setFillColor(sf::Color(0x00, 0x00, 0x00));
wall->background.setPosition(sf::Vector2f(backgroundRect.width, backgroundRect.height));
// Floor
std::shared_ptr<tile> floor(new tile);
floor->text.setFont(font);
floor->text.setCharacterSize(20);
floor->text.setString('.');
floor->text.setColor(floorColor);
backgroundRect = floor->text.getLocalBounds();
floor->background.setFillColor(sf::Color(0x00, 0x00, 0x00));
floor->background.setPosition(sf::Vector2f(backgroundRect.width, backgroundRect.height));
}
and then fill std::vector with these shared_ptr's, so I have a vector with 80x60 (4800) pointers. Now in a main loop I do:
window.clear();
for (int y = 0; y < 60; ++y) {
for (int x = 0; x < 80; ++x) {
const std::shared_ptr<tile> t = data[y * 80 + x];
t->text.setPosition(x * 20, y * 20);
draw(t->background, t->text.getTransform());
draw(t->text);
}
}
window.display();
and CPU load is still near 100% (90% actually).
P.S. I'm studying libtcod trying to implement some parts of this library in SFML (not a port, just taking the ideas from this library). It uses SDL1.2/SDL2 as a rendering library. I found an example of drawing text maps (with true colors background/foreground) and it's rocket fast. I can't believe I can't do similar things using SFML.
Thanks for your reply. Unfortunately that doesn't work for me. I create two tiles (for floor and wall):
struct tile {
sf::Text text;
sf::RectangleShape background;
};
int main() {
// Wall
std::shared_ptr<tile> wall(new tile);
wall->text.setFont(font);
wall->text.setCharacterSize(20);
wall->text.setString('#');
wall->text.setColor(wallColor);
sf::FloatRect backgroundRect = wall->text.getLocalBounds();
wall->background.setFillColor(sf::Color(0x00, 0x00, 0x00));
wall->background.setPosition(sf::Vector2f(backgroundRect.width, backgroundRect.height));
// Floor
std::shared_ptr<tile> floor(new tile);
floor->text.setFont(font);
floor->text.setCharacterSize(20);
floor->text.setString('.');
floor->text.setColor(floorColor);
backgroundRect = floor->text.getLocalBounds();
floor->background.setFillColor(sf::Color(0x00, 0x00, 0x00));
floor->background.setPosition(sf::Vector2f(backgroundRect.width, backgroundRect.height));
}
and then fill std::vector with these shared_ptr's, so I have a vector with 80x60 (4800) pointers. Now in a main loop I do:
window.clear();
for (int y = 0; y < 60; ++y) {
for (int x = 0; x < 80; ++x) {
const std::shared_ptr<tile> t = data[y * 80 + x];
t->text.setPosition(x * 20, y * 20);
draw(t->background, t->text.getTransform());
draw(t->text);
}
}
window.display();
and CPU load is still near 100% (90% actually).
Then you can scan your vector for a single color sequences, and then draw all sequence by two calls - first for background and the second for text. It should improve performance.
Because with current code you're calling window.draw at least 4800*2=9600 times per frame. It's very much.
Update: I didn't notice right away that you are using separate instances for each letter. It's overhead and will consume a lot of memory. Just create single text and single rectangle and use it to render any tile cell.
Also, you can improve performance by remove GetLocalBounds call. This call consumes valuable time. By design you are using fixed size cells, so you can calculate position with no need for this call. I'm doing it where possible and it gives a little better performance