当前位置:   article > 正文

Python开发游戏自动化脚本(三)后台键鼠操作_python 后台鼠标模拟

python 后台鼠标模拟

这一篇我们来探索如何实现后台键鼠操作。

键鼠操作就是模拟人工操作了,一旦实现了后台键鼠操作,我们就可以让游戏窗口在后台按照我们的脚本运行,同时处理别的工作了,也能够实现利用脚本同时操作多个游戏窗口。

基本原理

我首先查阅到文档是Simulating Input,但是里面使用的SendInput函数,根据原理只能对前台窗口生效,但我们要实现后台操作,所以我继续翻阅文档,得到了更多信息:关于窗口消息的文档_About Messages and Message Queues_中给出了游戏窗口如何处理键鼠操作的解释。键鼠操作其实就是向当前窗口发送对应的消息,我们可以通过PostMessageW或者SendMessage来想特定窗口发送消息,不同的是,PostMessageW只是通知窗口有个消息要处理,发送消息后不会等待消息处理结果直接返回,而SendMessage是通知窗口有个消息要立即处理,并等待处理结果再返回,二者在发送消息时没有区别,下面仅以PostMessageW为例,讲解如何发送消息,PostMessageW的声明如下:

BOOL PostMessageW(  
  HWND   hWnd,  
  UINT   Msg,  
  WPARAM wParam,  
  LPARAM lParam  
);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

其中hWnd是窗口句柄,Msg是消息的ID,wParam和lParam都需要按照消息的要求进行设置。每个消息的要求都不一样,具体如何设置,需要查看对应消息的文档。

键盘消息

对于键盘消息我们主要关注WM_KEYDOWN、WM_KEYUP,其中WM_KEYDOWN和WM_KEYUP分别对应按键的按下和放开。

from ctypes import windll``from ctypes.wintypes import HWND``import string``import time``   ``PostMessageW = windll.user32.PostMessageW``MapVirtualKeyW = windll.user32.MapVirtualKeyW``VkKeyScanA = windll.user32.VkKeyScanA``   ``WM_KEYDOWN = 0x100``WM_KEYUP = 0x101``   ``# https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes``VkCode = {`    `"back":  0x08,`    `"tab":  0x09,`    `"return":  0x0D,`    `"shift":  0x10,`    `"control":  0x11,`    `"menu":  0x12,`    `"pause":  0x13,`    `"capital":  0x14,`    `"escape":  0x1B,`    `"space":  0x20,`    `"end":  0x23,`    `"home":  0x24,`    `"left":  0x25,`    `"up":  0x26,`    `"right":  0x27,`    `"down":  0x28,`    `"print":  0x2A,`    `"snapshot":  0x2C,`    `"insert":  0x2D,`    `"delete":  0x2E,`    `"lwin":  0x5B,`    `"rwin":  0x5C,`    `"numpad0":  0x60,`    `"numpad1":  0x61,`    `"numpad2":  0x62,`    `"numpad3":  0x63,`    `"numpad4":  0x64,`    `"numpad5":  0x65,`    `"numpad6":  0x66,`    `"numpad7":  0x67,`    `"numpad8":  0x68,`    `"numpad9":  0x69,`    `"multiply":  0x6A,`    `"add":  0x6B,`    `"separator":  0x6C,`    `"subtract":  0x6D,`    `"decimal":  0x6E,`    `"divide":  0x6F,`    `"f1":  0x70,`    `"f2":  0x71,`    `"f3":  0x72,`    `"f4":  0x73,`    `"f5":  0x74,`    `"f6":  0x75,`    `"f7":  0x76,`    `"f8":  0x77,`    `"f9":  0x78,`    `"f10":  0x79,`    `"f11":  0x7A,`    `"f12":  0x7B,`    `"numlock":  0x90,`    `"scroll":  0x91,`    `"lshift":  0xA0,`    `"rshift":  0xA1,`    `"lcontrol":  0xA2,`    `"rcontrol":  0xA3,`    `"lmenu":  0xA4,`    `"rmenu":  0XA5``}``   ``   ``def get_virtual_keycode(key: str):`    `"""根据按键名获取虚拟按键码``   `    `Args:`        `key (str): 按键名``   `    `Returns:`        `int: 虚拟按键码`    `"""`    `if len(key) == 1 and key in string.printable:`        `# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscana`        `return VkKeyScanA(ord(key)) & 0xff`    `else:`        `return VkCode[key]``   ``   ``def key_down(handle: HWND, key: str):`    `"""按下指定按键``   `    `Args:`        `handle (HWND): 窗口句柄`        `key (str): 按键名`    `"""`    `vk_code = get_virtual_keycode(key)`    `scan_code = MapVirtualKeyW(vk_code, 0)`    `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown`    `wparam = vk_code`    `lparam = (scan_code << 16) | 1`    `PostMessageW(handle, WM_KEYDOWN, wparam, lparam)``   ``   ``def key_up(handle: HWND, key: str):`    `"""放开指定按键``   `    `Args:`        `handle (HWND): 窗口句柄`        `key (str): 按键名`    `"""`    `vk_code = get_virtual_keycode(key)`    `scan_code = MapVirtualKeyW(vk_code, 0)`    `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keyup`    `wparam = vk_code`    `lparam = (scan_code << 16) | 0XC0000001`    `PostMessageW(handle, WM_KEYUP, wparam, lparam)``   ``   ``if __name__ == "__main__":`    `# 需要和目标窗口同一权限,游戏窗口通常是管理员权限`    `import sys`    `if not windll.shell32.IsUserAnAdmin():`        `# 不是管理员就提权`        `windll.shell32.ShellExecuteW(`            `None, "runas", sys.executable, __file__, None, 1)``   `    `import win32gui`    `handle = windll.user32.FindWindowW(None, "雷电模拟器")`    `#handle = win32gui.FindWindowEx(0, 0, None, "雷电模拟器")`    `print(handle)`    `classname = win32gui.GetClassName(handle)`    `# print(classname)`    `if classname == 'LDPlayerMainFrame':`        `mainhandle = win32gui.FindWindowEx(handle, 0, "RenderWindow", "TheRender")`        `print(mainhandle)`        `renderhandle = win32gui.FindWindowEx(mainhandle, 0, "subWin", "sub")`        `print(renderhandle)`        `clickhandle = renderhandle`    `else:`        `clickhandle = handle``   `    `print(clickhandle)`    `# 控制角色向前移动两秒`    `key_down(mainhandle, 'w')`    `time.sleep(2)`    `key_up(mainhandle, 'w')
  • 1
  

  • 1
  • 2

后台发送按键消息“w”,因按下间隔2秒后松开,效果如上图

*这里需要注意的是,很多时候模拟器分好几级,不能直接接受按键命令,需要你找到可以接受命令的子窗口

如上图,具体方法看上述代码

鼠标消息

对于鼠标消息我们主要关注WM_MOUSEMOVE、WM_LBUTTONDOWN、WM_LBUTTONUP、WM_MOUSEWHEEL,这些消息分别对应鼠标移动、鼠标左键按下和放开,以及滚轮操作。

from ctypes import windll, byref``from ctypes.wintypes import HWND, POINT``import time``   ``PostMessageW = windll.user32.PostMessageW``ClientToScreen = windll.user32.ClientToScreen``   ``WM_MOUSEMOVE = 0x0200``WM_LBUTTONDOWN = 0x0201``WM_LBUTTONUP = 0x202``WM_MOUSEWHEEL = 0x020A``WHEEL_DELTA = 120``   ``   ``def move_to(handle: HWND, x: int, y: int):`    `"""移动鼠标到坐标(x, y)``   `    `Args:`        `handle (HWND): 窗口句柄`        `x (int): 横坐标`        `y (int): 纵坐标`    `"""`    `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousemove`    `wparam = 0`    `lparam = y << 16 | x`    `PostMessageW(handle, WM_MOUSEMOVE, wparam, lparam)``   ``   ``def left_down(handle: HWND, x: int, y: int):`    `"""在坐标(x, y)按下鼠标左键``   `    `Args:`        `handle (HWND): 窗口句柄`        `x (int): 横坐标`        `y (int): 纵坐标`    `"""`    `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-lbuttondown`    `wparam = 0`    `lparam = y << 16 | x`    `PostMessageW(handle, WM_LBUTTONDOWN, wparam, lparam)``   ``   ``def left_up(handle: HWND, x: int, y: int):`    `"""在坐标(x, y)放开鼠标左键``   `    `Args:`        `handle (HWND): 窗口句柄`        `x (int): 横坐标`        `y (int): 纵坐标`    `"""`    `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-lbuttonup`    `wparam = 0`    `lparam = y << 16 | x`    `PostMessageW(handle, WM_LBUTTONUP, wparam, lparam)``   ``   ``def scroll(handle: HWND, delta: int, x: int, y: int):`    `"""在坐标(x, y)滚动鼠标滚轮``   `    `Args:`        `handle (HWND): 窗口句柄`        `delta (int): 为正向上滚动,为负向下滚动`        `x (int): 横坐标`        `y (int): 纵坐标`    `"""`    `move_to(handle, x, y)`    `# https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-mousewheel`    `wparam = delta << 16`    `p = POINT(x, y)`    `ClientToScreen(handle, byref(p))`    `lparam = p.y << 16 | p.x`    `PostMessageW(handle, WM_MOUSEWHEEL, wparam, lparam)``   ``   ``def scroll_up(handle: HWND, x: int, y: int):`    `"""在坐标(x, y)向上滚动鼠标滚轮``   `    `Args:`        `handle (HWND): 窗口句柄`        `x (int): 横坐标`        `y (int): 纵坐标`    `"""`    `scroll(handle, WHEEL_DELTA, x, y)``   ``   ``def scroll_down(handle: HWND, x: int, y: int):`    `"""在坐标(x, y)向下滚动鼠标滚轮``   `    `Args:`        `handle (HWND): 窗口句柄`        `x (int): 横坐标`        `y (int): 纵坐标`    `"""`    `scroll(handle, -WHEEL_DELTA, x, y)``   ``   ``if __name__ == "__main__":`    `# 需要和目标窗口同一权限,游戏窗口通常是管理员权限`    `import sys`    `if not windll.shell32.IsUserAnAdmin():`        `# 不是管理员就提权`        `windll.shell32.ShellExecuteW(`            `None, "runas", sys.executable, __file__, None, 1)``   `    `import cv2`    `handle = windll.user32.FindWindowW(None, "雷电模拟器")`    `# 点击线路`    `left_down(handle, 1234, 20)`    `time.sleep(0.1)`    `left_up(handle, 1234, 20)`    `time.sleep(1)`    `# 滚动线路列表`    `scroll_down(handle, 1000, 200)
  • 1

后台操作无效时可考虑前台操作

由于部分游戏技术干预,玩家无法使用后台键鼠操作,可以考虑使用SendInput进行前台操作,详细操作见前面的文章。

以上就是本篇的所有内容,下一篇我们将探索如何实现窗口操作。

本文转自网络,如有侵权,请联系删除。

学习资源推荐

除了上述分享,如果你也喜欢编程,想通过学习Python获取更高薪资,这里给大家分享一份Python学习资料。

这里给大家展示一下我进的兼职群和最近接单的截图

兼职群

私单

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/236651
推荐阅读
相关标签