在 SFML 中拖动屏幕
Dragging screen in SFML
我正在使用 SFML 在 C++ 中构建一个基本程序,它允许用户放大对象并四处拖动屏幕。我已经能够使屏幕移动,但是移动与鼠标完全不同步并且不流畅。有没有人对我如何重写我的代码以便屏幕随鼠标正确移动有任何建议?
Time time = milliseconds(5);
x1 = Mouse::getPosition().x;
y1 = Mouse::getPosition().y;
if (Mouse::isButtonPressed(Mouse::Left))
{
std::cout << x1 - Mouse::getPosition().x << " " << y1 - Mouse::getPosition().y << std::endl;
sleep(time);
view.move((x1 - Mouse::getPosition().x)*2, (y1 - Mouse::getPosition().y)*2);
}
(回复一个答案而不是评论,因为我没有足够的声誉)
您是否尝试删除 sleep
和 cout
?我很确定是这两个原因造成的
When you use a custom view, or when you resize the window, pixels
displayed on the target no longer match units in the 2D world. For
example, clicking on pixel (10, 50) may hit the point (26.5, -84) of
your world. You end up having to use a conversion function to map your
pixel coordinates to world coordinates: mapPixelToCoords.
// get the current mouse position in the window
sf::Vector2i pixelPos = sf::Mouse::getPosition(window);
// convert it to world coordinates
sf::Vector2f worldPos = window.mapPixelToCoords(pixelPos);
By default, mapPixelToCoords uses the current view. If you want to
convert the coordinates using view which is not the active one, you
can pass it as an additional argument to the function.
The opposite, converting world coordinates to pixel coordinates, is
also possible with the mapCoordsToPixel function.
您可以在页面底部看到它:https://www.sfml-dev.org/tutorials/2.0/graphics-view.php
as user3881815 建议问题出在您的 sleep(
time)
因为它不能保证会休眠 time
[ms]。相反,可以添加任何 OS 调度粒度间隔的计数,从而导致波动。更不用说它会阻止执行......而且你不是通过鼠标变化而是通过某种比例移动(所以它不同步也就不足为奇了)。那么如何摆脱它呢?
您需要记住鼠标的最后位置并在处理循环中每隔 运行 使用一次。例如:
double mx=0,my=0,mx0=0,my0=0; // globals with actual and last mouse position
// here your main loop or event or whatever
for (bool exit=false;!exit;)
{
// here your other stuff
// mouse handler
mx0=mx; mx = Mouse::getPosition().x;
my0=my; my = Mouse::getPosition().y;
// here convert mx,my to coordinate system the view.move needs
if (Mouse::isButtonPressed(Mouse::Left)) view.move(mx-mx0,my-my0);
}
如果您的视图使用缩放,那么您很可能需要对 mx,my
应用坐标变换,但这取决于 view.move
想要什么。
我不会用 SFML 编写代码,所以我对你的观点没有经验,但你可以试试
view.move((mx-mx0)*view.zoom,(my-my0)*view.zoom);
或
view.move((mx-mx0)/view.zoom,(my-my0)/view.zoom);
使移动与您的鼠标同步,其中 view.zoom
是您的视图比例。两者中的哪一个取决于 view
表示法。
为了更好地处理鼠标,您还应该拥有按钮的最后和实际状态,以便您可以处理 mousedown、mouseup、mousemove 事件。有关详细信息,请参阅:
- Does anyone know of a low level (no frameworks) example of a drag & drop?
有多种不同的方法可以实际实现这一点。最直接的可能是使用 sf::Window::setView()
.
简单地更新 window 的视图
您可以执行类似以下示例的操作。尝试理解代码而不是仅仅复制它。
#include <SFML/Graphics.hpp>
int main()
{
// Let's setup a window
sf::RenderWindow window(sf::VideoMode(640, 480), "SFML View Transformation");
// Create something simple to draw
sf::Texture texture;
texture.loadFromFile("background.jpg");
sf::Sprite background(texture);
sf::Vector2f oldPos;
bool moving = false;
float zoom = 1;
// Retrieve the window's default view
sf::View view = window.getDefaultView();
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::MouseButtonPressed:
// Mouse button is pressed, get the position and set moving as active
if (event.mouseButton.button == 0) {
moving = true;
oldPos = window.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y));
}
break;
case sf::Event::MouseButtonReleased:
// Mouse button is released, no longer move
if (event.mouseButton.button == 0) {
moving = false;
}
break;
case sf::Event::MouseMoved:
{
// Ignore mouse movement unless a button is pressed (see above)
if (!moving)
break;
// Determine the new position in world coordinates
const sf::Vector2f newPos = window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
// Determine how the cursor has moved
// Swap these to invert the movement direction
const sf::Vector2f deltaPos = oldPos - newPos;
// Move our view accordingly and update the window
view.setCenter(view.getCenter() + deltaPos);
window.setView(view);
// Save the new position as the old one
// We're recalculating this, since we've changed the view
oldPos = window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
break;
}
case sf::Event::MouseWheelScrolled:
// Ignore the mouse wheel unless we're not moving
if (moving)
break;
// Determine the scroll direction and adjust the zoom level
// Again, you can swap these to invert the direction
if (event.mouseWheelScroll.delta <= -1)
zoom = std::min(2.f, zoom + .1f);
else if (event.mouseWheelScroll.delta >= 1)
zoom = std::max(.5f, zoom - .1f);
// Update our view
view.setSize(window.getDefaultView().getSize()); // Reset the size
view.zoom(zoom); // Apply the zoom level (this transforms the view)
window.setView(view);
break;
}
}
// Draw our simple scene
window.clear(sf::Color::White);
window.draw(background);
window.display();
}
}
另请注意,代码只会缩放 in/out,它不会缩放到您的光标位置,但添加起来应该很简单(基本上只是移动视图,类似于我所做的鼠标移动)。
我一直在尝试类似的东西,我想改进 。
事实上,mapPixelToCoords
的使用在某种程度上是昂贵的。这有点奇怪,因为 mapPixelToCoords
只对坐标应用一些矩阵运算,但问题是当我使用 mapPixelToCoords
时,当缩放不是原始缩放时,我的图像像疯了似的抖动。
在我的例子中,我更喜欢保留累积缩放并将 deltaPos
乘以缩放级别。
来自@Mario 代码的更改
新变量 accumZoom
:
double accumZoom = 1;
按钮按下事件:
case sf::Event::MouseButtonPressed:
// Mouse button is pressed, get the position and set moving as active
if (event.mouseButton.button == 0) {
moving = true;
oldPos = sf::Vector2f(sf::Mouse::getPosition(window)); // No call to mapPixelToCoords
}
break;
鼠标移动事件:
case sf::Event::MouseMoved:
{
// Ignore mouse movement unless a button is pressed (see above)
if (!moving)
break;
// Determine the new position in world coordinates
const sf::Vector2f newPos = sf::Vector2f(event.mouseMove.x, event.mouseMove.y);
// Determine how the cursor has moved
// Swap these to invert the movement direction
const sf::Vector2f deltaPos = oldPos - newPos;
// Applying zoom "reduction" (or "augmentation")
deltaPos.x *= accumZoom;
deltaPos.y *= accumZoom;
// Move our view accordingly and update the window
view.move(deltaPos); // <-- Here I use move
window.setView(view);
// Save the new position as the old one
// We're recalculating this, since we've changed the view
oldPos = newPos; // With move, I don't need to recalculate
break;
}
鼠标滚轮滚动事件:
case sf::Event::MouseWheelScrolled:
// Ignore the mouse wheel unless we're not moving
if (moving)
break;
// Determine the scroll direction and adjust the zoom level
// Again, you can swap these to invert the direction
if (event.mouseWheelScroll.delta <= -1)
zoom = std::min(2.f, zoom + .1f);
else if (event.mouseWheelScroll.delta >= 1)
zoom = std::max(.5f, zoom - .1f);
accumZoom *= zoom; // <-- accumulate zoom
// Update our view
view.setSize(window.getDefaultView().getSize()); // Reset the size
view.zoom(zoom); // Apply the zoom level (this transforms the view)
window.setView(view);
break;
我正在使用 SFML 在 C++ 中构建一个基本程序,它允许用户放大对象并四处拖动屏幕。我已经能够使屏幕移动,但是移动与鼠标完全不同步并且不流畅。有没有人对我如何重写我的代码以便屏幕随鼠标正确移动有任何建议?
Time time = milliseconds(5);
x1 = Mouse::getPosition().x;
y1 = Mouse::getPosition().y;
if (Mouse::isButtonPressed(Mouse::Left))
{
std::cout << x1 - Mouse::getPosition().x << " " << y1 - Mouse::getPosition().y << std::endl;
sleep(time);
view.move((x1 - Mouse::getPosition().x)*2, (y1 - Mouse::getPosition().y)*2);
}
(回复一个答案而不是评论,因为我没有足够的声誉)
您是否尝试删除 sleep
和 cout
?我很确定是这两个原因造成的
When you use a custom view, or when you resize the window, pixels displayed on the target no longer match units in the 2D world. For example, clicking on pixel (10, 50) may hit the point (26.5, -84) of your world. You end up having to use a conversion function to map your pixel coordinates to world coordinates: mapPixelToCoords.
// get the current mouse position in the window
sf::Vector2i pixelPos = sf::Mouse::getPosition(window);
// convert it to world coordinates
sf::Vector2f worldPos = window.mapPixelToCoords(pixelPos);
By default, mapPixelToCoords uses the current view. If you want to convert the coordinates using view which is not the active one, you can pass it as an additional argument to the function.
The opposite, converting world coordinates to pixel coordinates, is also possible with the mapCoordsToPixel function.
您可以在页面底部看到它:https://www.sfml-dev.org/tutorials/2.0/graphics-view.php
as user3881815 建议问题出在您的 sleep(
time)
因为它不能保证会休眠 time
[ms]。相反,可以添加任何 OS 调度粒度间隔的计数,从而导致波动。更不用说它会阻止执行......而且你不是通过鼠标变化而是通过某种比例移动(所以它不同步也就不足为奇了)。那么如何摆脱它呢?
您需要记住鼠标的最后位置并在处理循环中每隔 运行 使用一次。例如:
double mx=0,my=0,mx0=0,my0=0; // globals with actual and last mouse position
// here your main loop or event or whatever
for (bool exit=false;!exit;)
{
// here your other stuff
// mouse handler
mx0=mx; mx = Mouse::getPosition().x;
my0=my; my = Mouse::getPosition().y;
// here convert mx,my to coordinate system the view.move needs
if (Mouse::isButtonPressed(Mouse::Left)) view.move(mx-mx0,my-my0);
}
如果您的视图使用缩放,那么您很可能需要对 mx,my
应用坐标变换,但这取决于 view.move
想要什么。
我不会用 SFML 编写代码,所以我对你的观点没有经验,但你可以试试
view.move((mx-mx0)*view.zoom,(my-my0)*view.zoom);
或
view.move((mx-mx0)/view.zoom,(my-my0)/view.zoom);
使移动与您的鼠标同步,其中 view.zoom
是您的视图比例。两者中的哪一个取决于 view
表示法。
为了更好地处理鼠标,您还应该拥有按钮的最后和实际状态,以便您可以处理 mousedown、mouseup、mousemove 事件。有关详细信息,请参阅:
- Does anyone know of a low level (no frameworks) example of a drag & drop?
有多种不同的方法可以实际实现这一点。最直接的可能是使用 sf::Window::setView()
.
您可以执行类似以下示例的操作。尝试理解代码而不是仅仅复制它。
#include <SFML/Graphics.hpp>
int main()
{
// Let's setup a window
sf::RenderWindow window(sf::VideoMode(640, 480), "SFML View Transformation");
// Create something simple to draw
sf::Texture texture;
texture.loadFromFile("background.jpg");
sf::Sprite background(texture);
sf::Vector2f oldPos;
bool moving = false;
float zoom = 1;
// Retrieve the window's default view
sf::View view = window.getDefaultView();
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
case sf::Event::MouseButtonPressed:
// Mouse button is pressed, get the position and set moving as active
if (event.mouseButton.button == 0) {
moving = true;
oldPos = window.mapPixelToCoords(sf::Vector2i(event.mouseButton.x, event.mouseButton.y));
}
break;
case sf::Event::MouseButtonReleased:
// Mouse button is released, no longer move
if (event.mouseButton.button == 0) {
moving = false;
}
break;
case sf::Event::MouseMoved:
{
// Ignore mouse movement unless a button is pressed (see above)
if (!moving)
break;
// Determine the new position in world coordinates
const sf::Vector2f newPos = window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
// Determine how the cursor has moved
// Swap these to invert the movement direction
const sf::Vector2f deltaPos = oldPos - newPos;
// Move our view accordingly and update the window
view.setCenter(view.getCenter() + deltaPos);
window.setView(view);
// Save the new position as the old one
// We're recalculating this, since we've changed the view
oldPos = window.mapPixelToCoords(sf::Vector2i(event.mouseMove.x, event.mouseMove.y));
break;
}
case sf::Event::MouseWheelScrolled:
// Ignore the mouse wheel unless we're not moving
if (moving)
break;
// Determine the scroll direction and adjust the zoom level
// Again, you can swap these to invert the direction
if (event.mouseWheelScroll.delta <= -1)
zoom = std::min(2.f, zoom + .1f);
else if (event.mouseWheelScroll.delta >= 1)
zoom = std::max(.5f, zoom - .1f);
// Update our view
view.setSize(window.getDefaultView().getSize()); // Reset the size
view.zoom(zoom); // Apply the zoom level (this transforms the view)
window.setView(view);
break;
}
}
// Draw our simple scene
window.clear(sf::Color::White);
window.draw(background);
window.display();
}
}
另请注意,代码只会缩放 in/out,它不会缩放到您的光标位置,但添加起来应该很简单(基本上只是移动视图,类似于我所做的鼠标移动)。
我一直在尝试类似的东西,我想改进
事实上,mapPixelToCoords
的使用在某种程度上是昂贵的。这有点奇怪,因为 mapPixelToCoords
只对坐标应用一些矩阵运算,但问题是当我使用 mapPixelToCoords
时,当缩放不是原始缩放时,我的图像像疯了似的抖动。
在我的例子中,我更喜欢保留累积缩放并将 deltaPos
乘以缩放级别。
来自@Mario 代码的更改
新变量accumZoom
:
double accumZoom = 1;
按钮按下事件:
case sf::Event::MouseButtonPressed:
// Mouse button is pressed, get the position and set moving as active
if (event.mouseButton.button == 0) {
moving = true;
oldPos = sf::Vector2f(sf::Mouse::getPosition(window)); // No call to mapPixelToCoords
}
break;
鼠标移动事件:
case sf::Event::MouseMoved:
{
// Ignore mouse movement unless a button is pressed (see above)
if (!moving)
break;
// Determine the new position in world coordinates
const sf::Vector2f newPos = sf::Vector2f(event.mouseMove.x, event.mouseMove.y);
// Determine how the cursor has moved
// Swap these to invert the movement direction
const sf::Vector2f deltaPos = oldPos - newPos;
// Applying zoom "reduction" (or "augmentation")
deltaPos.x *= accumZoom;
deltaPos.y *= accumZoom;
// Move our view accordingly and update the window
view.move(deltaPos); // <-- Here I use move
window.setView(view);
// Save the new position as the old one
// We're recalculating this, since we've changed the view
oldPos = newPos; // With move, I don't need to recalculate
break;
}
鼠标滚轮滚动事件:
case sf::Event::MouseWheelScrolled:
// Ignore the mouse wheel unless we're not moving
if (moving)
break;
// Determine the scroll direction and adjust the zoom level
// Again, you can swap these to invert the direction
if (event.mouseWheelScroll.delta <= -1)
zoom = std::min(2.f, zoom + .1f);
else if (event.mouseWheelScroll.delta >= 1)
zoom = std::max(.5f, zoom - .1f);
accumZoom *= zoom; // <-- accumulate zoom
// Update our view
view.setSize(window.getDefaultView().getSize()); // Reset the size
view.zoom(zoom); // Apply the zoom level (this transforms the view)
window.setView(view);
break;