在 SFML 2 中使用精灵向量时如何停止出现白色方块?

How to stop having a white square when using a sprite vector with SFML 2?

我正在尝试制作精灵矢量,但前几个元素总是显示一个白色方块。 这些变量包含在 TileRPG class 中,然后在 main.cpp 中使用 tile.cpp

void TileRPG::draw(int id, float x, float y, sf::RenderWindow& 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::RenderWindow& window);
};

Sprite::setTexture 不会复制纹理,而是存储指向它的指针。一旦纹理向量在后续 push_backs 之一中重新分配其存储空间,此指针就会失效。

问题

来自sf::Sprite::setTexture() documentation:

the sprite doesn't store its own copy of the texture, but rather keeps a pointer to the one that you passed to this function.

因此,sf::Sprite 对象不会存储 sf::Texture 对象的副本。顺便说一句,这与The Flyweight Pattern, as it can be inferred from sf::Sprite's documentation:

有关

The separation of sf::Sprite and sf::Texture allows more flexibility and better performances: indeed a sf::Texture is a heavy resource, and any operation on it is slow (often too slow for real-time applications). On the other side, a sf::Sprite is a lightweight object which can use the pixel data of a sf::Texture and draw it with its own transformation/color/blending attributes.

代码中的 m_texture 数据成员是 sf::Texture 个对象的向量,即 std::vector<sf::Texture>。如果它没有足够的容量来存储要插入的 sf::Texture 对象,则对其调用 push_back() 将重新分配向量的内部缓冲区。因此,存储在 std::vector<sf::Texture> 中的 sf::Texture 个对象在调用 push_back() 后可能最终位于内存中的不同位置。由于 sf::Sprite 对象只保留指向 sf::Texture 对象的指针,如果 sf::Texture 对象已重新分配到内存中的其他位置,这些指针最终将指向内存中的错误位置。

可能的解决方案

您可以简单地调用 std::vector::reserve()预先为向量的内部缓冲区预留足够的内存。这种方法的缺点是理想情况下,您需要提前知道向量要存储的 sf::Texture 个对象的数量,以避免浪费内存。

另一种方法是仅在您已正确填充 std::vector<sf::Texture> 容器后 调用 sf::Sprite::setTexture()。这样,由于您不打算进一步将 sf::Texture 个对象插入到向量中,因此它不会重新分配其内部缓冲区。

最后,关于容器的另一种方法是将 m_texture 定义为 std::vector<std::unique_ptr<sf::Texture>>。这样,如果向量的内部缓冲区发生重新分配,只有 std::unique_ptr<sf::Texture> 对象的地址会受到影响,不会 pointee 个对象,(即不是 sf::Texture 个对象的地址)。您也可以使用 boost::stable_vector 来达到同样的目的。在这种情况下,您只需将 m_texture 定义为 boost::stable_vector<sf::Texture>