sfml投掷物围绕中心旋转与左右翻转

⌚Time: 2025-12-14 18:32:00

👨‍💻Author: Jack Ge

旋转

如果直接用setRotation旋转,得到的不是围绕中心的,而是围绕左上角旋转的结果


    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{
        if(m_attribute.RotateWithVelocity){
            // 计算角度(弧度)
            float angleRadians = std::atan2(m_gameWorldObject->speedv, m_gameWorldObject->speedh);
            // 转换为度数
            float angleDegrees = angleRadians * 180.0f / 3.14159265f;
            // 设置旋转 默认纹理图片都是0度向x轴正右方向
            m_rectangleShape->setRotation(angleDegrees);
        }
        //画
        target.draw(*m_rectangleShape, states);
        //画物理体积边界
        sf::RectangleShape bound;
        bound.setPosition((float)m_gameWorldObject->rect.left,(float)m_gameWorldObject->rect.top);//位置
        bound.setSize(sf::Vector2f((float)m_attribute.Width,(float)m_attribute.Height));//大小
        bound.setOutlineColor(sf::Color(255,1,221));//边界线颜色
        bound.setOutlineThickness(1.0);//边界线粗细
        bound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(bound);
        //画图形边界
        sf::RectangleShape abound;
        abound.setPosition(m_rectangleShape->getPosition());//位置
        abound.setSize(m_rectangleShape->getSize());//大小
        abound.setOutlineColor(sf::Color(55,221,221));//边界线颜色
        abound.setOutlineThickness(1.0);//边界线粗细
        abound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(abound);
    }

其中的白底图片是绘制的箭头纹理,粉色框代表的是箭头的物理体积,蓝色框代表的是箭头纹理图片的位置和大小。旋转之后是围绕左上角旋转的结果。

所以需要使用setOrigin设置中心


    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{
        if(m_attribute.RotateWithVelocity){
            m_rectangleShape->setOrigin(m_rectangleShape->getSize().x/2,m_rectangleShape->getSize().y/2);
            // 计算角度(弧度)
            float angleRadians = std::atan2(m_gameWorldObject->speedv, m_gameWorldObject->speedh);
            // 转换为度数
            float angleDegrees = angleRadians * 180.0f / 3.14159265f;
            // 设置旋转 默认纹理图片都是0度向x轴正右方向
            m_rectangleShape->setRotation(angleDegrees);
        }
        //画
        target.draw(*m_rectangleShape, states);
        //画物理体积边界
        sf::RectangleShape bound;
        bound.setPosition((float)m_gameWorldObject->rect.left,(float)m_gameWorldObject->rect.top);//位置
        bound.setSize(sf::Vector2f((float)m_attribute.Width,(float)m_attribute.Height));//大小
        bound.setOutlineColor(sf::Color(255,1,221));//边界线颜色
        bound.setOutlineThickness(1.0);//边界线粗细
        bound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(bound);
        //画图形边界
        sf::RectangleShape abound;
        abound.setPosition(m_rectangleShape->getPosition());//位置
        abound.setSize(m_rectangleShape->getSize());//大小
        abound.setOutlineColor(sf::Color(55,221,221));//边界线颜色
        abound.setOutlineThickness(1.0);//边界线粗细
        abound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(abound);
    }

但是因为中心被改变,不仅仅旋转围绕中心,连位置这些变换也是根据中心设定的,所以得到的是这种效果。蓝框代表的是绘制图片的位置,箭头图片需要与粉框代表的物理体积重合,但是实际上图片绘制在了左上偏离的地方。因为它不再以图片左上角作为位置根据,而是用中心作为位置根据

所以最后需要进行位置修正,让绘制偏离右下角与设置中心一样的距离,就能够重合了



    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{
        if(m_attribute.RotateWithVelocity){
            m_rectangleShape->setOrigin(m_rectangleShape->getSize().x/2,m_rectangleShape->getSize().y/2);
            // 计算角度(弧度)
            float angleRadians = std::atan2(m_gameWorldObject->speedv, m_gameWorldObject->speedh);
            // 转换为度数
            float angleDegrees = angleRadians * 180.0f / 3.14159265f;
            // 设置旋转 默认纹理图片都是0度向x轴正右方向
            m_rectangleShape->setRotation(angleDegrees);
            sf::Vector2f pos = m_rectangleShape->getPosition();
            pos.x += m_rectangleShape->getSize().x/2;
            pos.y += m_rectangleShape->getSize().y/2;
            m_rectangleShape->setPosition(pos);
            
        }
        //画
        target.draw(*m_rectangleShape, states);
        //画物理体积边界
        sf::RectangleShape bound;
        bound.setPosition((float)m_gameWorldObject->rect.left,(float)m_gameWorldObject->rect.top);//位置
        bound.setSize(sf::Vector2f((float)m_attribute.Width,(float)m_attribute.Height));//大小
        bound.setOutlineColor(sf::Color(255,1,221));//边界线颜色
        bound.setOutlineThickness(1.0);//边界线粗细
        bound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(bound);
        //画图形边界
        sf::RectangleShape abound;
        abound.setPosition(m_rectangleShape->getPosition());//位置
        abound.setSize(m_rectangleShape->getSize());//大小
        abound.setOutlineColor(sf::Color(55,221,221));//边界线颜色
        abound.setOutlineThickness(1.0);//边界线粗细
        abound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(abound);
    }

一开始我觉得,如果我旋转之前setOrigin设置了中心,之后恢复中心,是不是不影响位置。结果是错的。等于一开始的没有设置中心。依旧按照左上角旋转。



        if(m_attribute.RotateWithVelocity){
            m_rectangleShape->setOrigin(m_rectangleShape->getSize().x/2,m_rectangleShape->getSize().y/2);
            // 计算角度(弧度)
            float angleRadians = std::atan2(m_gameWorldObject->speedv, m_gameWorldObject->speedh);
            // 转换为度数
            float angleDegrees = angleRadians * 180.0f / 3.14159265f;
            // 设置旋转 默认纹理图片都是0度向x轴正右方向
            m_rectangleShape->setRotation(angleDegrees);
            //尝试恢复原点
            m_rectangleShape->setOrigin(0,0);
        }

左右翻转

上面的旋转代码能够处理左右朝向的问题都转换成了角度。对于不需要根据运动方向旋转的投掷物,只需要简单的根据左右朝向翻转一下

    virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const{
        if(m_attribute.RotateWithVelocity){
            m_rectangleShape->setOrigin(m_rectangleShape->getSize().x/2,m_rectangleShape->getSize().y/2);
            // 计算角度(弧度)
            float angleRadians = std::atan2(m_gameWorldObject->speedv, m_gameWorldObject->speedh);
            // 转换为度数
            float angleDegrees = angleRadians * 180.0f / 3.14159265f;
            // 设置旋转 默认纹理图片都是0度向x轴正右方向
            m_rectangleShape->setRotation(angleDegrees);
            sf::Vector2f pos = m_rectangleShape->getPosition();
            pos.x += m_rectangleShape->getSize().x/2;
            pos.y += m_rectangleShape->getSize().y/2;
            m_rectangleShape->setPosition(pos);
            
        }else if(m_gameWorldObject->speedh<0){//如果是向左速度,反转图像
            //设置原点
            m_rectangleShape->setOrigin(sf::Vector2f(m_rectangleShape->getLocalBounds().width, 0));
            //设置缩放
            m_rectangleShape->scale(-1.f, 1.f);
        }
        //画
        target.draw(*m_rectangleShape, states);
        //画物理体积边界
        sf::RectangleShape bound;
        bound.setPosition((float)m_gameWorldObject->rect.left,(float)m_gameWorldObject->rect.top);//位置
        bound.setSize(sf::Vector2f((float)m_attribute.Width,(float)m_attribute.Height));//大小
        bound.setOutlineColor(sf::Color(255,1,221));//边界线颜色
        bound.setOutlineThickness(1.0);//边界线粗细
        bound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(bound);
        //画图形边界
        sf::RectangleShape abound;
        abound.setPosition(m_rectangleShape->getPosition());//位置
        abound.setSize(m_rectangleShape->getSize());//大小
        abound.setOutlineColor(sf::Color(55,221,221));//边界线颜色
        abound.setOutlineThickness(1.0);//边界线粗细
        abound.setFillColor(sf::Color(255,255,255,0));//透明填充
        target.draw(abound);
        if(m_attribute.RotateWithVelocity){
            ;
        }else if(m_gameWorldObject->speedh<0){
            //恢复缩放
            m_rectangleShape->scale(-1.f, 1.f);
            //恢复原点
            m_rectangleShape->setOrigin(sf::Vector2f(0, 0));
        }
    }

总结

现在就是能够处理旋转和翻转两个场景。用的代码可能不是最优雅、正确的、官方的解决办法。但是能用。