我正在编码一个鼠标宏。它需要在屏幕上的每个点之间以设置的延迟满足某些点。例如,它必须在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 =(1-t)·x0 + t·x1
x ...内插值
x0 ...起始值
x1 ...目标值
t ...插值参数,范围为[0,1]
当我意识到可能构成约束的一些事实(不幸的是,OP并未明确提及)时,我甚至打算将此作为答案。
所有运算都是关于整数值。因此,进行整数算术可能是首选。
用增量值调用mouse_event()
和AccurateSleep()
。这可能是由OP使用的API决定的。
因此,我三思而后行,做了以下MCVE来类似于OP问题:
#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
Live Demo on coliru
注意:
最后的迭代是使用
i == smoothing
完成的,因此i / smoothing
的结果为1。因此,最后的插值步骤会产生精确的值-像OP原始方法一样,无需进行后期校正。