上下文
我有一个代表文本框的类。文本框包含标题、一些文本和一个矩形来围住它。目前它只显示自己:
struct Textbox : public sf::Drawable, public sf::Transformable{
sf::Text header;
sf::Text text;
sf::RectangleShape border;
Textbox(){
// set relative locations of the members
header.setPosition(0,0);
auto header_bounds = header.getGlobalBounds();
// the text should be just below the header
text.setPosition(0, header_bounds.top + header_bounds.height);
auto source_bounds = text.getGlobalBounds();
// this function just returns a rectangle enclosing two rectangles
sf::FloatRect rect = enclosing_rect(header_bounds, source_bounds);
// this function sets the position, width and length of border to be equal to rect's.
setRectParams(border, rect);
}
void draw(sf::RenderTarget& target, sf::RenderStates states){
states.transform = getTransform();
target.draw(header,states);
target.draw(text,states);
target.draw(border,states);
};
问题
我想要的
我想添加一个contains
方法。如果coor
在方框的边界内,则应返回true。以下是我的朴素实现:
bool Textbox::contains(sf::Vector2i coor) const {
return border.getGlobalBounds().contains(coor.x, coor.y);
}
为什么这个实现不起作用
当我通过Transformable
的非虚拟函数移动Textbox
时,这个实现就会出问题。 Textbox
会移动,并且它也会绘制转换后的形状。但是!它实际上并没有对它们进行转换!它只是显示它们已经被转换了。所以border
甚至不知道它已经被移动了。
可能的解决方案
- 我可以将
Transformable
API的所有功能添加到这个类中,从而遮蔽它们并在每个成员上自己调用transform。我不喜欢这样做,因为它让我写了比我想要的多得多的代码。这也引发了如何处理双重转换(Textbox和其成员的转换)的问题。 - 我可以编写一个完全不同的类
Group
,它包含drawable
和transformable
的向量,并具有所有的遮蔽API机制。剩下的就是继承它。这听起来实际上并不那么糟糕。 - 我听说过实体系统组件 - 它听起来有点过度设计。
- 当调用
contains
时应用变换。该函数是const - 它是一个查询。此外,在看似随机的调用上更新数据是不好的设计。 - 就像以前一样,只是变换适用于函数本地矩形。这也有点不对劲 - 为什么我要在整个
Textbox
上调用变换函数,以便在每个方法调用(到目前为止只有它的draw
和contains
,但未来谁知道)上应用它们。 - 使成员
mutable
,并在draw方法内部以某种方式对其进行变换。这听起来有点hackish。
问题
如何通过人性化的API将变换分组到多个实体上?
Textbox
对象上的转换仍然不适用于成员。因此,getGobalBounds
将返回成员未更改的包围矩形。 - Eyal Kamitchireturn sf::FloatRect(getPosition(), sf::Vector2f(rect.width * getScale(), rect.height * getScale());
以考虑缩放因素,不是吗?但是,我是在模拟运动,就好像它已经发生了一样,我不确定这种方法是否可以随着项目需求的增加而扩展(无意冒犯)。 - Eyal KamitchigetGlobalBounds
将返回正确的FloatRect
,因为它根据Textbox
的[0,0,width,height]
计算出围绕其的包围矩形,然后通过将Top
和Left
设置为Textbox
的位置来将其转换为世界坐标 **(Textbox
的原点必须为 [0,0]**)。 - DnafiqvssFloatRect
转换为sf::RectangleShape
,这样您就不必担心编写自己的旋转/缩放代码,之后调用rectangleShape.setRotation(getRotation())
等等设定旋转角度,最后返回该RectangleShape
的globalBounds
即可。 - DnafiqvssgetGlobalBounds
,你只需要return this->getTransform().transformRect(border.getGlobalBounds());
。然而,如果你旋转文本框,这仍然不起作用,因为一个旋转的形状的全局边界(即使使用你的代码)包含在形状本身之外的点。 - Miguel