【问题标题】:White square SFML白色方形 SFML
【发布时间】:2021-01-23 10:52:28
【问题描述】:

在尝试显示带纹理的按钮时,我在 SFML C++ 遇到了The white square problem。我有一个ImageButton.h,它继承自Button.h。纹理已成功加载(在调试器中检查)。但最后,我看到了一个白色方块。如何解决?

Button.h

#ifndef BUTTON_H
#define BUTTON_H

#include<SFML/Graphics.hpp>

class Button
{
public:
    Button();
    Button(sf::Vector2f size, sf::Vector2f pos,sf::Color outlineColor, float sizeOutline);
    void virtual draw(sf::RenderWindow* w) = 0;
protected:
    sf::RectangleShape frame;
};



#endif // !BUTTON_H

Button.cpp

#include "Button.h"

Button::Button()
{
}

Button::Button(sf::Vector2f size, sf::Vector2f pos,sf::Color outlineColor, float sizeOutline)
{
    frame.setPosition(pos);
    frame.setSize(size);
    frame.setOutlineColor(outlineColor);
    frame.setOutlineThickness(sizeOutline);
}


ImageButton.h

#ifndef IMAGE_BUTTON_H
#define IMAGE_BUTTON_H

#include"Button.h"

class ImageButton : public Button
{
public:
    ImageButton();
    ImageButton(sf::Vector2f size, sf::Vector2f pos, sf::Color outlineColor, float sizeOutline, std::string path);
    void draw(sf::RenderWindow* w);
private:
    sf::Texture backTexture;
    sf::Sprite background;
};


#endif // !IMAGE_BUTTON_H

ImageButton.cpp

#include "ImageButton.h"

ImageButton::ImageButton()
{
}

ImageButton::ImageButton(sf::Vector2f size, sf::Vector2f pos, sf::Color outlineColor, float sizeOutline, std::string path)
    : Button(size,pos,outlineColor,sizeOutline)
{
    
    backTexture.loadFromFile(path, sf::IntRect(sf::Vector2i(pos.x,pos.y),sf::Vector2i(size.x,size.y)));
    backTexture.setSmooth(true);
    background.setTexture(backTexture);
    background.setPosition(pos);
}

void ImageButton::draw(sf::RenderWindow* w)
{
    w->draw(this->background);
    w->draw(this->frame);
}

programm.h

#ifndef PROGRAMM_H
#define PROGRAMM_H

#include<SFML/Graphics.hpp>
#include"ImageButton.h"


class programm
{
public:
    programm();
    void run();
private:
    ImageButton b;
    sf::RenderWindow* window;
    sf::Event e;
    void render();
    void update();

};
#endif // !PROGRAMM_H

programm.cpp

#include "programm.h"

programm::programm()
{
    this->window = new sf::RenderWindow(sf::VideoMode(600, 600), "Novel Editor", sf::Style::Close);
    this->window->setPosition(sf::Vector2i(0, 0));
    this->window->setFramerateLimit(60);
    this->b = ImageButton(sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f), sf::Color::Yellow, 5.f, "images\\putin.png");
}

void programm::run()
{
    while (this->window->isOpen())
    {
        while (this->window->pollEvent(e))
        {
            update();
        }
    }
}

void programm::render()
{
    this->window->clear();
    b.draw(this->window);
    this->window->display();
}

void programm::update()
{
    switch (e.type)
    {
    case sf::Event::Closed:
    {
        this->window->close();
        break;
    }
    default:
        break;
    }

    render();
}

截图

【问题讨论】:

  • 据我所知,SFML 是建立在智能指针之上的,所以 ImageButtonthis-&gt;p 的赋值可以工作,但取决于 sf::Texturesf::Sprite 的赋值运算符是否正确实现(是吗?)。所以我会尝试将ImageButton 构造函数参数列表移动到初始化列表: b(&lt;here&gt;)。顺便说一句:this-&gt;window = new sf::RenderWindow(... 中的 newprogram.h 中的 sf::RenderWindow* window; 对我来说似乎很不一致,以这种方式混合自动变量和动态变量是很危险的。
  • 如果您查看此reddit.com/r/sfml/comments/68hmcb/white_square_problem,您会看到相同的模式,即自动将Player 对象分配给现有对象(player)。 sf::Texturesf::Sprite 中的赋值运算符(至少其中一个)看起来确实有问题

标签: c++ sfml


【解决方案1】:
programm::programm()
{
    ...
    this->b = ImageButton(...); //< this line cause the bug
}

您正在通过从本地 ImageButton 分配来初始化图像按钮 this-&gt;b。现在精灵将拥有本地实例纹理的纹理引用,当本地实例“死亡”时,纹理将被释放。您需要保持纹理的生命周期

解决方案 1:覆盖赋值运算符并在此处设置纹理

ImageButton& operator=(const ImageButton& ref) {
        backTexture = ref.backTexture;
        background = ref.background;
        background.setTexture(backTexture);
        return *this;
}

解决方案 2:创建一个 TextureManager 并将其用作您所有程序的纹理 API,并通过它维护您的纹理生命周期。

方案3:在programm构造函数处初始化图片按钮

programm::programm()
    :b (sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f), sf::Color::Yellow, 5.f, "img.jpg")
{
   ...
}

另一个错误

void ImageButton::draw(sf::RenderWindow* w)
{
    w->draw(this->background);
    w->draw(this->frame);
}

您在背景之后绘制框架,并且框架的填充颜色默认为白色

方案一:先画框,再画背景

解决方案 2:将框架填充颜色 alpha 设置为 0

Button::Button(sf::Vector2f size, sf::Vector2f pos, sf::Color outlineColor, float sizeOutline)
{
    frame.setPosition(pos);
    frame.setSize(size);
    frame.setOutlineColor(outlineColor);
    frame.setFillColor(sf::Color(0, 0, 0, 0)); // <----
    frame.setOutlineThickness(sizeOutline);
}

【讨论】:

  • 多么详细的解释!看来你对lib很熟悉,我现在才知道。你会说它成熟吗(除了众所周知的白方块问题)?
  • @Wolf 谢谢,我也刚刚开始学习 SFML。 Sprite 的内部纹理引用只是一个原始指针const Texture* m_texture,造成了所有麻烦,我们需要维护纹理的生命周期。只需阅读您的答案,它也得到了很好的解释(+1)
  • 您是否知道某些 SFML 错误报告中是否已经解决了这个问题? 我的意思是,我们看到文档没有告诉人们不应该做什么。至少应该将sf:Sprite的复制设为私有,当然最好实现复制...
  • 我试图找到一个但报告但没有。也许不复制是出于性能原因?使用 TextureManager 似乎很有趣,这里有一个有趣的"thread" (not too short)
【解决方案2】:

你和其他人的问题(on reddit, on SO, 和elsewhere) are面临是由构造函数和赋值运算符的方式引起的 sf::Sprite 已(未)实现:由于开发人员决定不 实现专门的功能,并没有将它们设为私有,编译器 提供不知道 m_texture 语义的默认值 指针,因此进行二进制复制。这可能不是最好的方法,但 it's documented.

因此,如果您在课堂上使用 sf::Sprite 成员而没有参加 复制的特殊措施,编译器将假定默认分配 语义,因此调用(无效的)赋值语义 sf::Sprite 班级。您在 program 调用 ImageButton 的默认分配

programm::programm()
{
    this->window = new sf::RenderWindow(sf::VideoMode(600, 600), "Novel Editor", sf::Style::Close);
    this->window->setPosition(sf::Vector2i(0, 0));
    this->window->setFramerateLimit(60);
    this->b = ImageButton(sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f), sf::Color::Yellow, 5.f, "images\\putin.png");
}

Sprite 成员 b 中的 textureSprite 在本地 ImageButton 内,其生命周期仅限于 programm 构造函数的范围。除此之外,您的 sprite 成员b 持有对已破坏纹理对象的引用,这会导致 是未定义的行为(另见sf::Sprite Class Reference (SFML / Learn / 2.4.1 Documentation)):

如果源纹理被破坏并且精灵尝试使用它, 行为未定义。

顺便说一句,我建议直接使用sf::RenderWindow window program 类不是通过指针,因此您可以将代码更改为 像这样(请注意,我使用 button 而不是 b 并缩短了内联 为简洁起见执行)。

class programm
{
public:
    programm():
        window(sf::VideoMode(600, 600), "Novel Editor", sf::Style::Close);
        button(sf::Vector2f(50.f, 50.f), sf::Vector2f(50.f, 50.f), 
        sf::Color::Yellow, 5.f, "images\\putin.png")
    {
        window->setPosition(sf::Vector2i(0, 0));
        window->setFramerateLimit(60);
    }
    // ...
private:
    // ...
    sf::RenderWindow window;
    ImageButton button;
};

另一种选择是提供专门的赋值运算符 (thakee nathees shows how) 或为您的 ImageButton 类复制构造函数来处理 纹理对象的深层副本。

或者,您可以考虑为 sf::Sprite 解决此问题并将其用于您的应用程序。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 1970-01-01
    • 2018-07-26
    • 2020-11-07
    • 2020-07-13
    • 1970-01-01
    • 2011-06-02
    相关资源
    最近更新 更多