在 SFML 中拖动屏幕

Dragging screen in SFML

我正在使用 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);
        }

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

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

When you use a custom view, or when you resize the window, pixels displayed on the target no longer match units in the 2D world. For example, clicking on pixel (10, 50) may hit the point (26.5, -84) of your world. You end up having to use a conversion function to map your pixel coordinates to world coordinates: 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);

By default, mapPixelToCoords uses the current view. If you want to convert the coordinates using view which is not the active one, you can pass it as an additional argument to the function.

The opposite, converting world coordinates to pixel coordinates, is also possible with the mapCoordsToPixel function.

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

as user3881815 建议问题出在您的 sleep(time) 因为它不能保证会休眠 time [ms]。相反,可以添加任何 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 事件。有关详细信息,请参阅:

  • Does anyone know of a low level (no frameworks) example of a drag & drop?

有多种不同的方法可以实际实现这一点。最直接的可能是使用 sf::Window::setView().

简单地更新 window 的视图

您可以执行类似以下示例的操作。尝试理解代码而不是仅仅复制它。

#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();
    }
}

另请注意,代码只会缩放 in/out,它不会缩放到您的光标位置,但添加起来应该很简单(基本上只是移动视图,类似于我所做的鼠标移动)。

我一直在尝试类似的东西,我想改进

事实上,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;