降低像素扫描延迟:游戏输入模拟脚本优化需求
优化游戏脚本按键延迟至100ms以内的方案
你当前的延迟主要来自三个核心点:pyautogui.pixel的封装开销、time.sleep的精度不足,以及旧APIkeybd_event的低效。下面是亲测有效的优化方案,能把延迟压到100ms以内:
1. 替换像素扫描为Win32原生方法
pyautogui.pixel内部做了大量封装,单次扫描耗时可达几十毫秒。直接用win32gui的底层API获取像素,速度能提升数倍:
import win32gui # 提前获取游戏窗口的DC(全屏游戏可传入0代表桌面DC) hwnd = win32gui.GetForegroundWindow() # 也可用win32gui.FindWindow(None, "游戏窗口标题")精准定位 dc = win32gui.GetDC(hwnd) def get_pixel_red(x, y): # 只提取红色通道,减少计算量 return win32gui.GetPixel(dc, x, y) & 0xFF
2. 用SendInput替代keybd_event
keybd_event是Windows旧API,SendInput是更高效的输入模拟接口,延迟更低。同时用高精度的Sleep替代time.sleep控制按键时长:
import ctypes from ctypes import wintypes # 定义SendInput所需的结构体 INPUT_KEYBOARD = 1 KEYEVENTF_KEYUP = 0x0002 class KEYBDINPUT(ctypes.Structure): _fields_ = (("wVk", wintypes.WORD), ("wScan", wintypes.WORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.LPVOID)) class INPUT(ctypes.Structure): class _INPUT(ctypes.Union): _fields_ = (("ki", KEYBDINPUT),) _anonymous_ = ("_input",) _fields_ = (("type", wintypes.DWORD), ("_input", _INPUT)) def press_space(): # 构造按下空格的输入事件 input_down = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=0x20, wScan=0, dwFlags=0, time=0, dwExtraInfo=None)) ctypes.windll.user32.SendInput(1, ctypes.byref(input_down), ctypes.sizeof(input_down)) # 高精度睡眠10ms,保证游戏能识别到按键按下 ctypes.windll.kernel32.Sleep(10) # 构造抬起空格的输入事件 input_up = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=0x20, wScan=0, dwFlags=KEYEVENTF_KEYUP, time=0, dwExtraInfo=None)) ctypes.windll.user32.SendInput(1, ctypes.byref(input_up), ctypes.sizeof(input_up))
3. 优化循环逻辑
原循环中keyboard.is_pressed('q')的检查有额外开销,换成Win32原生的键盘状态检测:
def is_q_pressed(): # 0x51是Q键的虚拟键码 return ctypes.windll.user32.GetAsyncKeyState(0x51) & 0x8000 != 0
完整优化后脚本
import ctypes from ctypes import wintypes import win32gui # 定义SendInput相关结构体 INPUT_KEYBOARD = 1 KEYEVENTF_KEYUP = 0x0002 class KEYBDINPUT(ctypes.Structure): _fields_ = (("wVk", wintypes.WORD), ("wScan", wintypes.WORD), ("dwFlags", wintypes.DWORD), ("time", wintypes.DWORD), ("dwExtraInfo", wintypes.LPVOID)) class INPUT(ctypes.Structure): class _INPUT(ctypes.Union): _fields_ = (("ki", KEYBDINPUT),) _anonymous_ = ("_input",) _fields_ = (("type", wintypes.DWORD), ("_input", _INPUT)) # 提前获取游戏窗口DC hwnd = win32gui.GetForegroundWindow() dc = win32gui.GetDC(hwnd) def get_pixel_red(x, y): return win32gui.GetPixel(dc, x, y) & 0xFF def press_space(): input_down = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=0x20, wScan=0, dwFlags=0, time=0, dwExtraInfo=None)) ctypes.windll.user32.SendInput(1, ctypes.byref(input_down), ctypes.sizeof(input_down)) ctypes.windll.kernel32.Sleep(10) input_up = INPUT(type=INPUT_KEYBOARD, ki=KEYBDINPUT(wVk=0x20, wScan=0, dwFlags=KEYEVENTF_KEYUP, time=0, dwExtraInfo=None)) ctypes.windll.user32.SendInput(1, ctypes.byref(input_up), ctypes.sizeof(input_up)) def is_q_pressed(): return ctypes.windll.user32.GetAsyncKeyState(0x51) & 0x8000 != 0 # 主循环 while not is_q_pressed(): red_top = get_pixel_red(2400, 690) red_bottom = get_pixel_red(2400, 761) if red_top >= 150 or red_bottom >= 150: press_space() # 释放DC资源 win32gui.ReleaseDC(hwnd, dc)
额外优化建议
- 把脚本保存为
.pyw文件运行,避免控制台窗口的性能开销; - 用
python -O(优化模式)启动脚本,去掉断言和调试信息; - 如果游戏是DX/OpenGL渲染,可尝试用OpenCV捕获游戏帧(复杂度较高),但上述优化已足够满足你的需求。
内容的提问来源于stack exchange,提问作者Hilda
相关产品推荐
相关产品推荐

