Как избавиться от белого квадрата при использовании спрайтового вектора с SFML 2?

#c #sfml

#c #sfml

Вопрос:

Я пытаюсь создать спрайтовый вектор, но первые несколько элементов всегда показывают белый квадрат. Переменные содержатся в классе TileRPG, чтобы затем использоваться в main.cpp tile.cpp

 void TileRPG::draw(int id, float x, float y, sf::RenderWindowamp; window) {
    m_sprite[id].setPosition(sf::Vector2f(x, y));
    window.draw(m_sprite[id]);
}


TileRPG::TileRPG() {
    std::ifstream file { "data/tileRPGList.rf" };
    std::string line{ "" }, access { "" };
    sf::Texture texture;
    sf::Sprite sprite;
    unsigned int i = 0;

    while (std::getline(file, line)) {
        m_texture.push_back(texture);
        m_sprite.push_back(sprite);
        access = "data/"   line;
        m_texture[i].loadFromFile(access);
        m_sprite[i].setTexture(m_texture[i]);
        i  ;
    }
}
  

tile.hpp

 class Tile {
protected:
    std::vector<sf::Texture> m_texture;
    std::vector<sf::Sprite> m_sprite;
};

class TileRPG : public Tile {
public:
    TileRPG();
    void draw(int id, float x, float y, sf::RenderWindowamp; window);
};
  

Ответ №1:

Проблема

Из sf::Sprite::setTexture() документации:

спрайт не сохраняет свою собственную копию текстуры, а скорее сохраняет указатель на ту, которую вы передали этой функции.

Итак, sf::Sprite объект не хранит копию sf::Texture объекта. Кстати, это связано с шаблоном Flyweight, как это можно сделать из sf::Sprite документации:

Разделение sf::Sprite и sf::Texture обеспечивает большую гибкость и лучшую производительность: действительно, sf::Texture это тяжелый ресурс, и любая операция с ним выполняется медленно (часто слишком медленно для приложений реального времени). С другой стороны, a sf::Sprite — это облегченный объект, который может использовать пиксельные данные a sf::Texture и рисовать его со своими собственными атрибутами преобразования / цвета / смешивания.

m_texture Элемент данных в вашем коде является вектором sf::Texture объектов, т.Е. std::vector<sf::Texture> . Вызов push_back() этого вектора перераспределит внутренний буфер вектора, если в нем недостаточно места для хранения sf::Texture объекта для вставки. Следовательно, sf::Texture объекты, хранящиеся в std::vector<sf::Texture> , могут оказаться расположенными в другой позиции в памяти после вызова push_back() . Поскольку sf::Sprite объекты просто хранят указатели на sf::Texture объекты, эти указатели в конечном итоге будут указывать на неправильное местоположение в памяти, если sf::Texture объекты были перераспределены где-то еще в памяти.

Возможные решения

Вы могли бы просто вызвать, std::vector::reserve() чтобы заранее зарезервировать достаточно памяти для внутреннего буфера вектора. Недостатком этого подхода является то, что в идеале вам нужно заранее знать количество sf::Texture объектов, которые ваш вектор будет хранить, чтобы избежать пустой траты памяти.

Другим подходом было бы вызывать sf::Sprite::setTexture() только после того, как вы правильно заполнили std::vector<sf::Texture> контейнер. Таким образом, поскольку вы не собираетесь дополнительно вставлять sf::Texture объекты в вектор, он не будет перераспределять свой внутренний буфер.

Наконец, альтернативным подходом к контейнеру было бы вместо этого определить m_texture как std::vector<std::unique_ptr<sf::Texture>> . Таким образом, если происходит перераспределение внутреннего буфера вектора, затрагиваются только адреса std::unique_ptr<sf::Texture> объектов, не адреса указательных объектов (т. Е. не адреса sf::Texture объектов). Вы также могли бы использовать boost::stable_vector для той же цели. В этом случае вместо этого вы бы просто определили m_texture as boost::stable_vector<sf::Texture> .

Ответ №2:

Sprite::setTexture не создает копию текстуры, но сохраняет указатель на нее. Затем этот указатель становится недействительным, как только вектор текстур перераспределяет свое хранилище в одном из последующих push_back файлов.