在SFML中分组对象的最佳方法是什么?

时间:2018-04-25 21:52:17

标签: c++ sfml

如果我在SFML中有3个不同的形状,并且我想将它们全部相对于单个中心旋转,就好像这3个形状在一个正方形内,那么最好的方法是什么? 它会将它们留在视图中并旋转视图吗? 还是有更实用的方法吗?

2 个答案:

答案 0 :(得分:6)

虽然我即将提出的方法不是那么简单,但在某种程度上反直觉,但功能更强大,每个渲染引擎都以这种或那种形式使用它。 大多数渲染和游戏引擎,如 Ogre3d 虚幻引擎(实际上,它的渲染引擎)使用诸如场景图之类的东西来分组对象和关系坐标绘制它们的系统,如果你想深入研究计算机图形学,你需要习惯这些东西。

让我们想象一下,你想编写一个程序,可以看到太阳周围地球和月球的旋转。 假设,太阳是中心,不动。 地球围绕着太阳,这很容易实现。 但也有月球围绕地球旋转,其产生的轨迹非常难以计算,我甚至没有提到行星的旋转不是完美的圆圈。 如果我们有另一个围绕月亮的鹅卵石怎么办? 这将是一个混乱。

人类提出的解决方案是从太阳的旋转中抽象月亮的旋转。 月亮围绕地球旋转,对吗? 因此,诀窍是将月亮的坐标系与地球严格绑定,并实现月球的旋转,就像太阳不存在一样。 另一个技巧是将地球的坐标系与太阳刚刚结合,并实现地球的旋转,就像月球和其他宇宙不存在一样。 如您所见,对象有明确的层次结构:Sun-> Earth-> Moon。 在计算机图形学方面,Sun是对象,地球和地球是Sun的对象。 地球和月球也是如此:地球是对象,月球和月球是地球的 child 对象。 整个系统称为场景图

不幸的是,与渲染引擎不同, SFML 不提供场景图的现成类,但它为我们提供了实现它的工具。 这是我的实施:

//SceneNode.h
class SceneNode :
    public sf::Transformable,
    public sf::Drawable,
    private sf::NonCopyable {
public:
    typedef std::unique_ptr<SceneNode>          Ptr;
    typedef std::pair<SceneNode*, SceneNode*>   Pair;

    explicit                SceneNode();

    void                    AttachChild( Ptr child );
    Ptr                     DetachChild( const SceneNode& node );

private:
    virtual void        draw(
        sf::RenderTarget& target,
        sf::RenderStates states
    ) const;
    virtual void        DrawCurrent(
        sf::RenderTarget& target,
        sf::RenderStates states
    ) const;
    void                DrawChildren(
        sf::RenderTarget& target,
        sf::RenderStates states
    ) const;

    std::vector<Ptr>    mChildren;
    SceneNode*          mParent;
};

//SceneNode.cpp
SceneNode::SceneNode( Category::Type category ) :
mChildren(),
mParent( nullptr ) {}

void
SceneNode::AttachChild( Ptr child ) {
    child->mParent = this;
    mChildren.push_back( std::move( child ) );
}

SceneNode::Ptr
SceneNode::DetachChild( const SceneNode& node ) {
    auto found = std::find_if(
        mChildren.begin(),
        mChildren.end(),
        [&] ( Ptr& p ) { return p.get() == &node; }
    );
    assert( found != mChildren.end() );

    Ptr result      = std::move( *found );
    result->mParent = nullptr;
    mChildren.erase( found );
    return result;
}

void
SceneNode::draw( sf::RenderTarget& target, sf::RenderStates states ) const {
    states.transform *= getTransform();

    DrawCurrent( target, states );
    DrawChildren( target, states );
}

void
SceneNode::DrawCurrent(
    sf::RenderTarget& target,
    sf::RenderStates states
) const {}

void
SceneNode::DrawChildren(
    sf::RenderTarget& target,
    sf::RenderStates states
) const {
    for ( const Ptr& child : mChildren ) {
        child->draw( target, states );
    }
}

任何SceneNode都可以像绘制一个矩形一样简单,但在绘制之后,如果您不忘记实现SceneNode方法,SceneNode::DrawCurrent将绘制其所有子节点。

答案 1 :(得分:0)

显然,对于我所见过的所有内容,最简单,最直观的方法是将对象放在视图中。

例如:

#include <SFML/Graphics.hpp>

#define windowWidth  1000
#define windowHeight 600
using namespace sf;

int main()
{
    RenderWindow window(VideoMode(windowWidth, windowHeight), "SFML Views");

    View view(FloatRect(0,0, windowWidth, windowHeight)); // container
    view.zoom(2); // half size to turn transformations visible

    RectangleShape background (Vector2f(windowWidth, windowHeight)); // draw a full rectangle to show the container dimensions
    background.setFillColor(Color::White);

    RectangleShape r1 (Vector2f(100,100)); // 1st object
    r1.setFillColor(Color::Red);
    r1.setPosition(Vector2f(30,10));


    RectangleShape r2 (Vector2f(150,200)); // 2nd object
    r2.setFillColor(Color::Blue);
    r2.setPosition(Vector2f(120,160));


    while (window.isOpen())
    {
        Event event;
        while (window.pollEvent(event))
        {
            if (event.type == Event::Closed)
                window.close();
        }

        window.clear();
        window.setView(view);
        window.draw(background);
        window.draw(r1);
        window.draw(r2);
        view.setRotation(30); // affects all elements

        window.display();
    }

    return 0;
}

Here is result