游戏内鼠标录制回放脚本相机移动异常问题求助
3D游戏鼠标录制回放失效问题排查与解决建议
问题背景
我是速通游戏爱好者,为制作个人速通流程编写了Python脚本,用于录制游戏内键鼠输入并按时序回放。脚本基于pydirectinput和pynput库,通过pynput的监听器捕获on_move、on_press等事件,实现了F1启停录制、F2回放的功能——键盘输入回放正常,但3D游戏中相机移动回放完全失效,轨迹随机跳动。
已尝试的无效方案
- 将光标固定在屏幕中心,使用
pydirectinput.moveRel(x,y)模拟相对移动驱动相机,无效; - 限定鼠标活动窗口,触达边界时瞬移到另一侧模拟无限窗口,无效。
原因分析
- 绝对坐标与相对坐标的核心冲突:3D游戏的相机控制几乎都依赖**鼠标相对位移(delta)**而非绝对屏幕坐标,但当前脚本录制的是鼠标绝对位置,回放时直接调用
moveTo会导致光标位置与游戏相机的映射逻辑完全错位——游戏实时读取的是鼠标移动的增量,而非最终停留位置。 - 系统级模拟的权限限制:部分全屏/独占模式的3D游戏会绕过系统消息队列,直接读取硬件输入,
pydirectinput的常规模拟方法可能被游戏拦截,无法触发相机响应。 - 录制时的光标干扰:当前脚本在录制时限制光标活动范围,破坏了原始的鼠标位移数据,导致录制的delta信息不准确,进一步加剧回放偏差。
可行解决建议
1. 录制并回放鼠标相对位移(delta)
这是解决问题的核心:
- 录制阶段:不再记录鼠标绝对坐标,而是计算相邻帧的位移差(dx, dy)作为事件数据;录制时将光标固定在屏幕中心并隐藏,避免边界限制打断位移记录。
- 回放阶段:使用Windows底层API(如
SendInput)直接模拟相对移动,绕过系统层面的输入拦截,确保游戏能捕获到真实的位移信号。
2. 优化游戏输入设置
- 关闭游戏内的鼠标加速、平滑、灵敏度曲线等非线性设置,确保鼠标位移与相机转动成严格线性关系;
- 若游戏支持,开启Raw Input模式,让游戏直接读取硬件输入,避免系统处理带来的模拟偏差。
3. 替换为更底层的输入模拟库
放弃pydirectinput的高层封装,直接使用pywin32调用SendInput或mouse_event,这类底层API对游戏独占模式的兼容性更好。
修改后的核心代码示例
调整录制逻辑(记录delta)
# 修改InputRecorder初始化 class InputRecorder: def __init__(self): # ... 原有代码 ... self.prev_x, self.prev_y = pydirectinput.position() # 修改on_move函数 def on_move(x, y): global recorder if recorder.recording: # 计算相对位移 dx = x - recorder.prev_x dy = y - recorder.prev_y recorder.record_event('delta', (dx, dy)) recorder.prev_x, recorder.prev_y = x, y # 录制时固定光标在中心,防止触达边界 pydirectinput.moveTo(recorder.center_x, recorder.center_y, _pause=False)
调整回放逻辑(用SendInput模拟相对移动)
# 在InputRecorder的replay_inputs方法中,替换delta分支的代码 elif event_type == 'delta': dx, dy = event_data # 调用Windows SendInput API模拟相对鼠标移动 import ctypes INPUT_MOUSE = 0 MOUSEEVENTF_MOVE = 0x0001 class POINT(ctypes.Structure): _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)] class INPUT(ctypes.Structure): class _INPUT(ctypes.Union): class _MOUSEINPUT(ctypes.Structure): _fields_ = [("dx", ctypes.c_long), ("dy", ctypes.c_long), ("mouseData", ctypes.c_ulong), ("dwFlags", ctypes.c_ulong), ("time", ctypes.c_ulong), ("dwExtraInfo", ctypes.POINTER(ctypes.c_ulong))] _fields_ = [("mi", _MOUSEINPUT)] _anonymous_ = ("_input",) _fields_ = [("type", ctypes.c_ulong), ("_input", _INPUT)] extra = ctypes.c_ulong(0) input_mouse = INPUT() input_mouse.type = INPUT_MOUSE # 将相对位移转换为SendInput要求的绝对坐标比例(范围-32768到32767) input_mouse.mi.dx = dx * 65536 // recorder.screen_width input_mouse.mi.dy = dy * 65536 // recorder.screen_height input_mouse.mi.dwFlags = MOUSEEVENTF_MOVE input_mouse.mi.dwExtraInfo = ctypes.pointer(extra) ctypes.windll.user32.SendInput(1, ctypes.byref(input_mouse), ctypes.sizeof(input_mouse))
内容的提问来源于stack exchange,提问作者jason_1992
相关产品推荐
相关产品推荐

