C#实现逼真的鼠标移动

13

我正在展示一款软件,想要构建一个鼠标“移动”功能,以便我可以基本自动化这个过程。我希望创建更真实的鼠标移动,但在思考过程中遇到了一些困难。我可以用C#轻松地移动鼠标,但希望它比光标只出现在某个x、y坐标并按下一个按钮更真实一些。

我获取鼠标的当前位置和终点位置。计算两点之间的弧线,但然后我需要计算沿着该弧线的点,以便我可以添加计时器事件,从一个点移动到下一个点,然后重复此操作直到到达目标...

有人想详细说明一下吗?

谢谢,R。


1
你是在弧形轨迹下移动鼠标从点A到点B吗?我似乎是直线运动。这会让你的生活变得更加容易。 :) - JP Alioto
那么,你有什么问题?能详细说明一下你的想法吗? - JerSchneid
JP:除非你使用尺子,否则你的鼠标轨迹都是直线吗? :)我正在考虑使用代码以逼真的方式移动鼠标。我已经搜索了谷歌,寻找代码库,但这似乎并不是一个常见的需求。 - flavour404
2
我在想在谷歌上搜索“样条”可能会有所帮助。 - annakata
你能告诉我如何实现鼠标点击吗?我正在使用Visual Studio 2010中的C#。 - BlueCacti
5个回答

24

我尝试过弧形计算方法,结果过于复杂了,最后看起来也不真实。像JP在评论中建议的那样,直线看起来更加自然。

这是我编写的一个函数,用于计算鼠标的线性移动。应该相当容易理解。GetCursorPosition()和SetCursorPosition(Point)是对win32函数GetCursorPos和SetCursorPos的封装。

至于数学方面——技术上讲,这被称为线性插值线段。

public void LinearSmoothMove(Point newPosition, int steps) {
    Point start = GetCursorPosition();
    PointF iterPoint = start;

    // Find the slope of the line segment defined by start and newPosition
    PointF slope = new PointF(newPosition.X - start.X, newPosition.Y - start.Y);

    // Divide by the number of steps
    slope.X = slope.X / steps;
    slope.Y = slope.Y / steps;

    // Move the mouse to each iterative point.
    for (int i = 0; i < steps; i++)
    {
        iterPoint = new PointF(iterPoint.X + slope.X, iterPoint.Y + slope.Y);
        SetCursorPosition(Point.Round(iterPoint));
        Thread.Sleep(MouseEventDelayMS);
    }

    // Move the mouse to the final destination.
    SetCursorPosition(newPosition);
}

3
我知道有人会对这个数学问题很聪明! :) - JP Alioto
@Erik,MouseEventDelayMS是什么? - Ashley Simpson
抱歉 - 一个整数常量,确定在移动事件之间等待的毫秒数。 - Erik Forbes
你因为四舍五入而失去了精度吗?当我在屏幕上画一条线并试图沿着它移动鼠标时,鼠标并没有完全沿着那条线移动。 - gonzobrains
是的,但我不需要一个完全准确的解决方案。 - Erik Forbes
显示剩余2条评论

12

我将之前提到的WindMouse函数转换成了C#,实际上非常逼真。请注意,这只是一个粗略的示例,没有使用GetCursorPosSetCursorPos的包装器。我将使用Windows输入模拟器的包装器。

static class SampleMouseMove {

    static Random random = new Random();
    static int mouseSpeed = 15;

    static void Main(string[] args) {
        MoveMouse(0, 0, 0, 0);
    }

    static void MoveMouse(int x, int y, int rx, int ry) {
        Point c = new Point();
        GetCursorPos(out c);

        x += random.Next(rx);
        y += random.Next(ry);

        double randomSpeed = Math.Max((random.Next(mouseSpeed) / 2.0 + mouseSpeed) / 10.0, 0.1);

        WindMouse(c.X, c.Y, x, y, 9.0, 3.0, 10.0 / randomSpeed,
            15.0 / randomSpeed, 10.0 * randomSpeed, 10.0 * randomSpeed); 
    }

    static void WindMouse(double xs, double ys, double xe, double ye,
        double gravity, double wind, double minWait, double maxWait,
        double maxStep, double targetArea) {

        double dist, windX = 0, windY = 0, veloX = 0, veloY = 0, randomDist, veloMag, step;
        int oldX, oldY, newX = (int)Math.Round(xs), newY = (int)Math.Round(ys);

        double waitDiff = maxWait - minWait;
        double sqrt2 = Math.Sqrt(2.0);
        double sqrt3 = Math.Sqrt(3.0);
        double sqrt5 = Math.Sqrt(5.0);

        dist = Hypot(xe - xs, ye - ys);

        while (dist > 1.0) {

            wind = Math.Min(wind, dist);

            if (dist >= targetArea) {
                int w = random.Next((int)Math.Round(wind) * 2 + 1);
                windX = windX / sqrt3 + (w - wind) / sqrt5;
                windY = windY / sqrt3 + (w - wind) / sqrt5;
            }
            else {
                windX = windX / sqrt2;
                windY = windY / sqrt2;
                if (maxStep < 3)
                    maxStep = random.Next(3) + 3.0;
                else
                    maxStep = maxStep / sqrt5;
            }

            veloX += windX;
            veloY += windY;
            veloX = veloX + gravity * (xe - xs) / dist;
            veloY = veloY + gravity * (ye - ys) / dist;

            if (Hypot(veloX, veloY) > maxStep) {
                randomDist = maxStep / 2.0 + random.Next((int)Math.Round(maxStep) / 2);
                veloMag = Hypot(veloX, veloY);
                veloX = (veloX / veloMag) * randomDist;
                veloY = (veloY / veloMag) * randomDist;
            }

            oldX = (int)Math.Round(xs);
            oldY = (int)Math.Round(ys);
            xs += veloX;
            ys += veloY;
            dist = Hypot(xe - xs, ye - ys);
            newX = (int)Math.Round(xs);
            newY = (int)Math.Round(ys);

            if (oldX != newX || oldY != newY)
                SetCursorPos(newX, newY);

            step = Hypot(xs - oldX, ys - oldY);
            int wait = (int)Math.Round(waitDiff * (step / maxStep) + minWait);
            Thread.Sleep(wait);
        }

        int endX = (int)Math.Round(xe);
        int endY = (int)Math.Round(ye);
        if (endX != newX || endY != newY)
            SetCursorPos(endX, endY);
    }

    static double Hypot(double dx, double dy) {
        return Math.Sqrt(dx * dx + dy * dy);
    }

    [DllImport("user32.dll")]
    static extern bool SetCursorPos(int X, int Y);

    [DllImport("user32.dll")]
    public static extern bool GetCursorPos(out Point p);
}

3
procedure WindMouse(xs, ys, xe, ye, gravity, wind, minWait, maxWait, maxStep, targetArea: extended);
var
  veloX, veloY, windX, windY, veloMag, dist, randomDist, lastDist, step: extended;
  lastX, lastY: integer;
  sqrt2, sqrt3, sqrt5: extended;
begin
  sqrt2:= sqrt(2);
  sqrt3:= sqrt(3);
  sqrt5:= sqrt(5);
  while hypot(xs - xe, ys - ye) > 1 do
  begin
    dist:= hypot(xs - xe, ys - ye);
    wind:= minE(wind, dist);
    if dist >= targetArea then
    begin
      windX:= windX / sqrt3 + (random(round(wind) * 2 + 1) - wind) / sqrt5;
      windY:= windY / sqrt3 + (random(round(wind) * 2 + 1) - wind) / sqrt5;
    end else
    begin
      windX:= windX / sqrt2;
      windY:= windY / sqrt2;
      if (maxStep < 3) then
      begin
        maxStep:= random(3) + 3.0;
      end else
      begin
        maxStep:= maxStep / sqrt5;
      end;
    end;
    veloX:= veloX + windX;
    veloY:= veloY + windY;
    veloX:= veloX + gravity * (xe - xs) / dist;
    veloY:= veloY + gravity * (ye - ys) / dist;
    if hypot(veloX, veloY) > maxStep then
    begin
      randomDist:= maxStep / 2.0 + random(round(maxStep) / 2);
      veloMag:= sqrt(veloX * veloX + veloY * veloY);
      veloX:= (veloX / veloMag) * randomDist;
      veloY:= (veloY / veloMag) * randomDist;
    end;
    lastX:= Round(xs);
    lastY:= Round(ys);
    xs:= xs + veloX;
    ys:= ys + veloY;
    if (lastX <> Round(xs)) or (lastY <> Round(ys)) then
      MoveMouse(Round(xs), Round(ys));
    step:= hypot(xs - lastX, ys - lastY);
    wait(round((maxWait - minWait) * (step / maxStep) + minWait));
    lastdist:= dist;
  end;
  if (Round(xe) <> Round(xs)) or (Round(ye) <> Round(ys)) then
    MoveMouse(Round(xe), Round(ye));
end;

{*******************************************************************************
procedure MMouse(x, y, rx, ry: integer);
By: Benland100
Description: Moves the mouse.
*******************************************************************************}
//Randomness is just added to the x,y. Might want to change that.
procedure MMouse(x, y, rx, ry: integer);
var
  cx, cy: integer;
  randSpeed: extended;
begin
  randSpeed:= (random(MouseSpeed) / 2.0 + MouseSpeed) / 10.0;
  if randSpeed = 0.0 then
    randSpeed := 0.1;
  getMousePos(cx,cy);
  X := x + random(rx);
  Y := y + random(ry);
  WindMouse(cx,cy,x,y,9.0,3.0,10.0/randSpeed,15.0/randSpeed,10.0*randSpeed,10.0*randSpeed);
end;

这里有一些用SCAR编写的方法。转换成C#不应太难,这些方法非常实际。


谢谢你,我会看一下。有趣的昵称! :) - flavour404
我一直在尝试弄清楚minE()函数的作用,你有什么想法吗? - flavour404
@flavour404 - minE() 返回两个变量中较小的一个 - Math.Min() 或类似函数。来源:http://forums.freddy1990.com/index.php?topic=4214.0 - Erik Forbes
不确定来源的可靠性,但根据上面的代码判断,我认为它是准确的。 - Erik Forbes

1
通常的方法是,我认为,是用自己的手实际移动真正的鼠标:并让软件捕捉这些(真实)运动,并重放它们。

1
我喜欢这种方法,但它只适用于您已经知道光标应该去哪里的情况,如果程序在运行时计算位置,则此方法将无法使用。 - Tortillas

1
你可以尝试这个。

public static class Mouse
{
    private const float MOUSE_SMOOTH = 200f;

    public static void MoveTo(int targetX, int targetY)
    {
        var targetPosition = new Point(targetX, targetY);
        var curPos = Cursor.Position;

        var diffX = targetPosition.X - curPos.X;
        var diffY = targetPosition.Y - curPos.Y;

        for (int i = 0; i <= MOUSE_SMOOTH; i++)
        {
            float x = curPos.X + (diffX / MOUSE_SMOOTH * i);
            float y = curPos.Y + (diffY / MOUSE_SMOOTH * i);
            Cursor.Position = new Point((int)x, (int)y);
            Thread.Sleep(1);
        }

        if (Cursor.Position != targetPosition)
        {
            MoveTo(targetPosition.X, targetPosition.Y);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接