SFML:如何通过易用API为多个可变换可绘制对象分组变换
问题背景
我实现了一个代表文本框的Textbox类,继承自sf::Drawable和sf::Transformable,包含标题文本、内容文本和边框矩形,目前仅实现了绘制功能:
struct Textbox : public sf::Drawable, public sf::Transformable{ sf::Text header; sf::Text text; sf::RectangleShape border; Textbox(){ // 设置成员的相对位置 header.setPosition(0,0); auto header_bounds = header.getGlobalBounds(); // 内容文本位于标题正下方 text.setPosition(0, header_bounds.top + header_bounds.height); auto source_bounds = text.getGlobalBounds(); // 该函数返回包围两个矩形的最小矩形 sf::FloatRect rect = enclosing_rect(header_bounds, source_bounds); // 该函数将边框的位置、宽高设置为与rect一致 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是否在文本框边框内,最初的实现如下:
bool Textbox::contains(sf::Vector2i coor) const { return border.getGlobalBounds().contains(coor.x, coor.y); }
但当通过Transformable的非虚函数移动Textbox时,该方法失效。原因是绘制时虽然应用了Textbox的变换,但并未修改成员对象自身的变换状态,边框的全局边界仍停留在初始位置,不知道自身已被移动。
可选方案
- 重写
Transformable的所有API并手动为每个成员应用变换,但代码量过大且存在双重变换问题; - 编写
Group类管理可绘制和可变换对象集合,Textbox继承该类; - 使用实体组件系统(ECS),但过于繁琐;
- 在
contains调用时应用变换,但违背const函数设计原则; - 在方法内对局部矩形应用变换,担心设计不够优雅;
- 将成员设为
mutable并在draw中变换,属于hack式实现。
核心问题
如何通过易用的API为多个实体分组应用变换?
解决方案
针对contains方法的失效问题,最简洁且符合SFML设计原则的解决方式是利用Transformable提供的变换方法,将坐标或局部边界转换到正确的坐标系下,无需修改成员变量或重写大量API:
方法一:将屏幕坐标转换为局部坐标系判断
bool Textbox::contains(sf::Vector2i coor) const { // 将屏幕坐标转换为Textbox局部坐标系下的浮点坐标 sf::Vector2f localCoord = getInverseTransform().transformPoint(static_cast<sf::Vector2f>(coor)); // 判断局部坐标是否在边框的局部边界内 return border.getLocalBounds().contains(localCoord); }
方法二:将局部边界转换为全局坐标系判断
bool Textbox::contains(sf::Vector2i coor) const { // 用Textbox的变换将边框的局部边界转换为全局边界 sf::FloatRect globalBorderBounds = getTransform().transformRect(border.getLocalBounds()); // 判断屏幕坐标是否在变换后的全局边界内 return globalBorderBounds.contains(static_cast<sf::Vector2f>(coor)); }
这两种实现都完全符合const函数的设计要求,无需修改任何成员变量的状态,同时正确应用了Textbox的变换。本质上是利用SFML的变换矩阵,在需要判断时动态计算正确的边界或坐标,避免了维护多个实体变换状态的麻烦。
对于“分组应用变换”的核心问题,这种思路可以推广:当需要管理多个共享同一变换的可绘制对象时,无需为每个对象单独设置变换,而是通过父容器的变换统一处理,在需要碰撞检测、边界计算时,动态通过变换矩阵转换坐标系即可。这种方式既遵循了SFML的设计模式,又避免了冗余代码和状态不一致的问题。
内容的提问来源于stack exchange,提问作者Eyal Kamitchi
相关产品推荐
相关产品推荐

