Reputation: 25
I have a SFML window with sf::view
and I need to move the view around my game map via mouse movement.
I want it to look as if the player is grabbing and moving the object.
For example: myCube.setPosition(mousePos);
But I do not want to move the objects in my game world, instead I want it to move the view.
My attempt:
view.setCenter(sf::Mouse::getPosition(window).x, sf::Mouse::getPosition(window).y);
Upvotes: 0
Views: 955
Reputation: 25
awesome, thanks,. perfect. maybe a bit too complicated to me, especially with auto function and usage of renderTarget but I will get into it. I add reaction only to RMB. My version:
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;
}
}
};
`
Upvotes: 0
Reputation: 1397
Incase you have not read about views a good page explaining them is here: https://www.sfml-dev.org/tutorials/2.5/graphics-view.php
The way I would do it would be using the three mouse events involved with dragging (sf::Event::MouseButtonPressed, sf::Event::MouseButtonReleased and sf::Event::MouseMoved), and whilst dragging, update the view based on how far the mouse has travelled.
Events are explained here: https://www.sfml-dev.org/tutorials/2.5/window-events.php
I think you need to store whether you are dragging or not (so, a boolean), and the previous mouse position (sf::Vector2i). I will use a class that stores these and has a handleEvent()
function for my example. I will also store a render target, alternatively you could store a view or pass in a render target/view into the handleEvent()
function.
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;
};
Let us instantiate the class and call the handleEvent function. I will instantiate the class after I have created the window, and call the handleEvent function at the bottom of the event loop.
ViewDragger view_dragger{ window };
...
while (window.pollEvent(event)) {
...
view_dragger.handleEvent(event);
}
Finally we will write the function that moves the view. Let's figure out if we are dragging or not first.
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;
}
}
Now that we know if we are dragging or not, let us create a third event underneath the two we just made that drags the view.
First we will write the code that will update the previous mouse position.
// 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;
Now we are going to calculate the how far the mouse has moved in the view.
This is different to how far the mouse has moved in the window.
The mouse might move from (0, 0) to (10, 0) in the window, but that does not mean it moved 10 units in the view. If the view is already moved, then (0, 0) could be anything, and if the view is scaled, then 10 pixels across horizontally is not 10 units towards the right, and if the view is rotated it is super hard to figure out.
Luckily a function already exists for us to change from window space to view space: sf::RenderTarget::mapPixelToCoords()
.
We will use that with the current mouse position and the previous mouse position.
// calculate how far mouse has moved in view
const auto delta =
target.mapPixelToCoords(mouse_position) -
target.mapPixelToCoords(previous_mouse_position);
Finally we need to apply it negatively to the view.
So if we moved the mouse ten units to the right, we want the view to move 10 units to the left.
// apply negatively to view
auto view = target.getView();
view.move(-delta);
target.setView(view);
That should be it!
Full code:
#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;
}
Upvotes: 1