白色方块 SFML
White square SFML
我在 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);
}
图片Button.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
图片Button.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();
}
截图
你和其他人的问题(on reddit,
on SO,
和 elsewhere)
所面临的是由构造函数和赋值运算符的方式引起的
sf::Sprite
are (not) implemented: 因为开发者决定不
实现专门的功能并且没有将它们设为私有,编译器
提供不了解 m_texture
语义的默认值
指针,因此进行二进制复制。这可能不是最好的方法,但是
it's documented.
因此,如果您在 class 中使用 sf::Sprite
成员,而没有采取
复制的特殊措施,编译器将采用默认分配
语义,因此调用的(无效的)赋值语义
sf::Sprite
class。您在中创建的本地 ImageButton
对象
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");
}
成员b
中的Sprite
接管了指针texture
Sprite
在本地 ImageButton
中,其生命周期限于
programm
构造函数的作用域。除此之外,您的 sprite
成员 b
持有对已销毁纹理对象的引用,这会导致
是未定义的行为(另见 sf::Sprite Class Reference (SFML / Learn / 2.4.1 Documentation)):
If the source texture is destroyed and the sprite tries to use it,
the behavior is undefined.
顺便说一下,我建议直接使用 sf::RenderWindow window
program
class 不是通过指针,因此您可以将代码更改为某些内容
像这样(请注意,我使用 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;
};
另一种选择是提供专门的赋值运算符
()
或为您的 ImageButton
class 复制构造函数来处理
纹理对象的深拷贝。
或者,您可以考虑实现一个包装器 class
sf::Sprite
解决此问题并将其用于您的应用程序。
programm::programm()
{
...
this->b = ImageButton(...); //< this line cause the bug
}
您正在通过从本地 ImageButton
分配来初始化图像按钮 this->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);
}
我在 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);
}
图片Button.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
图片Button.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();
}
截图
你和其他人的问题(on reddit,
on SO,
和 elsewhere)
所面临的是由构造函数和赋值运算符的方式引起的
sf::Sprite
are (not) implemented: 因为开发者决定不
实现专门的功能并且没有将它们设为私有,编译器
提供不了解 m_texture
语义的默认值
指针,因此进行二进制复制。这可能不是最好的方法,但是
it's documented.
因此,如果您在 class 中使用 sf::Sprite
成员,而没有采取
复制的特殊措施,编译器将采用默认分配
语义,因此调用的(无效的)赋值语义
sf::Sprite
class。您在中创建的本地 ImageButton
对象
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");
}
成员b
中的Sprite
接管了指针texture
Sprite
在本地 ImageButton
中,其生命周期限于
programm
构造函数的作用域。除此之外,您的 sprite
成员 b
持有对已销毁纹理对象的引用,这会导致
是未定义的行为(另见 sf::Sprite Class Reference (SFML / Learn / 2.4.1 Documentation)):
If the source texture is destroyed and the sprite tries to use it, the behavior is undefined.
顺便说一下,我建议直接使用 sf::RenderWindow window
program
class 不是通过指针,因此您可以将代码更改为某些内容
像这样(请注意,我使用 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;
};
另一种选择是提供专门的赋值运算符
(ImageButton
class 复制构造函数来处理
纹理对象的深拷贝。
或者,您可以考虑实现一个包装器 class
sf::Sprite
解决此问题并将其用于您的应用程序。
programm::programm()
{
...
this->b = ImageButton(...); //< this line cause the bug
}
您正在通过从本地 ImageButton
分配来初始化图像按钮 this->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);
}