简体   繁体   English

MOUSEEVENTF_MOVE 不会移动到游戏中的正确坐标

[英]MOUSEEVENTF_MOVE doesn't move to correct coordinates in-game

Feel like I am bashing my head against the wall here so wanted to reach out and see if there is some easy solution I am missing first before I go crazy.感觉就像我在这里用头撞墙,所以想伸出手看看是否有一些简单的解决方案,在我 go 发疯之前先错过。

The problem:问题:

  • I was tasked to write a TAS for an older directx11 game for a charity event.我的任务是为慈善活动的旧 directx11 游戏编写 TAS。 I want to detect a pixel color, and move to that pixel.我想检测像素颜色,然后移动到该像素。 I have pixel detection working via OpenCV but the actual movements to that pixel do not line up.我通过 OpenCV 进行像素检测,但该像素的实际移动不对齐。
  • I found a function that does what I want, but it uses a fixed number to try and modify the movement by that I can't figure out for my game.我找到了一个 function 可以满足我的要求,但它使用一个固定的数字来尝试修改我无法为我的游戏弄清楚的运动。
  • My mouse dpi does not affect the movement, but the in-game sensitivity does.我的鼠标 dpi 不影响移动,但游戏中的灵敏度会。
  • The game does not like SetCursorPos and seems to reject that input.游戏不喜欢 SetCursorPos 并且似乎拒绝该输入。

Code:代码:

#include <iostream>
#include <Windows.h>

using namespace std;

//Magic number defines
const float constant = 0.116f;
float mouseSensitivity = 10.0f;
float modifier = mouseSensitivity * constant;

int centerScreenX = GetSystemMetrics(SM_CXSCREEN) / 2;
int centerScreenY = GetSystemMetrics(SM_CYSCREEN) / 2;

//Move mouse to center of screen
void centerMouse() {
    SetCursorPos(centerScreenX, centerScreenY);
    cout << "Moving to center of screen at(" << centerScreenX << ", " << centerScreenY << ")" << endl;
}

//Calculates actual coordinates for mouse movement based on sensitivity and a constant.
void calibrateCoordinates(int& x, int& y)
{
    if (abs(x) < 5)
        x = 0;
    else {
        x = x - centerScreenX;
        x = (int)((float)x / modifier);
    }

    if (abs(y) < 5)
        y = 0;
    else
    {
        y = y - centerScreenY;
        y = (int)((float)y / modifier);
    }
    cout << "Coordinates needed to move by (" << x << ", " << y << ")" << endl;
}


// Moves to x,y coordinates after processed into actual coordinates based on sensitivity and a constant.
void moveTo(int x, int y)
{
    SetProcessDPIAware();
    calibrateCoordinates(x, y);
    mouse_event(MOUSEEVENTF_MOVE, x, y, 0, 0);

    //Sanity check where the mouse ended up at
    POINT p;
    if (GetCursorPos(&p))
    {
        cout << "Mouse ended up at (" << p.x << ", " << p.y << ")" << endl;
    }
}


int main() {
    while (true) {
        // Check if the F19 button is pressed
        if (GetAsyncKeyState(VK_F19) & 0x8000) {

            //Hardcoded values of pixel we need to move to. Handled automatically via OpenCV in the real code. Simplified here
            int xTo = 784;
            int yTo = 686;

            //Centers mouse to line up cursor with crosshair
            centerMouse();

            //Tries to move to coords
            moveTo(xTo, yTo);
        }
    }
    return 0;
}

Output: Output:

Matched pixel found at (784, 686)[4, 20, 222, 255] Moving to center of screen at (1280, 720) Coordinates needed to move by (-271, -20) Mouse ended up at (1009, 700)匹配像素位于 (784, 686)[4, 20, 222, 255] 移动到屏幕中心 (1280, 720) 坐标需要移动 (-271, -20) 鼠标最终位于 (1009, 700)

The mouse should have ended up at (1012, 649) for the cross-hair to line up with the pixel I want.鼠标应该在 (1012, 649) 结束,以便十字准线与我想要的像素对齐。 Do I just need to keep experimenting to find the magic number that it works with?我是否只需要继续试验以找到它适用的神奇数字? Or is there any easier way to do this?还是有更简单的方法来做到这一点? Thanks谢谢

GetSystemMetrics function is not DPI aware. GetSystemMetrics function 不支持 DPI。 Use GetSystemMetricsForDpi instead to get a correct result.请改用GetSystemMetricsForDpi以获得正确的结果。

Disclaimer: Sorry if this isn't an answer but I can't comment yet because of my low rep.免责声明:抱歉,如果这不是答案,但由于我的代表率低,我还不能发表评论。

  1. If it's a game, have you tried taking the DX9/11's version of "GetSystemMetrics"?如果是游戏,你试过拿DX9/11版的“GetSystemMetrics”吗? I had the same problem on DX9 aimbot years ago where the mouse aims slightly to the right.几年前,我在 DX9 瞄准机器人上遇到了同样的问题,鼠标稍微向右瞄准。 Now I can't give the exact answer since I've never done DX11 hooking but this is the mouse_move code for my aimbot:现在我不能给出确切的答案,因为我从来没有做过 DX11 挂钩,但这是我的 aimbot 的 mouse_move 代码:
mouse_event(MOUSEEVENTF_MOVE, coordinates.x - interfaceinfo->D3D9Viewport.Width * 0.5f, coordinates.y - interfaceinfo->D3D9Viewport.Height * 0.5f, 0, 0);

The coordinates is the target's position, while interfaceinfo->D3D9Viewport.Width/Height is the screen info given by the DX9, in this case using **IDirect3DDevice9::GetViewport** coordinates是目标的 position,而interfaceinfo->D3D9Viewport.Width/Height是 DX9 给出的屏幕信息,在这种情况下使用**IDirect3DDevice9::GetViewport**

HRESULT WINAPI Renderer::hkPresent(LPDIRECT3DDEVICE9 pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion) {
    renderer->Device_D3D9 = pDevice;
    Device_D3D9->GetViewport(&interfaceinfo->D3D9Viewport);
    return renderer->oPresent(pDevice, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
}
  1. If it's a game, have you tried moving the mouse by hooking DINPUT functions.如果是游戏,您是否尝试过通过挂接 DINPUT 函数来移动鼠标。 By hooking to GetDeviceState and GetDeviceData, you can emulate keyclicks and mouse clicks/movement without moving your actual mouse (unlike sendinput and mouse_event)通过连接到 GetDeviceState 和 GetDeviceData,您可以在不移动实际鼠标的情况下模拟按键点击和鼠标点击/移动(与 sentinput 和 mouse_event 不同)
HRESULT WINAPI Renderer::hkGetDeviceState(IDirectInputDevice8A* DIDevice, DWORD cbData, LPVOID *lpvData) {
    HRESULT hResult = renderer->oGetDeviceState(DIDevice, cbData, lpvData);
    
    if (interfaceinfo->bCurrentWindow >= 1) {
        if (hResult == DI_OK) {
            for (auto i = 0; i < cbData; i++) {
                lpvData[i] = 0;
            }
        }
    }

    static BYTE buffer[256];
    if (true) {
        buffer[DIK_W] = LOBYTE(0x80);
        buffer['w'] = LOBYTE(0x80);
        buffer['W'] = LOBYTE(0x80);
        cbData = 256;
        memcpy(lpvData, buffer, cbData);
    }
    else {
        hResult = renderer->oGetDeviceState(DIDevice, cbData, lpvData);
    }
    
    return hResult;
}

HRESULT WINAPI Renderer::hkGetDeviceData(IDirectInputDevice8A* DIDevice, DWORD cbObjectData, LPDIDEVICEOBJECTDATA rgdod, LPDWORD pdwInOut, DWORD dwFlags) {
    HRESULT ret = renderer->oGetDeviceData(DIDevice, cbObjectData, rgdod, pdwInOut, dwFlags);
    if (ret == DI_OK) {
        for (DWORD i = 0; i < *pdwInOut; ++i) {
            if (interfaceinfo->bCurrentWindow >= 1) {
                *pdwInOut = 0;
            }
            else {
                switch (rgdod[i].dwOfs) {
                case DIK_W:
                        rgdod[i].dwData = LOBYTE(0x80);
                    break;
                case DIK_SPACE: {
                    static byte last_written = 0;
                    rgdod[i].dwData = HIBYTE(last_written ? 0x01 : 0x00);
                    last_written = last_written ? 0x00 : 0x01;
                    break;
                }
                }
            }
        }
    }
    return ret;
}

First of all, you appear to be passing absolute coordinates to the function mouse_event , but that function is interpreting them as coordinates relative to the last mouse position, because you are not passing the MOUSEEVENTF_ABSOLUTE flag to mouse_event .首先,您似乎将绝对坐标传递给 function mouse_event ,但 function 将它们解释为相对于最后一个鼠标 position 的坐标,因为您没有将MOUSEEVENTF_ABSOLUTE标志传递给mouse_event Therefore, if you want to pass absolute coordinates, you should also pass that flag.因此,如果你想传递绝对坐标,你也应该传递那个标志。 Also, Microsoft recommends that you use SendInput instead of mouse_event .此外,Microsoft 建议您使用SendInput而不是mouse_event

Another issue is the following:另一个问题如下:

The official Microsoft documentation for the function GetSystemMetrics states the following: function GetSystemMetrics的官方 Microsoft 文档说明如下:

This API is not DPI aware, and should not be used if the calling thread is per-monitor DPI aware.此 API 不是 DPI 感知的,如果调用线程是每个显示器的 DPI 感知的,则不应使用。 For the DPI-aware version of this API, see GetSystemMetricsForDPI .有关此 API 的 DPI 感知版本,请参阅GetSystemMetricsForDPI For more information on DPI awareness, see the Windows High DPI documentation .有关 DPI 感知的更多信息,请参阅Windows 高 DPI 文档

Therefore, the results of the lines因此,线条的结果

int centerScreenX = GetSystemMetrics(SM_CXSCREEN) / 2;
int centerScreenY = GetSystemMetrics(SM_CYSCREEN) / 2;

are possibly incorrect.可能不正确。 I suggest that you change these lines to the following:我建议您将这些行更改为以下内容:

int centerScreenX = GetSystemMetricsForDpi( SM_CXSCREEN, GetDpiForSystem() ) / 2;
int centerScreenY = GetSystemMetricsForDpi( SM_CYSCREEN, GetDpiForSystem() ) / 2;

However, the function GetDpiForSystem will simply return the value 96 unless the process or thread is made DPI-aware beforehand.但是,function GetDpiForSystem将简单地返回值96 ,除非进程或线程事先设置为 DPI 感知。 Therefore, it is important that you call the function SetProcessDPIAware before calling the function GetDPIForSystem .因此,在调用 function GetDPIForSystem之前调用 function SetProcessDPIAware非常重要。 Alternatively, you can set the default DPI awareness through an application manifest setting, as described here .或者,您可以通过应用程序清单设置来设置默认 DPI 感知,如此所述。

According to this Microsoft documentation , there are currently 4 different modes of DPI awareness:根据这个 Microsoft 文档,目前有 4 种不同的 DPI 感知模式:

  1. Unaware浑然不觉
  2. System系统
  3. Per-Monitor每个显示器
  4. Per-Monitor V2每显示器 V2

Since you are using the function SetProcessDPIAware , you are setting the DPI awareness mode of your process to "System".由于您使用的是 function SetProcessDPIAware ,因此您正在将进程的 DPI 感知模式设置为“系统”。 This mode will only make your process aware of the DPI on the primary monitor at the time the user logged in. If the DPI was changed since then, then your process will be unaware of these changes.此模式只会让您的进程在用户登录时了解主监视器上的 DPI。如果此后更改了 DPI,那么您的进程将不知道这些更改。 For this reason, you may want to consider using "Per-Monitor V2" DPI-awareness instead, by using the function SetProcessDpiAwarenessContext or SetThreadDpiAwarenessContext .出于这个原因,您可能需要考虑使用“Per-Monitor V2”DPI 感知,通过使用 function SetProcessDpiAwarenessContextSetThreadDpiAwarenessContext However, this will require your application to handle WM_DPICHANGED messages.但是,这将要求您的应用程序处理WM_DPICHANGED消息。

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

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