简体   繁体   English

使用带有设置延迟的 mouse_event 平滑鼠标移动 C++

[英]Smooth Mouse Movement using mouse_event with set delay C++

I'm coding a mouse macro.我正在编写一个鼠标宏。 It needs to meet certain points on the screen in a set delay between each point.它需要在每个点之间的设定延迟内满足屏幕上的某些点。 For exammple, it must move (x 14, y 30) in 132ms.例如,它必须在 132 毫秒内移动 (x 14, y 30)。 The issue I'm having is mouse_event jumps to that exact position so I need to include some sort of smoothing method so that it moves smoothly to each point.我遇到的问题是 mouse_event 跳转到那个确切位置,所以我需要包含某种平滑方法,以便它平滑地移动到每个点。 (the smoother the movement is the better the macro). (运动越平滑,宏观越好)。 Currently I am using this method of smoothing each movement.目前我正在使用这种平滑每个动作的方法。

This works well but it has its limitations for example if it needs to move 10 pixels left and the smoothing is set to 20 it will continue to jump.这很有效,但它有其局限性,例如,如果它需要向左移动 10 个像素并且平滑设置为 20,它将继续跳跃。

Does anyone know of a more accurate method of smoothing mouse movements?有谁知道平滑鼠标移动的更准确方法? (requirements accurate, smooth) (要求准确、流畅)

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 was my first thought when I read the question (as well as mentioned in the other answer ). 线性插值是我阅读问题时的第一个想法(以及在其他答案中提到)。

A general formular for interpolation is:插值的通用公式是:

x = (1 - t) · x 0 + t · x 1 x = (1 - t) · x 0 + t · x 1

x ... interpolated value x ... 内插值
x 0 ... start value x 0 ... 起始值
x 1 ... destination value x 1 ... 目标值
t ... interpolation parameter in range [0, 1] t ... 范围内的插值参数 [0, 1]

I even intended to write this as answer when I realized some facts that might form possible constraints (which the OP unfortunately didn't mention explicitly).当我意识到一些可能构成可能约束的事实(不幸的是 OP 没有明确提及)时,我什至打算将其写为答案。

  1. All operations are about integral values.所有操作都与整数值有关。 So, doing integer arithmetic may be preferred.因此,进行整数运算可能是首选。
  2. The mouse_event() as well as the AccurateSleep() is called with delta values.使用 delta 值调用mouse_event()AccurateSleep() This might be dictated by the API used by OP.这可能由 OP 使用的 API 决定。

So, I thought twice and made the following MCVE to resemble OPs problem:所以,我三思而后行,使以下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));
}

Output:输出:

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

Then I modified Smoothing() implementing the above mentioned interpolation formula with some adjustments to the specific situation:然后我修改了Smoothing()实现上述插值公式,并根据具体情况进行了一些调整:

  1. For t , i / smoothing (with i in range [1, smoothing]) is used.对于t ,使用i / smoothingi在 [1,平滑] 范围内)。
  2. While the loop does the interpolation for each i , the values of previous iteration are kept and used to compute delta values for the function calls of mouse_event() and AccurateSleep() .当循环为每个i进行插值时,前一次迭代的值被保留并用于计算mouse_event()AccurateSleep()函数调用的增量值。
  3. Of course, the order of operations is important as this is integer arithmetic.当然,运算顺序很重要,因为这是整数运算。 Hence, xI = i * x / smoothing is not equivalent to xI = i / smoothing * x .因此, xI = i * x / smoothing不等于xI = i / smoothing * x (Ie commutativity is not provided by these integral operations.) (即这些积分运算不提供交换性。)

The modified Smoothing() :修改后的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;
  }
}

Output:输出:

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在coliru上进行现场演示

Note:笔记:

The last iteration is done with i == smoothing so that i / smoothing results in 1. Hence, the last interpolation step yields the exact values – no post-correction is necessary like in OPs original approach.最后一次迭代是用i == smoothing完成的,因此i / smoothing结果为 1。因此,最后一个插值步骤产生了精确的值——不需要像 OP 原始方法那样进行后校正。

View the points as vectors and interpolate between them.将点视为向量并在它们之间进行插值。 This is often referred to as "lerping" sort for linear interpolation.这通常被称为线性插值的“lerping”排序。 You can find many resources that may help if you search up linear interpolation.如果您搜索线性插值,您可以找到许多可能有帮助的资源。 Here's an answer that may help understand what it is.这是一个可能有助于理解它是什么的答案

Since I have extra time on my hands I've typed up an example of a program that does it as well.由于我手头有多余的时间,因此我已经输入了一个程序示例,该示例也可以执行此操作。

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

Short Explaination: t isa value from 0 to 1. When t == 0.0 lerp will return a "copy" of p1 .简短说明: t是从 0 到 1 的值。当t == 0.0 lerp将返回p1的“副本”。 When t == 1.0 lerp will return a "copy" of p2 .t == 1.0 lerp将返回p2的“副本”。 When t == 0.5 lerp will return (p1 + p2) / 2 (the mid point between them).t == 0.5 lerp将返回(p1 + p2) / 2 (它们之间的中点)。

You'll also need to add code to continually update the position of the mouse.您还需要添加代码以不断更新鼠标的位置。 To do this you'll need to keep track of how much time has elapsed and calculate the value of t based on the amount of time required to travel from p1 to p2 and the actual time that has elapsed.为此,您需要跟踪经过了多少时间,并根据从p1p2所需的时间量以及实际经过的时间来计算t的值。 The above code does this with the use of a while loop and std::chrono to keep track of elapsed time.上面的代码使用 while 循环和std::chrono来跟踪经过的时间。 This will implementation however will be dependent on how you intend to trigger these "updates".然而,这将实现将取决于您打算如何触发这些“更新”。

Hope this helped.希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM