1

I've picked up SFML 2.3.2 on Linux Mint 18.1 using C++17 to learn working with GUIs. As a second project after Hello World, I'm trying to reproduce Snake, that game old phones had pre-installed. Most stuff worked until now, with a few minor exceptions that I will have to deal with later, some caused by the game not being fully finished yet.

Just to be sure that I understand the matter correctly, as I've never worked with GUIs and Images in a lower-level language before. First you load your image into the texture, then you add the texture to a sprite, and then you draw that sprite onto the window?

The program compiles flawlessly, initializes everything correctly, and runs with no major unexplained issues. Except that the images don't show. The sprites are there and shown in their default mono-chrome background color, but don't show any images.

What do I do wrong, and how do I fix it? Thanks!

// HEADERS
#include <iostream>
#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/System/String.hpp>
#include <SFML/Window/Keyboard.hpp>

// CREATE NEW FONT
sf::Font create_font()
{
    sf::Font f;
    bool id = f.loadFromFile("/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf");

    if (id == false)
    {
        std::cerr << "Font:\tCould not be loaded." << std::endl;
    }
    else
    {
        std::cerr << "Font:\tLoaded successfully." << std::endl;
    }

    return f;

}

// CREATE NEW SPRITE USING TEXTURE, GIVEN ARE PATH WITH COORDINATES AND DIMENSIONS
sf::Sprite load_img(std::string path, long x, long y, long w, long h)
{
    sf::Texture t;
    bool id = t.loadFromFile(path);

    if (id == false)
    {
        std::cerr << "Texture:\t" << path << "\tFailed to load." << std::endl;
    }
    else
    {
        std::cerr << "Texture:\t" << path << "\tLoaded successfully." << std::endl;
    }

    sf::Sprite s(t);
    s.setTextureRect(sf::IntRect((int)x, (int)y, (int)w, (int)h));
    s.setPosition(x, y);
    return s;
}


// MAIN FUNCTION
int main()
{
    // DECLARING/DEFINING VARIABLES
    unsigned long window_width = 512;
    unsigned long window_height = 512;
    unsigned long score = 0;
    unsigned long head_old_position_x, head_old_position_y;
    std::string title = "Snek";
    std::string wdir = "/home/kate/Documents/coding/snek/";

    // WINDOW
    sf::RenderWindow window(sf::VideoMode(window_width, window_height), title);
    window.setFramerateLimit(60);

    // SPRITES
    sf::Sprite background = load_img(wdir + "img/background.png", 0, 0, 512, 512);
    sf::Sprite head = load_img(wdir + "img/head.png", 47, 39, 8, 8);
    sf::Sprite body = load_img(wdir + "img/body.png", 39, 39, 8, 8);
    sf::Sprite poison = load_img(wdir + "img/poison.png", 119, 119, 8, 8);
    sf::Sprite trap = load_img(wdir + "img/trap.png", 159, 159, 8, 8);
    sf::Sprite candy = load_img(wdir + "img/candy.png", 199, 199, 8, 8);

    // FONT
    sf::Font font = create_font();

    // TEXT
    sf::Text score_display(title, font, 20);
    sc// HEADERS
#include <iostream>
#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/System/String.hpp>
#include <SFML/Window/Keyboard.hpp>

// CREATE NEW FONT
sf::Font create_font()
{
    sf::Font f;
    bool id = f.loadFromFile("/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf");

    if (id == false)
    {
        std::cerr << "Font:\tCould not be loaded." << std::endl;
    }
    else
    {
        std::cerr << "Font:\tLoaded successfully." << std::endl;
    }

    return f;

}

// CREATE NEW SPRITE USING TEXTURE, GIVEN ARE PATH WITH COORDINATES AND DIMENSIONS
sf::Sprite load_img(std::string path, long x, long y, long w, long h)
{
    sf::Texture t;
    bool id = t.loadFromFile(path);

    if (id == false)
    {
        std::cerr << "Texture:\t" << path << "\tFailed to load." << std::endl;
    }
    else
    {
        std::cerr << "Texture:\t" << path << "\tLoaded successfully." << std::endl;
    }

    sf::Sprite s(t);
    s.setTextureRect(sf::IntRect((int)x, (int)y, (int)w, (int)h));
    s.setPosition(x, y);
    return s;
}


// MAIN FUNCTION
int main()
{
    // DECLARING/DEFINING VARIABLES
    unsigned long window_width = 512;
    unsigned long window_height = 512;
    unsigned long score = 0;
    unsigned long head_old_position_x, head_old_position_y;
    std::string title = "Snek";
    std::string wdir = "/home/kate/Documents/coding/snek/";

    // WINDOW
    sf::RenderWindow window(sf::VideoMode(window_width, window_height), title);
    window.setFramerateLimit(60);

    // SPRITES
    sf::Sprite background = load_img(wdir + "/img/background.png", 0, 0, 512, 512);
    sf::Sprite head = load_img(wdir + "/img/head.png", 47, 39, 8, 8);
    sf::Sprite body = load_img(wdir + "/img/body.png", 39, 39, 8, 8);
    sf::Sprite poison = load_img(wdir + "/img/poison.png", 119, 119, 8, 8);
    sf::Sprite trap = load_img(wdir + "/img/trap.png", 159, 159, 8, 8);
    sf::Sprite candy = load_img(wdir + "/img/candy.png", 199, 199, 8, 8);

    // FONT
    sf::Font font = create_font();

    // TEXT
    sf::Text score_display(title, font, 20);
    score_display.setString(std::to_string(score));
    score_display.setPosition(5, 5);

    // LOOP
    while (window.isOpen())
    {
        sf::Event event;
        while ( window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            { window.close(); }

            head_old_position_x = head.getPosition().x;
            head_old_position_y = head.getPosition().y;

            // MOVEMENT BASED ON KEYBOARD INPUT
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
            {
                head.setPosition(head.getPosition().x - 8, head.getPosition().y);
                body.setPosition(head_old_position_x, head_old_position_y);
            }

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
            {
                head.setPosition(head.getPosition().x, head.getPosition().y - 8);
                body.setPosition(head_old_position_x, head_old_position_y);
            }

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            {
                head.setPosition(head.getPosition().x + 8, head.getPosition().y);
                body.setPosition(head_old_position_x, head_old_position_y);
            }

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            {
                head.setPosition(head.getPosition().x, head.getPosition().y + 8);
                body.setPosition(head_old_position_x, head_old_position_y);
            }


            if (body.getPosition().x == candy.getPosition().x && body.getPosition().y == candy.getPosition().y)
            {
                score ++;
                score_display.setString(std::to_string(score));
            }


            // REFRESH WINDOW
            window.clear();
            window.draw(background);
            window.draw(head);
            window.draw(body);
            window.draw(poison);
            window.draw(trap);
            window.draw(candy);
            window.draw(score_display);
            window.display();
        }
    }

    return 0;
}
ore_display.setString(std::to_string(score));
    score_display.setPosition(5, 5);

    // LOOP
    while (window.isOpen())
    {
        sf::Event event;
        while ( window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            { window.close(); }

            head_old_position_x = head.getPosition().x;
            head_old_position_y = head.getPosition().y;

            // MOVEMENT BASED ON KEYBOARD INPUT
            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
            {
                head.setPosition(head.getPosition().x - 8, head.getPosition().y);
                body.setPosition(head_old_position_x, head_old_position_y);
            }

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
            {
                head.setPosition(head.getPosition().x, head.getPosition().y - 8);
                body.setPosition(head_old_position_x, head_old_position_y);
            }

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
            {
                head.setPosition(head.getPosition().x + 8, head.getPosition().y);
                body.setPosition(head_old_position_x, head_old_position_y);
            }

            if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
            {
                head.setPosition(head.getPosition().x, head.getPosition().y + 8);
                body.setPosition(head_old_position_x, head_old_position_y);
            }


            if (body.getPosition().x == candy.getPosition().x && body.getPosition().y == candy.getPosition().y)
            {
                score ++;
                score_display.setString(std::to_string(score));
            }


            // REFRESH WINDOW
            window.clear();
            window.draw(background);
            window.draw(head);
            window.draw(body);
            window.draw(poison);
            window.draw(trap);
            window.draw(candy);
            window.draw(score_display);
            window.display();
        }
    }

    return 0;
}
Katembers
  • 103
  • 1
  • 1
  • 8

3 Answers3

0

Your texture goes out of scope, see When is an object "out of scope"? for more details. You declare the texture inside the load_img function, but sf::Texture t; is deleted after you exit load_img and return the sprite.

So here is an updated function in which you store the texture outside the load_img function and pass it by reference. Only relevant parts shown. First of all pass the texture to the load_img function. Delete the sf::Texture t;.

sf::Sprite load_img(std::string path, long x, long y, long w, long h, sf::Texture& t)
{
    bool id = t.loadFromFile(path);
    [...]
}

Declare a texture and pass it to the load_img function.

sf::Texture headtexture;
sf::Sprite head = load_img(wdir + "img/head.png", 47, 39, 8, 8, headtexture);

If you do this for all your sprites, it works and shows the images/sprites correctly.

Edit 25-Sep-2017

Based on the comments, here is a minimal working example without functions:

// HEADERS
#include <iostream>
#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Color.hpp>
#include <SFML/Graphics/Font.hpp>
#include <SFML/Graphics/Image.hpp>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/System/String.hpp>
#include <SFML/Window/Keyboard.hpp>

// MAIN FUNCTION
int main()
{
    // DECLARING/DEFINING VARIABLES
    unsigned long window_width = 512;
    unsigned long window_height = 512;
    std::string title = "Snek";
    std::string wdir = "/home/kate/Documents/coding/snek/";

    // WINDOW
    sf::RenderWindow window(sf::VideoMode(window_width, window_height), title);
    window.setFramerateLimit(60);

    // SPRITES
    sf::Texture headtexture;
    sf::Sprite head;

    headtexture.loadFromFile(wdir + "img/head.png");
    head.setTexture(headtexture);
    head.setTextureRect(sf::IntRect(47, 39, 8, 8));
    head.setPosition(30, 30);

    // LOOP
    while (window.isOpen())
    {
        sf::Event event;
        while ( window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
            { window.close(); }

            // REFRESH WINDOW
            window.clear();
            window.draw(head);
            window.display();
        }
    }

    return 0;
}
Martin Sand
  • 482
  • 1
  • 8
  • 14
  • I've suspected that already yesterday and moved everything to `int main()` just to be 100% sure. The sprites show with their default mono-chrome background color, but the image isn't there. Basically, all I see is the window's black background. – Katembers Sep 24 '17 at 16:26
  • Yes, but currently you define the texture inside the function and it goes out of scope when you exit it leading to undefined behavior. – Martin Sand Sep 24 '17 at 18:09
  • I've changed it so that both textures and sprites are used exclusively in `int main()`. It never goes anywhere else at all. Still doesn't work. – Katembers Sep 24 '17 at 20:07
  • And you pass texture by reference `sf::Texture& t` to the load_img as shown in my answer? – Martin Sand Sep 24 '17 at 20:10
  • This isn't the problem. the texture couldn't have gone out of scope since it was set inside the function. – Arnav Borborah Sep 25 '17 at 12:33
  • @MartinSand I removed `load_img()` as a whole and moved it all to `int main()` instead. There's no out of scope anymore now, because everything is in the same scope. – Katembers Sep 25 '17 at 12:40
  • @Katambers According to https://www.sfml-dev.org/documentation/2.4.2/classsf_1_1Sprite.php the texture in `Sprite (const Texture &texture)` is a reference. So when you set `sf::Texture t;` inside the function the memory is released when you exit the function and the reference points to somewhere. I updated my answer with a minimal working example. – Martin Sand Sep 25 '17 at 20:24
0

You've lost the reference to your texture. In your case, the texture is a local variable in the load_img() function.

You should keep your texture loaded and available while any sprite is using it.

I recommend you to read this section of the SFML tutorial where this problem is explained.

alseether
  • 1,889
  • 2
  • 24
  • 39
  • I've read in comments that you moved everything to `main`. Could you update with that code? maybe that way we can fin any error – alseether Sep 28 '17 at 06:16
  • That was part of the problem, but the main issue was somewhere else. See the answer that I marked as solution. That was the main issue here. – Katembers Sep 28 '17 at 18:02
0

Ok, i think i've got it.

Possibly nothing is shown because you have all drawing stuff inside event loop

Try to put that stuff in the while(window.isOpen()) loop scope.

alseether
  • 1,889
  • 2
  • 24
  • 39
  • YES. That's it! Thanks! Finally :D Was going through my code like crazy the last week and couldn't find it. – Katembers Sep 28 '17 at 18:01