У меня есть окно SFML с sf::view
, и мне нужно перемещать вид по моей игровой карте с помощью движения мыши.
Я хочу, чтобы выглядело так, как будто игрок берет и перемещает объект.
Например: 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). Я буду использовать класс, который хранит их и имеет функцию handleEvent()
для моего примера. Я также буду хранить цель рендеринга, в качестве альтернативы вы можете сохранить представление или передать цель/представление рендеринга в функцию 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;
};
Давайте создадим экземпляр класса и вызовем функцию 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::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, но я займусь этим. Я добавляю реакцию только на RMB. Моя версия:
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;
}
}
};
`