Почему текстуры не отображаются должным образом?

#c #sprite #sfml #stdvector

#c #спрайт #sfml #stdvector

Вопрос:

У меня есть проект, в котором мои текстуры не отображаются внутри window должным образом. Вместо текстур он всегда будет показывать пустые белые спрайты. Я не могу понять, что я делаю не так. Вот мой код:

заголовок класса с векторными текстурами

     class textures
    {
    public:
        sf::Texture dirt_Texture;
        sf::Texture grass_Texture;
        std::vector<sf::Texture> texturesVector;
        /*
             dirt = 0
            grass = 1
        */
    public:
        textures();

    sf::Texture getTextureByID(int id);



};
 

и .cpp-файл для него:

 //constructor populate vector with textures
textures::textures()
{
    if (!dirt_Texture.loadFromFile("dirt.PNG"))
    {
        std::cout << "Error while loading texture.n";
    }
    dirt_Texture.isSmooth();
    texturesVector.push_back(dirt_Texture);

    if (!grass_Texture.loadFromFile("grass.PNG"))
    {
        std::cout << "Error while loading texture.n";
    }
    texturesVector.push_back(grass_Texture);
    std::cout << "Texture constructor has been called.n";
}

sf::Texture textures::getTextureByID(int id)
{
    return texturesVector[id];
}
 

с дочерним классом sprite:

 class sprites : public textures
{
private:
    //textures* textureClass;
    sf::Sprite sprite;
    std::vector<int> textureIDsVector;
public:
    sprites();
    ~sprites();

    int getVectorValueAtLine(int amp;line);
    void setVectorValueAtLineTo(int amp;line, int value);
    void updateTextureAtLine(intamp; line);
    sf::Sprite* getSprite();

};
 

где primary functions выглядит так в .cpp

 sprites::sprites()
{
    std::vector<int> cubeIDsVector(100, 0);
    textureIDsVector = cubeIDsVector;
    //textureClass = new textures();
    std::cout << "Sprite constructor has been called.n";
}

void sprites::updateTextureAtLine(intamp; line)
{
    switch (textureIDsVector[line])
    {
    case 0:
        //switcher = 0;
        sprite.setTexture(getTextureByID(0));
        sprite.setColor(sf::Color(55, 150, 150, 150));
        std::cout << "case 0n";
        break;
    case 1:
        //switcher = 1;
        sprite.setTexture(getTextureByID(1));
        sprite.setColor(sf::Color(155, 50, 150, 150));
        std::cout << "case 1n";
        break;
    default:
        break;
    }
}
 

в основном цикле я создаю объект sprite в куче после sf::RenderWindow и после window.clean(…) вызывает функцию updateTextureAtLine() из цикла for .

Ошибка не возвращена, и при отладке это тоже кажется нормальным, я новичок в этом, но похоже, что текстура всегда находится в памяти, я не могу выяснить, в чем проблема.

Решается, как описано ниже.

Ответ №1:

getTextureByID() возвращает копию текстуры, а не ссылку. Эта копия уничтожается, когда она выходит за пределы области видимости, поэтому, как только вызов sprite.setTexture() завершается.
В результате спрайт получает указатель на текстуру, которая больше не существует.

Решение состоит в том, чтобы вместо этого возвращать указатель или ссылку из getTextureByID() . Пока мы меняем эту функцию, мы могли бы также создать функцию и возвращаемое значение const , поскольку мы не планируем ее изменять, хотя это необязательно — это хорошая привычка, на которую я мог бы также указать.

Вот пример const функции, которая возвращает const reference :

 // header
const sf::Textureamp; getTextureByID(int id) const;
// source
const sf::Textureamp; textures::getTextureByID(int id) const {
    return texturesVector[id];
}
 

Надеюсь, это должно решить вашу проблему.


Несвязанные примечания:

Ваш класс текстур хранит всего четыре текстуры, две текстуры грязи и две текстуры травы.
Это может быть то, что вы намеревались, или это может быть не так.
Одно из решений — не иметь переменных-членов dirt_Texture и grass_Texture или просто не иметь texturesVector .
Другое решение — создать texturesVector указатели хранилища на текстуры, они также могут быть const , и это будет выглядеть так:

 // header
std::vector<const sf::Texture*> texturesVector;
// source
//   constructor
texturesVector.push_back(amp;dirt_Texture);
...
texturesVector.push_back(amp;grass_Texture);
//   getTextureByID()
return *texturesVector[id]; // (or alternatively return a pointer)
 

Наконец, если вы храните объекты текстур внутри texturesVector (если вы переключитесь на указатели, это не будет проблемой), то обратите внимание, что добавление дополнительных текстур может заставить внутренний массив вектора изменить местоположение в памяти и, таким образом, сделать недействительными ваши текстуры.
Если вы не планируете добавлять дополнительные текстуры на полпути к запуску программы, тогда все в порядке. Я часто добавляю текстуры в середине выполнения программ, потому что мне нравится ленивая инициализация. Если вы планируете добавлять больше текстур, то либо используйте другой контейнер, который не перемещает свои объекты, либо динамически выделяйте место для ваших текстур. Я поклонник последнего, используя сопоставление строк с уникальными указателями текстур ( std::unordered_map<std::string, std::unique_ptr<sf::Texture>> )

Комментарии:

1. Итак, если я добавлю sf::Texture dirt_Texture и sf::Texture grass_Texture; в конструктор текстур, он исчезнет после вызова конструктора, и в vector будут храниться только две текстуры. Это правильно?

2. и т.д. основная проблема решена, большое вам спасибо 🙂

3. Да, перемещение sf::Texture dirt_Texture и sf::Texture grass_Texture ввод в конструктор текстур должны сделать их сохраненными только в векторе.