I am using SFML to display a sprite on the screen. I first use a texture and call loadFromFile() to load the image. This works well with no errors occurring. It seems that the image path is correct and it loads the image, but it doesn't seem to store the data in the sprite. The sprite is displayed without error, but it shows a blank white image.
Is there a problem with my code in the following snippets? They should be fairly easy to read :).
My code:
Main.cpp
#include <SFML/Graphics.hpp>
#include "Menu.h"
using namespace sf;
int main()
{
VideoMode vm = VideoMode::getDesktopMode();
int width = vm.width, height = vm.height;
RenderWindow window(VideoMode(width, height), "Score Arena v1.0", Style::Fullscreen);
window.setPosition(Vector2i(0, 0));
Menu menu(width, height);
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
switch (event.type) {
case Event::Closed:
window.close();
break;
//when a key is pressed
case Event::KeyPressed:
if (Keyboard::isKeyPressed(Keyboard::Escape))
window.close();
break;
}
}
window.clear();
menu.draw(window);
window.display();
}
return 0;
}
Menu.h
#pragma once
#include <string>
#include <SFML/Graphics.hpp>
using namespace std;
using namespace sf;
class Menu {
int page = 0;
string title = "Score Arena";
string labels[4]{
"Single Player",
"Multi Player",
"Options",
"Exit"
};
Sprite background;
int width, height;
public:
Menu(int, int);
void draw(RenderWindow &window);
};
Menu.cpp
#include "Menu.h"
#include <SFML/Graphics.hpp>
#include <iostream>
Menu::Menu(int width, int height) {
this->width = width;
this->height = height;
Texture bg;
if (!bg.loadFromFile("menuBg.jpg"))
std::cout << "Error loading image!" << std::endl;
background.setTexture(bg);
}
void Menu::draw(RenderWindow &window) {
window.draw(background, RenderStates::Default);
}
An sf::Sprite
object doesn't internally copy the passed sf::Texture
object when you call the setTexture()
member function. An sf::Sprite
just refers to an sf::Texture
; the former doesn't own the latter. The associated sf::Texture
must exist when you render the sf::Sprite
.
It is stated in the documentation for sf::Sprite
:
It is important to note that the
sf::Sprite
instance doesn't copy the texture that it uses, it only keeps a reference to it. Thus, asf::Texture
must not be destroyed while it is used by asf::Sprite
(i.e. never write a function that uses a localsf::Texture
instance for creating a sprite).
The problem is in your Menu
's constructor:
Menu::Menu(int width, int height) {
this->width = width;
this->height = height;
Texture bg; // <-- it is local variable !!!
if (!bg.loadFromFile("menuBg.jpg"))
std::cout << "Error loading image!" << std::endl;
background.setTexture(bg); // <-- associating sprite to a local object!!!
// bg goes out of existence!!!
}
The sf::Texture
object, bg
, ceases to exist as soon the program control flow leaves the constructor because bg
is a local variable inside the constructor. As a result, the sf::Sprite
object, background
– a Menu
data member – outlives its associated sf::Texture
object, bg
– a local object.
Then, in Menu::draw()
you use this sf::Sprite
object, background
, which internally refers to an sf::Texture
object that no longer exists:
void Menu::draw(RenderWindow &window) {
window.draw(background, RenderStates::Default);
}
Instead of the sf::Texture
object being a local object in Menu
's constructor, you could make it a data member of Menu
:
class Menu {
// ...
Texture bg;
Sprite background;
// ...
};
This way, Menu
owns bg
(in contrast to Menu
's constructor owning bg
as was the case before), and therefore Menu
controls bg
's lifetime: bg
is kept alive while the Menu
object lives. This time, when you call Menu::draw()
, the sf::Sprite
object does refer to a living sf::Texture
object.