SFML 纹理显示为白框 [英] SFML texture displaying as a white box

查看:43
本文介绍了SFML 纹理显示为白框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在由另一个类扩展的基类中有一个纹理和精灵,但是在绘制时,精灵显示为一个白框.我知道这与精灵失去它与纹理对象的链接有关,但我对 C++ 有点陌生,所以我不确定它是怎么发生的.

I have a texture and sprite in a base class that is being extended by another class, however when drawn, the sprite displays as a white box. I know this is something to do with the sprite losing it's link to the texture object, but I'm kind of new to C++, so I'm not really sure how it happened.

这是代码(我已经删除了一些不相关的部分以缩小尺寸):

Here is the code (I've removed some of the irrelevant parts to cut down the size):

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:

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);
    }
}

健康.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:

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);
    }
}

(我不知道这是否是问题的一部分,但我也可以发布它)我将拾音器存储在一个向量中,如下所示:

(I don't know if this is part of the problem, but I might as well post it too) I store the pickups in a vector like so:

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

推荐答案

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

A std::vector copies or moves the inserted elements, so as long as you have the default copy constructor or as long as you do not change this dirty a texture per element-style, (the elements just need to have one common texture object to actually point to, so you waste much much memory) the pointer that the sf::Sprite object holds to the texture gets invalid. To see why we need to think whats happens on insertion:

您设置了一个不错的 Pickupish 对象并将其添加到调用复制构造函数的向量中.假设您想要添加的漂亮对象是对象 A,现在添加/复制的对象是 B.两者都有一个精灵 S 和一个纹理 T.两个纹理都有效,但问题是:AS 指向 AT,所以复制到BBS也指向AT!我假设 A 只是暂时的,所以它会被破坏,连同它的纹理,你有它,一个漂亮的白盒子.

You setup a nice Pickupish object and add it to the vector which calls the copy-constructor. Lets say your nice object that you wanted to add is object A and the now added/copied object is B. Both have a sprite S and a texture T. Both textures are valid, but the problem is this: A's S points to A's T, so after copying it to B B's S points also to A's T! As I assume A is just temporary so it gets destructed, together with its texture, and there you have it, a nice white box.

你可以用其他一些肮脏的方法来解决这个问题,比如在 Pickup 中制作你自己的复制构造函数,如下所示:

You can solve this in some other dirty ways like making your own copy-constructor in Pickup like this:

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

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

or by storing std::unique_ptr<Pickups::Health>'s and not just Pickups::Health's.

然而,你应该使用一种更好的方法是某种Resourcemanager,它只存储所有相关的纹理,理想情况下,一个大的tileset,因为加载一次但大比加载多个但小纹理.您可以编写自己的非常简单的管理器或使用其他一些管理器,例如来自伟大的 Thor 库.要将特定图块设置为 Sprite 的纹理,只需调用 sf::Sprite::setTextureRect.

However a much better way you should use is some kind of Resourcemanager, which just stores all relevant textures, ideally one, a big tileset, because loading once but big is faster than loading multiple but small textures. You can write your own very simple manager or use some other e.g. the one from the great Thor library. To set a specific tile as texture for a Sprite just call sf::Sprite::setTextureRect.

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

I want to mention some additional improvements to your design. Let Pickup derive from sf::Drawable and define its pure virtual draw function, which you can make private. Thus your from Pickup deriving object doesn't need to know from any sf::RenderTarget and you can just do target.draw(myPickupObject).

不需要存储位置,只需让 Pickup 也从 sf::Transformable 派生.您不必实现任何函数,您唯一需要做的就是将矩阵应用于传递给 drawsf::RenderStates 对象.

There is no need to store the position, just let Pickup derive from sf::Transformable, too. You don't have to implement any functions, the only thing you need to do is applying the matrix to the sf::RenderStates object thats passed to draw.

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

Overall your draw function might look like this:

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

    target.draw(sprite, states);
}

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

So your Pickup has now only sf::Sprite as member and overall your header just needs to include SFML/Graphics/Sprite.hpp.

这篇关于SFML 纹理显示为白框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆