我正在编码鼠标宏。它需要在屏幕上的每个点之间以设置的延迟满足某些点。例如,它必须在132毫秒内移动(x 14,y 30)。我遇到的问题是mouse_event跳到那个确切的位置,因此我需要包括某种平滑方法,以便它可以平滑地移动到每个点。 (移动越流畅,宏越好)。目前,我正在使用这种平滑每个运动的方法。
这很好,但是有其局限性,例如,如果它需要向左移动10个像素并且将平滑设置为20,它将继续跳跃。
有人知道一种更精确的平滑鼠标移动方法吗? (要求准确,流畅)
void Smoothing(int smoothing, int delay, int x, int y) {
for (int i = 0; i < smoothing; i++) {
mouse_event(1, x / smoothing, y / smoothing, 0, 0);
AccurateSleep(delay / smoothing);
}
mouse_event(1, x % smoothing, y % smoothing, 0, 0);
Sleep(delay % smoothing);
}
[Linear Interpolation是我在阅读问题时(以及在the other answer中提到的)时的第一个想法。
用于插值的通用公式是:
x 0 + t·x 1
<< x ...插值x 0
...起始值x 1 ...目标值t ...范围[0,1]中的插值参数>[当我意识到可能构成约束的一些事实时,我甚至打算将其写为答案(不幸的是,OP并未明确提及)。所有操作都是关于整数值。因此,最好进行整数运算。
mouse_event()
以及AccurateSleep()
用增量值调用。这可能是由OP使用的API决定的。#include <iostream>
static int xMouse = 0, yMouse = 0, t = 0;
void mouse_event(int _1, int dx, int dy, int _4, int _5)
{
xMouse += dx; yMouse += dy;
std::cout << "mouse_event(" << _1 << ", " << dx << ", " << dy << ", " << _4 << ", " << _5 << "): "
<< xMouse << ", " << yMouse << '\n';
}
void AccurateSleep(int delay)
{
t += delay;
std::cout << "AccurateSleep(" << delay << "): " << t << '\n';
}
void Sleep(int delay)
{
t += delay;
std::cout << "Sleep(" << delay << "): " << t << '\n';
}
void Smoothing(int smoothing, int delay, int x, int y)
{
for (int i = 0; i < smoothing; i++) {
mouse_event(1, x / smoothing, y / smoothing, 0, 0);
AccurateSleep(delay / smoothing);
}
mouse_event(1, x % smoothing, y % smoothing, 0, 0);
Sleep(delay % smoothing);
}
#define PRINT_AND_DO(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
PRINT_AND_DO(xMouse = 0; yMouse = 0; t = 0);
PRINT_AND_DO(Smoothing(10, 132, 14, 30));
PRINT_AND_DO(xMouse = 0; yMouse = 0; t = 0);
PRINT_AND_DO(Smoothing(20, 15, 10, 0));
}
输出:
xMouse = 0; yMouse = 0; t = 0; Smoothing(10, 132, 14, 30); mouse_event(1, 1, 3, 0, 0): 1, 3 AccurateSleep(13): 13 mouse_event(1, 1, 3, 0, 0): 2, 6 AccurateSleep(13): 26 mouse_event(1, 1, 3, 0, 0): 3, 9 AccurateSleep(13): 39 mouse_event(1, 1, 3, 0, 0): 4, 12 AccurateSleep(13): 52 mouse_event(1, 1, 3, 0, 0): 5, 15 AccurateSleep(13): 65 mouse_event(1, 1, 3, 0, 0): 6, 18 AccurateSleep(13): 78 mouse_event(1, 1, 3, 0, 0): 7, 21 AccurateSleep(13): 91 mouse_event(1, 1, 3, 0, 0): 8, 24 AccurateSleep(13): 104 mouse_event(1, 1, 3, 0, 0): 9, 27 AccurateSleep(13): 117 mouse_event(1, 1, 3, 0, 0): 10, 30 AccurateSleep(13): 130 mouse_event(1, 4, 0, 0, 0): 14, 30 Sleep(2): 132 xMouse = 0; yMouse = 0; t = 0; Smoothing(20, 15, 10, 0); mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 0, 0, 0, 0): 0, 0 AccurateSleep(0): 0 mouse_event(1, 10, 0, 0, 0): 10, 0 Sleep(15): 15
然后我修改了
Smoothing()
,以实现上述插值公式,并对特定情况进行了一些调整:
对于
t,使用
i / smoothing
(i
的范围为[1,平滑])。
i
进行插值,但先前迭代的值将保留并用于计算mouse_event()
和AccurateSleep()
的函数调用的增量值。xI = i * x / smoothing
不等于xI = i / smoothing * x
。 (即这些积分运算未提供可交换性。)Smoothing()
:void Smoothing(int smoothing, int delay, int x, int y)
{
int x_ = 0, y_ = 0, t_ = 0;
for (int i = 1; i <= smoothing; ++i) {
// i / smoothing provides the interpolation paramter in [0, 1]
int xI = i * x / smoothing;
int yI = i * y / smoothing;
int tI = i * delay / smoothing;
mouse_event(1, xI - x_, yI - y_, 0, 0);
AccurateSleep(tI - t_);
x_ = xI; y_ = yI; t_ = tI;
}
}
输出:
xMouse = 0; yMouse = 0; t = 0;
Smoothing(10, 132, 14, 30);
mouse_event(1, 1, 3, 0, 0): 1, 3
AccurateSleep(13): 13
mouse_event(1, 1, 3, 0, 0): 2, 6
AccurateSleep(13): 26
mouse_event(1, 2, 3, 0, 0): 4, 9
AccurateSleep(13): 39
mouse_event(1, 1, 3, 0, 0): 5, 12
AccurateSleep(13): 52
mouse_event(1, 2, 3, 0, 0): 7, 15
AccurateSleep(14): 66
mouse_event(1, 1, 3, 0, 0): 8, 18
AccurateSleep(13): 79
mouse_event(1, 1, 3, 0, 0): 9, 21
AccurateSleep(13): 92
mouse_event(1, 2, 3, 0, 0): 11, 24
AccurateSleep(13): 105
mouse_event(1, 1, 3, 0, 0): 12, 27
AccurateSleep(13): 118
mouse_event(1, 2, 3, 0, 0): 14, 30
AccurateSleep(14): 132
xMouse = 0; yMouse = 0; t = 0;
Smoothing(20, 15, 10, 0);
mouse_event(1, 0, 0, 0, 0): 0, 0
AccurateSleep(0): 0
mouse_event(1, 1, 0, 0, 0): 1, 0
AccurateSleep(1): 1
mouse_event(1, 0, 0, 0, 0): 1, 0
AccurateSleep(1): 2
mouse_event(1, 1, 0, 0, 0): 2, 0
AccurateSleep(1): 3
mouse_event(1, 0, 0, 0, 0): 2, 0
AccurateSleep(0): 3
mouse_event(1, 1, 0, 0, 0): 3, 0
AccurateSleep(1): 4
mouse_event(1, 0, 0, 0, 0): 3, 0
AccurateSleep(1): 5
mouse_event(1, 1, 0, 0, 0): 4, 0
AccurateSleep(1): 6
mouse_event(1, 0, 0, 0, 0): 4, 0
AccurateSleep(0): 6
mouse_event(1, 1, 0, 0, 0): 5, 0
AccurateSleep(1): 7
mouse_event(1, 0, 0, 0, 0): 5, 0
AccurateSleep(1): 8
mouse_event(1, 1, 0, 0, 0): 6, 0
AccurateSleep(1): 9
mouse_event(1, 0, 0, 0, 0): 6, 0
AccurateSleep(0): 9
mouse_event(1, 1, 0, 0, 0): 7, 0
AccurateSleep(1): 10
mouse_event(1, 0, 0, 0, 0): 7, 0
AccurateSleep(1): 11
mouse_event(1, 1, 0, 0, 0): 8, 0
AccurateSleep(1): 12
mouse_event(1, 0, 0, 0, 0): 8, 0
AccurateSleep(0): 12
mouse_event(1, 1, 0, 0, 0): 9, 0
AccurateSleep(1): 13
mouse_event(1, 0, 0, 0, 0): 9, 0
AccurateSleep(1): 14
mouse_event(1, 1, 0, 0, 0): 10, 0
AccurateSleep(1): 15
注意:
[最后一次迭代是用
i == smoothing
完成的,因此i / smoothing
的结果为1。因此,最后的插值步骤会产生精确的值–像OP原始方法一样,不需要后校正。将点作为矢量查看并在它们之间进行插值。这通常被称为线性插值的“公差”排序。您可以找到许多资源,这些资源在搜索线性插值时可能会有所帮助。这是an answer,可能有助于您了解它的含义。由于我手头有多余的时间,所以我也输入了一个可以执行此操作的程序示例。
#include <iostream> #include <chrono> struct Vec2d { double x; double y; Vec2d(double x, double y) : x(x), y(y) {}; }; Vec2d lerp(Vec2d const& a, Vec2d const& b, double t) { double x((1.0 - t) * a.x + t * b.x); double y((1.0 - t) * a.y + t * b.y); return Vec2d(x, y); } int main(int argc, char* argv[]) { Vec2d p1(10, 10); Vec2d p2(20, 40); double maxTime(100); //max time 100 milliseconds double elapsedTime(0); std::chrono::time_point<std::chrono::system_clock> start(std::chrono::system_clock::now()); std::chrono::time_point<std::chrono::system_clock> end(start); while(elapsedTime < maxTime) { elapsedTime += std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); start = end; //This is where the lerping happens double t(elapsedTime / maxTime); Vec2d p3(lerp(p1, p2, t)); //Show what's happening. std::cout << "p3: " << p3.x << ", " << p3.y << std::endl; end = std::chrono::system_clock::now(); } return 0; }
简短说明:t
isa值,从0到1。当t == 0.0
时,lerp
将返回p1
的“副本”。当t == 1.0
时,lerp
将返回p2
的“副本”。当t == 0.5
时,lerp
将返回(p1 + p2) / 2
(它们之间的中点)。您还需要添加代码以不断更新鼠标的位置。为此,您需要跟踪经过了多少时间,并根据从
希望这有所帮助。t
到p1
所需的时间量和实际经过的时间来计算p2
的值。上面的代码使用while循环和std::chrono
来跟踪经过的时间,从而做到了这一点。但是,此实现将取决于您打算如何触发这些“更新”。