SFML 纹理显示为白色框

SFML texture displaying as a white box

我在基础 class 中有一个纹理和 sprite 正在被另一个 class 扩展,但是当绘制时,该 sprite 显示为一个白色框。我知道这与精灵失去它 link 到纹理对象有关,但我是 C++ 的新手,所以我不太确定它是怎么发生的。

这是代码(我删除了一些不相关的部分以减少大小):

Pickup.h:

#ifndef PICKUPS_PICKUP_H
#define PICKUPS_PICKUP_H

#include <SFML\Graphics.hpp>
#include "..\Player.h"

namespace Pickups
{
    class Pickup
    {
    private:
        sf::Vector2f position;
        sf::Texture texture;
        sf::Sprite sprite;
    public:
        Pickup();

        bool drop(float dt);
        void draw(sf::RenderWindow* window);

        void setTexture(sf::String name);

        void setPos(sf::Vector2f position);
        sf::Vector2f getPos();

        void isColliding(Player* player);

        virtual void onCollect(Player* player) = 0;
    };
}

#endif

pickup.cpp:

#include "Pickup.h"

namespace Pickups
{
    Pickup::Pickup()
    {
    }

    void Pickup::draw(sf::RenderWindow* window)
    {
        window->draw(sprite);
    }

    void Pickup::setTexture(sf::String name)
    {
        if (!texture.loadFromFile("images/pickups/" + name + ".png"))
            std::cout << "Error loading image: images/pickups/" + name.toAnsiString() + ".png" << std::endl;
        else
            sprite.setTexture(texture);
    }
}

Health.h:

#ifndef PICKUPS_HEALTH_H
#define PICKUPS_HEALTH_H

#include "Pickup.h"

namespace Pickups
{
    class Health : public Pickup
    {
    private:
        int worth;
    public:
        Health(sf::Vector2f position, int worth);
        void onCollect(Player* player);
    };
}

#endif

health.cpp:

#include "Health.h"

namespace Pickups
{
    Health::Health(sf::Vector2f position, int worth)
    {
        setTexture("health");
        setPos(position);
        this->worth = worth;
    }

    void Health::onCollect(Player* player)
    {
        player->addLives(worth);
    }
}

(我不知道这是否是问题的一部分,但我也可以 post 它) 我将拾音器存储在这样的向量中:

std::vector<Pickups::Health> pickups;

为了避免此类问题,我总是将我的 Texture 声明为指针并在 class 的析构函数中将其删除。像这样,只要您的对象未被销毁,您的 Texture 就会一直存在。

验证图像的加载总是好的:

    if (!texture.loadFromFile("images/pickups/health.png")){
      //some error code
    }

但这不是这里的问题。

一个std::vector复制或移动插入的元素,所以只要你有默认的复制构造函数或者只要你不改变这个脏每个元素一个纹理-style,(元素只需要有一个公共纹理 object 实际指向,所以你浪费了很多内存) sf::Sprite object 持有纹理的指针变得无效。要了解为什么我们需要考虑插入时发生了什么:

您设置了一个不错的 Pickupish object 并将其添加到调用 copy-constructor 的向量中。假设你想要添加的 nice object 是 object A,现在 added/copied object 是 B。两者都有精灵 S 和纹理 T。两个纹理都有效,但问题是:AS指向AT,所以在复制到BBS也指向AT!正如我假设 A 只是暂时的,所以它连同它的纹理一起被破坏,然后你就拥有了它,一个漂亮的白色盒子。

您可以通过其他一些肮脏的方式解决这个问题,例如在 Pickup 中制作您自己的 copy-constructor,如下所示:

Pickup::Pickup(const Pickup& other)
: position(other.position), texture(other.texture), sprite(other.sprite)
{ sprite.setTexture(texture); }

或者通过存储 std::unique_ptr<Pickups::Health> 而不仅仅是 Pickups::Health

然而,您应该使用一种更好的方法是某种 Resourcemanager,它只存储所有相关的纹理,理想情况下是一个大的图块集,因为加载一次但大的比加载多个但小的纹理更快.您可以编写自己的非常简单的管理器或使用其他一些管理器,例如来自伟大的 Thor library. To set a specific tile as texture for a Sprite just call sf::Sprite::setTextureRect.


我想提一下您设计的一些额外改进。让 Pickup 派生自 sf::Drawable 并定义其纯虚拟 draw 函数,您可以将其设为 private。因此,您从 Pickup 派生 object 不需要从任何 sf::RenderTarget 知道,您可以只做 target.draw(myPickupObject).

不需要存储位置,让Pickup也从sf::Transformable导出即可。您不必实现任何功能,您唯一需要做的就是将矩阵应用于传递给 draw.

sf::RenderStates object

总体而言,您的 draw 函数可能如下所示:

void Pickup::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
    //'applying' transformation-matrix
    states.transform *= getTransform();

    target.draw(sprite, states);
}

所以您的 Pickup 现在只有 sf::Sprite 作为成员,总体而言,您的 header 只需要包括 SFML/Graphics/Sprite.hpp.