GameObject class doesn't draw a image but a white box instead

Started by
13 comments, last by Rattenhirn 7 years, 7 months ago

I tried making the sf::texture a pointer so that it was impossible for it to run out of scope, and the image is rendered as it should be.

I don't understand how the texture is running out of scope though...

It's a part of the Sprite class, so unless the sprite itself gets destroyed, it shouldn't run out of scope.

And if the sprite gets destroyed, it should remove the texture completely, and also remove the white square....??? o.o

Edit : Figured it out by playing around, still don't understand how this fixed the problem though.

I changed my MainMenuState constructor to use a initialize list for gameObject instead, which resulted in the image being displayed. :)

Advertisement

I think the fundamental confusion for you here is, that in C++ there's a difference between these two cases:

Case 1:

MyClass myObject = MyClass(12345); // calls the MyClass constructor

Case 2:

MyClass myObject; // calls the MyClass default constructor

myObject = MyClass(12345); // creates a temporary MyClass object, copies it into myObject and destroys the temporary again

When you write a constructor that initializes members in the body and not in the initializer list, like you did in MainMenuState, you have Case 2.

Besides being a bit inefficient this usually is no problem, unless MyClass cannot be copied safely, as is the case with sf::Texture.

For this reason, I discourage the use of the "initialization written as assignment" notation, so this situation can never arise:

Case 1:

MyClass myObject(12345); // calls the MyClass constructor

Case 2:

MyClass myObject; // calls the MyClass default constructor

myObject(12345); // compile error!

In additon the NonCopyable idiom I referred to earlier should be used to make it impossible to unintentionally copy objects of classes that can't be copied safely.

Here's a longer example illustration the issue, I've written and tested it here, so you can just paste and run it.


#include <iostream>

using namespace std;

class InternalTexture
{
public:
    InternalTexture() : data(123456) { }
    int data;
};

class Texture
{
public:
    Texture()
        : internal(new InternalTexture())
    {
        cout << "Texture::ctor" << endl;
    }
    
    ~Texture()
    {
        cout << "Texture::dtor" << endl;
        delete internal;
    }
    
    InternalTexture* internal;
};

class Sprite
{
public:
    Sprite()
    {
        texture = Texture();
    }
    
    Texture texture;
};

int main()
{
   // Case 1:
   {
      cout << "Texture only:" << endl; 
      Texture texture = Texture();
      cout << "texture.internal = " << texture.internal->data << endl;
   }
   
   // Case 2:
   {
       cout << "Texture in sprite:" << endl;
       Sprite sprite;
       cout << "sprite.texture.internal = " << sprite.texture.internal->data << endl;
       // crash in Texture::dtor
   }
   
   return 0;
}

InternalTexture can't be copied safely, so Case 2 will fail. This is what I think happens inside sf::Texture, although the implementation there is more sophisticated as it knows that it refers to an invalid "InternalTexture" and handles it by that dreaded white square.

You can try to fix Case 2 be doing one or more of these:

1) Fix "Texture", by correctly applying the "rule of 3" (look it up) and implementing copy constructor and copy assignment operator

2) Fix "Texture" by making it "uncopyable" by making copy constructor and copy assignment operator private (NonCopyable idiom)

3) Fix "Sprite" by correctly using initializer lists to initialize its members. Note that Sprite would need to be fixed by applying 1 or 2 as well.

4) Make everything way more complicated (and fun!) by learning about C++ 11 move semantics

I hope that helps!

I skimmed over it.. do you ever initialize "visible" anywhere in your sprite or GameObject? No? Then it's set to false, and your Sprite class is not drawing it due to the if conditional.

Edit: Scratch that, I see you initialized it in your .h file (not used to that at all...)

I'd have to add what has been said: you're going to have to use your debugger. Make sure all values are set correctly, then continue. As it stands, your code appears valid. Once you make sure EVERY piece of variables and objects have valid data, then please attach the rest of your code.

I think the fundamental confusion for you here is, that in C++ there's a difference between these two cases:

Case 1:

MyClass myObject = MyClass(12345); // calls the MyClass constructor

Case 2:

MyClass myObject; // calls the MyClass default constructor

myObject = MyClass(12345); // creates a temporary MyClass object, copies it into myObject and destroys the temporary again

When you write a constructor that initializes members in the body and not in the initializer list, like you did in MainMenuState, you have Case 2.

Besides being a bit inefficient this usually is no problem, unless MyClass cannot be copied safely, as is the case with sf::Texture.

For this reason, I discourage the use of the "initialization written as assignment" notation, so this situation can never arise:

Case 1:

MyClass myObject(12345); // calls the MyClass constructor

Case 2:

MyClass myObject; // calls the MyClass default constructor

myObject(12345); // compile error!

In additon the NonCopyable idiom I referred to earlier should be used to make it impossible to unintentionally copy objects of classes that can't be copied safely.

Here's a longer example illustration the issue, I've written and tested it here, so you can just paste and run it.


#include <iostream>

using namespace std;

class InternalTexture
{
public:
    InternalTexture() : data(123456) { }
    int data;
};

class Texture
{
public:
    Texture()
        : internal(new InternalTexture())
    {
        cout << "Texture::ctor" << endl;
    }
    
    ~Texture()
    {
        cout << "Texture::dtor" << endl;
        delete internal;
    }
    
    InternalTexture* internal;
};

class Sprite
{
public:
    Sprite()
    {
        texture = Texture();
    }
    
    Texture texture;
};

int main()
{
   // Case 1:
   {
      cout << "Texture only:" << endl; 
      Texture texture = Texture();
      cout << "texture.internal = " << texture.internal->data << endl;
   }
   
   // Case 2:
   {
       cout << "Texture in sprite:" << endl;
       Sprite sprite;
       cout << "sprite.texture.internal = " << sprite.texture.internal->data << endl;
       // crash in Texture::dtor
   }
   
   return 0;
}

InternalTexture can't be copied safely, so Case 2 will fail. This is what I think happens inside sf::Texture, although the implementation there is more sophisticated as it knows that it refers to an invalid "InternalTexture" and handles it by that dreaded white square.

You can try to fix Case 2 be doing one or more of these:

1) Fix "Texture", by correctly applying the "rule of 3" (look it up) and implementing copy constructor and copy assignment operator

2) Fix "Texture" by making it "uncopyable" by making copy constructor and copy assignment operator private (NonCopyable idiom)

3) Fix "Sprite" by correctly using initializer lists to initialize its members. Note that Sprite would need to be fixed by applying 1 or 2 as well.

4) Make everything way more complicated (and fun!) by learning about C++ 11 move semantics

I hope that helps!

Oh, I see. Did some more reading on it and I (finally ^^) understand. :P

Thanks a lot for the help :)

Oh, I see. Did some more reading on it and I (finally ^^) understand. Thanks a lot for the help

Yay!

This topic is closed to new replies.

Advertisement