【问题标题】:Dragging screen in SFMLSFML 中的拖动屏幕
【发布时间】:2017-06-06 22:18:48
【问题描述】:

我正在使用 SFML 用 C++ 构建一个基本程序,它允许用户放大对象并拖动屏幕。我已经能够使屏幕移动,但是移动与鼠标完全不同步并且不流畅。有人对我如何重写代码以使屏幕随着鼠标正确移动有任何建议吗?

        Time time = milliseconds(5);
        x1 = Mouse::getPosition().x;
        y1 = Mouse::getPosition().y;

        if (Mouse::isButtonPressed(Mouse::Left))
        {
            std::cout << x1 - Mouse::getPosition().x << " " << y1 - Mouse::getPosition().y << std::endl;
            sleep(time);
            view.move((x1 - Mouse::getPosition().x)*2, (y1 - Mouse::getPosition().y)*2);
        }

【问题讨论】:

    标签: c++ graphics 2d sfml


    【解决方案1】:

    有多种不同的方法可以实际实现这一目标。最直接的一种可能是使用sf::Window::setView() 简单地更新您的窗口视图。

    您可以执行以下示例。尝试理解代码,而不仅仅是复制它。

    #include <SFML/Graphics.hpp>
    
    int main()
    {
        // Let's setup a window
        sf::RenderWindow window(sf::VideoMode(640, 480), "SFML View Transformation");
    
        // Create something simple to draw
        sf::Texture texture;
        texture.loadFromFile("background.jpg");
        sf::Sprite background(texture);
    
        sf::Vector2f oldPos;
        bool moving = false;
    
        float zoom = 1;
    
        // Retrieve the window's default view
        sf::View view = window.getDefaultView();
    
        while (window.isOpen()) {
            sf::Event event;
            while (window.pollEvent(event)) {
                switch (event.type) {
                    case sf::Event::Closed:
                        window.close();
                        break;
                    case sf::Event::MouseButtonPressed:
                        // Mouse button is pressed, get the position and set moving as active
                        if (event.mouseButton.button == 0) {
                            moving = true;
                            oldPos = window.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y));
                        }
                        break;
                    case  sf::Event::MouseButtonReleased:
                        // Mouse button is released, no longer move
                        if (event.mouseButton.button == 0) {
                            moving = false;
                        }
                        break;
                    case sf::Event::MouseMoved:
                        {
                            // Ignore mouse movement unless a button is pressed (see above)
                            if (!moving)
                                break;
                            // Determine the new position in world coordinates
                            const sf::Vector2f newPos = window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
                            // Determine how the cursor has moved
                            // Swap these to invert the movement direction
                            const sf::Vector2f deltaPos = oldPos - newPos;
    
                            // Move our view accordingly and update the window
                            view.setCenter(view.getCenter() + deltaPos);
                            window.setView(view);
    
                            // Save the new position as the old one
                            // We're recalculating this, since we've changed the view
                            oldPos = window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
                            break;
                        }
                    case sf::Event::MouseWheelScrolled:
                        // Ignore the mouse wheel unless we're not moving
                        if (moving)
                            break;
    
                        // Determine the scroll direction and adjust the zoom level
                        // Again, you can swap these to invert the direction
                        if (event.mouseWheelScroll.delta <= -1)
                            zoom = std::min(2.f, zoom + .1f);
                        else if (event.mouseWheelScroll.delta >= 1)
                            zoom = std::max(.5f, zoom - .1f);
    
                        // Update our view
                        view.setSize(window.getDefaultView().getSize()); // Reset the size
                        view.zoom(zoom); // Apply the zoom level (this transforms the view)
                        window.setView(view);
                        break;
                }
            }
    
            // Draw our simple scene
            window.clear(sf::Color::White);
            window.draw(background);
            window.display();
        }
    }
    

    还请注意,代码只会放大/缩小,不会向光标位置缩放,但这应该很容易添加(基本上只是像我为鼠标移动所做的那样移动视图)。

    【讨论】:

    • 请注意:报告为滚轮delta 的实际值可能取决于驱动程序和平台,并且可能需要进行一些测试,直到您认为它感觉正确(并且可以在任何地方按预期工作)。跨度>
    • 您好马里奥感谢您的详细回复。我从未使用过 mapPixelToCoords()。您能否简要解释一下该功能的作用或指导我查看有关它的一些详细文档。
    • 哈哈,我傻了 Lorence Hernandez 刚刚回答了这个问题。
    【解决方案2】:

    我一直在尝试类似的东西,我想改进@Mario's answer。

    事实上,使用mapPixelToCoords 最终会变得有些昂贵。这有点奇怪,因为mapPixelToCoords 只对坐标应用了一些矩阵运算,但问题是当我使用mapPixelToCoords 时,当缩放不是原始图像时,我的图像会像疯了一样抖动。

    在我的情况下,我更喜欢保留累积的缩放并将 deltaPos 乘以缩放级别。

    @Mario 代码的变化

    新变量accumZoom:
    double accumZoom = 1;
    
    按钮按下事件:
    case sf::Event::MouseButtonPressed:
        // Mouse button is pressed, get the position and set moving as active
        if (event.mouseButton.button == 0) {
            moving = true;
            oldPos = sf::Vector2f(sf::Mouse::getPosition(window)); // No call to mapPixelToCoords
        }
        break;
    
    鼠标移动事件:
    case sf::Event::MouseMoved:
    {
        // Ignore mouse movement unless a button is pressed (see above)
        if (!moving)
            break;
        // Determine the new position in world coordinates
        const sf::Vector2f newPos = sf::Vector2f(event.mouseMove.x, event.mouseMove.y);
        // Determine how the cursor has moved
        // Swap these to invert the movement direction
        const sf::Vector2f deltaPos = oldPos - newPos;
    
        // Applying zoom "reduction" (or "augmentation")
        deltaPos.x *= accumZoom;
        deltaPos.y *= accumZoom;
    
        // Move our view accordingly and update the window
        view.move(deltaPos); // <-- Here I use move
        window.setView(view);
    
        // Save the new position as the old one
        // We're recalculating this, since we've changed the view
        oldPos = newPos; // With move, I don't need to recalculate
        break;
    }
    
    鼠标滚轮滚动事件:
    case sf::Event::MouseWheelScrolled:
        // Ignore the mouse wheel unless we're not moving
        if (moving)
            break;
    
        // Determine the scroll direction and adjust the zoom level
        // Again, you can swap these to invert the direction
        if (event.mouseWheelScroll.delta <= -1)
            zoom = std::min(2.f, zoom + .1f);
        else if (event.mouseWheelScroll.delta >= 1)
            zoom = std::max(.5f, zoom - .1f);
    
        accumZoom *= zoom; // <-- accumulate zoom
        // Update our view
        view.setSize(window.getDefaultView().getSize()); // Reset the size
        view.zoom(zoom); // Apply the zoom level (this transforms the view)
        window.setView(view);
        break;
    

    【讨论】:

    • 认为我刚刚遇到了这个问题。它之所以如此疯狂地摇晃是因为视图的移动导致鼠标的坐标移动,从而导致视图移动,从而导致鼠标的坐标移动,等等......
    【解决方案3】:

    (因为我没有足够的声誉,所以回复了答案而不是评论)

    您是否尝试过删除sleep 和cout?我很确定是这两个原因造成的

    【讨论】:

    • cout 是否在场并没有太大影响。我把睡眠放在那里,因为我需要获得初始鼠标坐标和最终鼠标坐标,然后减去它们以获得偏移量。如果没有睡眠,偏移量将为 0。
    • 如果你愿意,我可以发布我的完整代码,这样你就可以更好地了解我想要做什么。
    【解决方案4】:

    当您使用自定义视图或调整窗口大小时,像素 显示在目标上不再匹配 2D 世界中的单位。为了 例如,点击像素 (10, 50) 可能会碰到点 (26.5, -84) 你的世界。您最终不得不使用转换函数来映射您的 像素坐标到世界坐标:mapPixelToCoords。

    // get the current mouse position in the window
    sf::Vector2i pixelPos = sf::Mouse::getPosition(window);
    
    // convert it to world coordinates
    sf::Vector2f worldPos = window.mapPixelToCoords(pixelPos);
    

    默认情况下,mapPixelToCoords 使用当前视图。如果你想 使用不是活动视图的视图转换坐标,您 可以将其作为附加参数传递给函数。

    相反,将世界坐标转换为像素坐标,是 也可以使用 mapCoordsToPixel 函数。

    您可以在页面底部看到它:https://www.sfml-dev.org/tutorials/2.0/graphics-view.php

    【讨论】:

      【解决方案5】:

      正如用户 3881815 所暗示的,问题出在您的 sleep(time) 上,因为它不能保证会在 time [毫秒] 内休眠。相反,可以添加任何计数的 OS 调度粒度间隔,从而导致波动。更不用说它会阻止执行......而且你不是通过鼠标变化而是按一定比例移动(所以它不同步也就不足为奇了)。那么如何摆脱呢?

      您需要记住上次鼠标的位置,并在处理循环中每次运行时使用该位置。例如:

      double mx=0,my=0,mx0=0,my0=0; // globals with actual and last mouse position
      
      // here your main loop or event or whatever
      for (bool exit=false;!exit;)
       {
       // here your other stuff
      
       // mouse handler
       mx0=mx; mx = Mouse::getPosition().x;
       my0=my; my = Mouse::getPosition().y;
       // here convert mx,my to coordinate system the view.move needs
       if (Mouse::isButtonPressed(Mouse::Left)) view.move(mx-mx0,my-my0);
       }
      

      如果您的视图使用缩放,那么您很可能需要对mx,my 应用坐标变换,但这取决于view.move 想要什么。

      我不使用 SFML 编码,所以我对您的观点没有经验,但您可以尝试

      view.move((mx-mx0)*view.zoom,(my-my0)*view.zoom);
      

      或

      view.move((mx-mx0)/view.zoom,(my-my0)/view.zoom);
      

      使移动与鼠标同步,view.zoom 是您的视图比例。两者中的哪一个取决于view 符号。

      为了更好地处理鼠标,您还应该拥有按钮的最后和实际状态,以便您可以处理 mousedown、mouseup、mousemove 事件。欲了解更多信息,请参阅:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-09
        • 1970-01-01
        相关资源
        最近更新 更多