C++ SFML 如何通过鼠标移动调整游戏视图

C++ SFML How to adjust game view via mouse movements

我有一个带 sf::view 的 SFML window,我需要通过鼠标移动来围绕我的游戏地图移动视图。
我想让它看起来好像玩家正在抓取并移动物体。
例如:myCube.setPosition(mousePos); 但是我不想移动游戏世界中的对象,而是希望它移动视图。

我的尝试:

view.setCenter(sf::Mouse::getPosition(window).x, sf::Mouse::getPosition(window).y);

如果您还没有阅读有关视图的信息,请访问此处的一个很好的页面来解释视图:https://www.sfml-dev.org/tutorials/2.5/graphics-view.php

我的做法是使用与拖动相关的三个鼠标事件(sf::Event::MouseButtonPressed、sf::Event::MouseButtonReleased 和 sf::Event::MouseMoved),并在拖动时更新视图根据鼠标移动的距离。
事件解释如下:https://www.sfml-dev.org/tutorials/2.5/window-events.php

我认为你需要存储你是否正在拖动(所以,一个布尔值),以及之前的鼠标位置(sf::Vector2i)。我将使用一个 class 来存储这些,并为我的示例提供一个 handleEvent() 函数。我还将存储渲染目标,或者您可以存储视图或将渲染 target/view 传递到 handleEvent() 函数中。

class ViewDragger {
public:
    /// set render target with view and initialize dragging to false
    ViewDragger(sf::RenderTarget& target) :
        target{target},
        dragging{}
    {}
    /// handle dragging related events
    void handleEvent(const sf::Event event) {
        // todo...
    }
private:
    /// the render target with the view we want to change
    sf::RenderTarget& target;
    /// the last known mouse position
    sf::Vector2i previous_mouse_position;
    /// whether we are dragging or not
    bool dragging;
};

让我们实例化 class 并调用 handleEvent 函数。我将在创建 window 之后实例化 class,并在事件循环底部调用 handleEvent 函数。

ViewDragger view_dragger{ window };
...
while (window.pollEvent(event)) {
    ...
    view_dragger.handleEvent(event);
}

最后我们将编写移动视图的函数。 先看看是不是拖动吧

void handleEvent(const sf::Event event) {
    switch (event.type) {
    // if mouse button is pressed start dragging
    case sf::Event::MouseButtonPressed:
        dragging = true;
        break;
    // if mouse button is released stop draggin
    case sf::Event::MouseButtonReleased:
        dragging = false;
        break;
    }
}

现在我们知道我们是否在拖动,让我们在我们刚刚创建的两个拖动视图的下方创建第三个事件。

首先我们将编写更新先前鼠标位置的代码。

// if dragging mouse
case sf::Event::MouseMoved:
    // get mouse position
    const sf::Vector2i mouse_position{
        event.mouseMove.x, event.mouseMove.y
    };
    // if dragging, move view
    if (dragging) {
        // todo...
    }
    // update previous mouse position
    previous_mouse_position = mouse_position;
    break;

现在我们要计算鼠标在视图中移动了多远。

这与鼠标在 window 中移动的距离不同。
鼠标可能在 window 中从 (0, 0) 移动到 (10, 0),但这并不意味着它在视图中移动了 10 个单位。如果视图已经移动,则 (0, 0) 可以是任何值,如果缩放视图,则水平方向 10 个像素不是向右 10 个单位,如果旋转视图,则很难弄清楚.

幸运的是,我们已经有一个功能可以让我们从 window space 更改为查看 space: sf::RenderTarget::mapPixelToCoords().

我们将把它与当前鼠标位置和先前鼠标位置一起使用。

// calculate how far mouse has moved in view
const auto delta = 
    target.mapPixelToCoords(mouse_position) -
    target.mapPixelToCoords(previous_mouse_position);

最后我们需要将它消极地应用到视图中。

因此,如果我们将鼠标向右移动十个单位,我们希望视图向左移动 10 个单位。

// apply negatively to view
auto view = target.getView();
view.move(-delta);
target.setView(view);

应该就是了!

完整代码:

#include <SFML/Graphics.hpp>

class ViewDragger {
public:
    /// set render target with view and initialize dragging to false
    ViewDragger(sf::RenderTarget& target) :
        target{ target },
        dragging{}
    {}
    /// handle dragging related events
    void handleEvent(const sf::Event event) {
        switch (event.type) {
        // if mouse button is pressed start dragging
        case sf::Event::MouseButtonPressed:
            dragging = true;
            break;
        // if mouse button is released stop draggin
        case sf::Event::MouseButtonReleased:
            dragging = false;
            break;
        // if dragging mouse
        case sf::Event::MouseMoved:
            // get mouse position
            const sf::Vector2i mouse_position{
                event.mouseMove.x, event.mouseMove.y
            };
            // if dragging, move view
            if (dragging) {
                // calculate how far mouse has moved in view
                const auto delta = 
                    target.mapPixelToCoords(mouse_position) -
                    target.mapPixelToCoords(previous_mouse_position);
                // apply negatively to view
                auto view = target.getView();
                view.move(-delta);
                target.setView(view);
            }
            // update previous mouse position
            previous_mouse_position = mouse_position;
            break;
        }
    }
private:
    /// the render target with the view we want to change
    sf::RenderTarget& target;
    /// the last known mouse position
    sf::Vector2i previous_mouse_position;
    /// whether we are dragging or not
    bool dragging;
};

int main() {
    sf::RenderWindow window{ sf::VideoMode(200, 200), "View Dragging!" };
    ViewDragger view_dragger{ window };

    sf::CircleShape shape{ 100.f };
    shape.setFillColor(sf::Color::Green);

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

        window.clear();
        window.draw(shape);
        window.display();
    }

    return EXIT_SUCCESS;
}

太棒了,谢谢。完美的。也许对我来说有点太复杂了,尤其是自动功能和 renderTarget 的使用,但我会深入研究它。我只对人民币加反应。 我的版本:

class ViewDragger
{
private:
    sf::RenderTarget& RTwindow;
    sf::Vector2i previousMousePosition;

    bool dragging;
public:
    //get window at construct and initialize variables with initializer list
    ViewDragger(sf::RenderTarget& RTwindow)
        : RTwindow{RTwindow}, dragging{}
    {
    }
    //use after window.pollEvent(event)) to get event handler
    void handleEvent(const sf::Event event)
    {
        switch (event.type)
        {
        case sf::Event::MouseButtonPressed:
            if (event.mouseButton.button == sf::Mouse::Right)
            {
                //start dragging
                dragging = true;
            }
            break;
        case sf::Event::MouseButtonReleased:
            if (event.mouseButton.button == sf::Mouse::Right)
            {
                //stop dragging
                dragging = false;
            }
            break;
        case sf::Event::MouseMoved:
            //get new mouse position
            const sf::Vector2i mousePosition(event.mouseMove.x, event.mouseMove.y);
            if (dragging)
            {
                //if mouse is dragging, count difference between new mouse position and old mouse position 
                //example: mouse move down by x100: new(x400,.y300) - (x300,y300) = x100 and for opposite direction make it -x100
                const sf::Vector2f delta = RTwindow.mapPixelToCoords(mousePosition) - RTwindow.mapPixelToCoords(previousMousePosition);
                sf::View view = RTwindow.getView();
                view.move(-delta);
                //update view
                RTwindow.setView(view);

            }
            //save current mouse position as old mouse position for next run
            previousMousePosition = mousePosition;
            break;
        }
    
    }
};

`