#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;
}
}
};
`