C SFML Как настроить вид игры с помощью движений мыши

#c #sfml

#c #sfml

Вопрос:

У меня есть окно SFML sf::view , и мне нужно перемещать вид вокруг моей игровой карты с помощью движения мыши.
Я хочу, чтобы это выглядело так, как будто игрок хватает и перемещает объект.
Например: myCube.setPosition(mousePos); но я не хочу перемещать объекты в моем игровом мире, вместо этого я хочу, чтобы он перемещал вид.

Моя попытка:

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

Ответ №1:

Если вы не читали о представлениях, хорошая страница, объясняющая их, находится здесь: 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). Я буду использовать класс, который хранит их и имеет handleEvent() функцию для моего примера. Я также сохраню цель рендеринга, в качестве альтернативы вы можете сохранить представление или передать цель рендеринга / представление в handleEvent() функцию.

 class ViewDragger {
public:
    /// set render target with view and initialize dragging to false
    ViewDragger(sf::RenderTargetamp; 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::RenderTargetamp; target;
    /// the last known mouse position
    sf::Vector2i previous_mouse_position;
    /// whether we are dragging or not
    bool dragging;
};
 

Давайте создадим экземпляр класса и вызовем функцию handleEvent . Я создам экземпляр класса после создания окна и вызову функцию 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;
 

Теперь мы собираемся рассчитать, как далеко мышь переместилась в представлении.

Это отличается от того, как далеко мышь переместилась в окне.
Мышь может перемещаться с (0, 0) на (10, 0) в окне, но это не означает, что она переместила 10 единиц в представлении. Если вид уже перемещен, то (0, 0) может быть любым, и если вид масштабируется, то 10 пикселей по горизонтали — это не 10 единиц вправо, и если вид повернут, это очень сложно понять.

К счастью, для нас уже существует функция перехода из оконного пространства в пространство просмотра : 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::RenderTargetamp; 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::RenderTargetamp; 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;
}
 

Ответ №2:

потрясающе, спасибо,. Идеальный. может быть, это слишком сложно для меня, особенно с автоматической функцией и использованием RenderTarget, но я разберусь в этом. Я добавляю реакцию только на RMB. Моя версия:

 class ViewDragger
{
private:
    sf::RenderTargetamp; RTwindow;
    sf::Vector2i previousMousePosition;

    bool dragging;
public:
    //get window at construct and initialize variables with initializer list
    ViewDragger(sf::RenderTargetamp; 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;
        }
    
    }
};
 

`