如何将三个方形Sprite组合为立方体并实现平滑移动?
问题描述
我希望通过组合三个Sprite(其中两个经过剪切和缩放变换)来实现如下立方体效果:
我已实现剪切变换函数:
sf::Transform createShearTransform(float shearX, float shearY) { sf::Transform transform; transform = sf::Transform( 1, shearX, 0.f, shearY, 1, 0.f, 0.f, 0.f, 1.f ); return transform; }
但无法完成预期效果,也没法让整个立方体平滑移动,以下是我的代码:
#include <SFML/Graphics.hpp> sf::Transform createShearTransform(float shearX, float shearY) { sf::Transform transform; transform = sf::Transform( 1, shearX, 0.f, shearY, 1, 0.f, 0.f, 0.f, 1.f ); return transform; } int main() { sf::Texture texture; texture.loadFromFile("tex.png"); sf::Sprite front(texture); sf::Sprite top(texture); sf::Sprite right(texture); float offset = 64 + 32; float size = 64; top .setPosition({ offset , offset }); front.setPosition({ offset , offset + size }); right.setPosition({ offset + size, offset + size }); sf::Transform shear1 = createShearTransform(-1, 0); sf::Transform shear2 = createShearTransform(0, -1); sf::RenderWindow window( sf::VideoMode(800, 800), "" ); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } auto mousepos = sf::Vector2f(sf::Mouse::getPosition(window)); front.setPosition(mousepos); right.setPosition(mousepos); top.setPosition(mousepos); window.clear(); window.draw(front); sf::Transform f1; f1.translate({ 64, -128 }); sf::Transform s1; s1.scale({ 1, 0.75 }); sf::Transform s2; s2.scale({ 0.75 , 1 }); sf::Transform f2; f2.translate({ 16, -60 }); window.draw(top, f1 * top.getTransform() * shear1 * s1); window.draw(right, f2 * right.getTransform() * shear2 * s2); window.display(); } }
解决方案
核心问题在于变换顺序错误和各面未绑定统一基准点。SFML变换是右结合的,且要实现整体移动,所有面必须基于同一个中心位置计算偏移。以下是修正后的代码:
#include <SFML/Graphics.hpp> sf::Transform createShearTransform(float shearX, float shearY) { sf::Transform transform; transform = sf::Transform( 1, shearX, 0.f, shearY, 1, 0.f, 0.f, 0.f, 1.f ); return transform; } int main() { sf::Texture texture; if (!texture.loadFromFile("tex.png")) { // 纹理加载失败处理 return 1; } sf::Sprite front(texture); sf::Sprite top(texture); sf::Sprite right(texture); const float cubeSize = 64.f; // 将所有Sprite原点设为中心,简化变换与定位逻辑 front.setOrigin(cubeSize / 2, cubeSize / 2); top.setOrigin(cubeSize / 2, cubeSize / 2); right.setOrigin(cubeSize / 2, cubeSize / 2); // 预定义顶面和右侧面的固定变换(缩放→剪切,符合SFML右结合顺序) const sf::Transform topTransform = createShearTransform(-1.f, 0.f) * sf::Transform().scale(1.f, 0.75f); const sf::Transform rightTransform = createShearTransform(0.f, -1.f) * sf::Transform().scale(0.75f, 1.f); sf::RenderWindow window(sf::VideoMode(800, 800), "SFML Cube"); while (window.isOpen()) { sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } // 以鼠标位置作为立方体的中心基准点 sf::Vector2f cubeCenter = sf::Vector2f(sf::Mouse::getPosition(window)); window.clear(); // 绘制正面:直接定位到中心 front.setPosition(cubeCenter); window.draw(front); // 绘制顶面:基于中心向上偏移,应用预定义变换 top.setPosition(cubeCenter + sf::Vector2f(0.f, -cubeSize * 0.75f)); window.draw(top, topTransform); // 绘制右侧面:基于中心向右偏移,应用预定义变换 right.setPosition(cubeCenter + sf::Vector2f(cubeSize * 0.75f, 0.f)); window.draw(right, rightTransform); window.display(); } return 0; }
关键修正说明
- 原点设置:将Sprite原点设为纹理中心,避免变换时出现非预期偏移,让定位逻辑更直观。
- 变换顺序:SFML变换组合遵循右结合规则,正确顺序为
缩放 → 剪切,确保形变符合立方体面的透视逻辑。 - 统一基准点:所有面的位置都基于立方体中心(鼠标位置)计算偏移,保证整体移动时同步。
- 层级绘制:先绘制正面,再绘制顶面和右侧面,保证正确的遮挡关系。
内容的提问来源于stack exchange,提问作者Signup Thanks
相关产品推荐
相关产品推荐

