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