I'm working on a game project and found myself getting stuck on the following issue:
I have a class called Game : public sf::Drawable
which I use to (among other things), draw everything in my game. Game
contains a class called Player : public Entity
, which in turn is a subclass of Entity : public sf::Drawable
.
These classes are slightly simplified, but the affected functions are the same:
class Entity : public sf::Drawable
{
private:
sf::Sprite eSprite;
sf::Texture eTex;
std::string texpath;
public:
virtual Entity(std::string texpath, sf::IntRect intrect){
this->texpath = texpath;
eTex.loadFromFile(texpath, intrect);
eSprite.setTexture(eTex); }
virtual ~Entity(){}
virtual void draw(sf::RenderTarget target, sf::RenderStates states)const{
target.draw(this->eSprite);}
//Lots of other functions
}
class Player : public Entity¨
{
public:
~Player(){}
Player(std::string texpath, sf::IntRect spriteintrect)
:Entity(texpath, spriteintrect){}
void draw(sf::RenderTarget target, sf::RenderStates states){
Entity::draw(target, states); }
}
#define PLAYER_START "../filepath/image.png",
sf::IntRect{0,0,40,60,}, sf::Vector2f(320.0f, 200.0f) //Ease of access
class Game : public sf::Drawable
{
private:
Player player;
public:
Game() { player = Player(PLAYER_START); };
~Game() {};
void draw(sf::RenderTarget &target, sf::RenderStates states)const { target.draw(player); }
};
int main(){
sf::RenderWindow window(sf::VideoMode(640, 480), "Game Test");
sf::Event event = sf::Event{};
Game game;
while (window.isOpen())
{
while (window.pollEvent(event))
if (event.type == sf::Event::Closed)
window.close();
window.clear();
window.draw(game);
window.display();
}
}
This code produces a white square.
I have tried:
Player
outside of Game
and calling window.draw(player);
on it. This works.Player
through assignment operator and drawing. This works.Player
through the copy-constructor and drawing. This works.Player
into a Game
class and drawing that. This does not work and is what I illustrate with the code aboveI realise this problem could (probably) be solved by moving the sf::Sprite
and sf::Texture
to the Player
class, but since I want to eventually derive Coin
and Enemy
from the Entity base class, I'd prefer to solve the problem as-is.*
Thanks for any help /Lore
Objects of sf::Sprite
store POINTER to textures.
When you have player = Player()
for base class will be called copy assignment operator - generated by compiler by default. It copies all member variables one by one.
After executing that copy assignment for:
entity1 = entity2;
we have:
entity1.eSprite = entity2.eSprite; // shallow copy
entity1.eTex = entity2.eTex;
and the key point - what texture is pointed by entity1.eSprite
? It is entity2.eTex! And unfortunately for you, entity2 is temporary in your case, so you have dangling pointer to texture inside sprite ... common mistake in SFML and it is described in offical reference of SFML.
You successfully loaded a texture, constructed a sprite correctly, and... all you see on your screen now is a white square. What happened?
This is a common mistake. When you set the texture of a sprite, all it does internally is store a pointer to the texture instance. Therefore, if the texture is destroyed or moves elsewhere in memory, the sprite ends up with an invalid texture pointer.
Solution, add copy assignment operator which properly sets texture for sprite, as deep-copy:
Entity& operator=(const Entity& theOther) {
if (this != &theOther) {
this->eTex = theOther.eTex;
this->eSprite.setTexture(this->eTex); // the most imporant line in this answer
}
return *this;
}