基于Python图像识别与PyAutoGUI的游戏自动化脚本开发实战

发布时间:2026/8/12 13:44:42
基于Python图像识别与PyAutoGUI的游戏自动化脚本开发实战 1. 项目概述与核心思路最近在《流放之路》里折腾装备尤其是那些需要大量重复工艺操作的“做装”流程真是让人头大。每次都要盯着屏幕重复点击、确认、等待不仅枯燥还容易因为疲劳导致操作失误浪费宝贵的通货。作为一个喜欢用技术解决重复劳动的程序员我就在想能不能用Python写个脚本把这些繁琐的步骤自动化起来这个想法就是“Python实现流放之路自动做装”项目的由来。本质上它不是一个外挂或作弊工具而是一个基于图像识别和模拟鼠标键盘操作的自动化脚本旨在替代玩家执行那些规则固定、但极其耗时的工艺操作比如用“剥离石”剥离词缀、用“富豪石”点金、或者进行特定序列的“庄园工艺”。这个项目适合有一定Python基础并且对流放之路装备制作系统有较深理解的玩家。它不要求你是编程高手但需要你有耐心去调试和适配。整个脚本的核心思路是让程序“看到”游戏画面通过截图和图像识别然后“模仿”你的手去操作通过控制鼠标和键盘。听起来简单但里面涉及到游戏窗口的精准定位、各种游戏内UI元素的识别、以及应对网络延迟、画面卡顿等实际问题的策略。接下来我会详细拆解我是如何一步步实现这个自动化脚本的包括用到的库、核心逻辑、踩过的坑以及一些提升稳定性的独家技巧。2. 环境准备与核心工具选型工欲善其事必先利其器。在开始写代码之前我们需要搭建好Python环境并选对工具库。我的开发环境是Windows 11Python版本选择了3.9这是一个比较稳定且各类库支持良好的版本。游戏运行在窗口化模式分辨率设置为1920x1080这是为了保证图像识别模板的稳定性。2.1 核心库的安装与作用解析自动化脚本主要依赖三个核心库pyautogui、opencv-python和pillow。你可以通过pip一键安装pip install pyautogui opencv-python pillowPyAutoGUI这是我们的“手”。它负责控制鼠标移动、点击、拖动以及键盘按键。它的函数非常直观比如pyautogui.moveTo(x, y)将鼠标移动到指定坐标pyautogui.click()执行点击。但是这里有一个至关重要的注意事项PyAutoGUI的坐标是基于整个屏幕的绝对坐标。如果你的游戏窗口没有固定在屏幕的绝对位置或者你切换了显示器坐标就会全部错乱。因此我们第一步必须是让脚本能自动找到游戏窗口的位置。OpenCV-Python (cv2)这是我们的“眼睛”。我们用它来处理截图进行图像匹配从而在屏幕上找到特定的按钮、图标或状态标识。比如我们需要识别“工艺台”的打开按钮、确认“工艺”的黄色按钮或者装备上特定的词缀图标。OpenCV的模板匹配功能是我们的核心武器。Pillow (PIL)这是图像处理的辅助工具。我们主要用它来配合PyAutoGUI进行截图 (pyautogui.screenshot返回的就是PIL的Image对象)然后可以方便地将其转换为OpenCV能处理的数组格式或者进行一些简单的裁剪、尺寸调整。2.2 游戏设置与前期准备在写代码之前需要在游戏内进行一些固定设置这是保证脚本稳定运行的基础窗口模式必须使用“窗口化”或“窗口化全屏”模式。全屏模式会干扰截图和坐标定位。分辨率与UI缩放将游戏分辨率固定在一个常用值如1080p并关闭游戏内的UI缩放功能。UI缩放会改变按钮等元素的实际像素大小导致我们准备好的模板图片无法匹配。关闭干扰特效尽量关闭角色技能特效、光环特效等动态效果特别是在工艺台附近。这些动态效果可能会被误识别为我们想要找的静态UI元素。整理仓库将需要操作的装备和使用的通货剥离石、富豪石、改造石等放在仓库固定、容易点击的位置最好是单独一页。同样工艺台也最好放在城镇里一个背景干净、不会轻易被其他玩家或特效遮挡的位置。实操心得在开始编写识别逻辑前我强烈建议你先用pyautogui.displayMousePosition()这个功能。运行它它会实时在终端显示鼠标当前的屏幕坐标。然后你手动把鼠标移动到游戏窗口的四个角、工艺台按钮、确认按钮等关键位置记下这些坐标。这能帮你快速理解屏幕坐标体系并为后续的窗口定位提供参考点。3. 核心模块设计与实现细节整个脚本可以划分为几个核心模块游戏窗口定位、图像识别引擎、操作执行器、以及主控逻辑。我们一个个来拆解。3.1 游戏窗口定位模块这是整个脚本的基石。如果找不到窗口后续所有操作都是空中楼阁。我们不能依赖固定的屏幕坐标必须动态查找。我的方法是利用PyAutoGUI截图然后通过OpenCV匹配一个游戏窗口独有的、且位置固定的特征图标。对于《流放之路》一个很好的目标是游戏窗口左上角的小图标比如角色头像旁边的生命球图标的一部分或者任务追踪栏的UI元素。你需要事先对这个特征区域进行截图保存为一个小的模板图片如window_anchor.png。import pyautogui import cv2 import numpy as np from PIL import Image def find_game_window(template_path, confidence0.8): 在屏幕上寻找游戏窗口的锚点。 :param template_path: 锚点模板图片的路径 :param confidence: 匹配置信度默认0.8 :return: 窗口左上角的坐标 (x, y)如果未找到则返回 (None, None) # 获取全屏截图 screenshot pyautogui.screenshot() screenshot_cv cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR) template cv2.imread(template_path, cv2.IMREAD_COLOR) if template is None: raise FileNotFoundError(f无法加载模板图片: {template_path}) # 使用模板匹配 result cv2.matchTemplate(screenshot_cv, template, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc cv2.minMaxLoc(result) if max_val confidence: top_left max_loc # 假设我们的锚点模板就是窗口左上角的一部分直接返回 # 如果锚点是窗口内部的某个点可能需要根据偏移量计算窗口左上角 return top_left else: print(f未找到游戏窗口最高匹配度: {max_val:.2f}) return None, None找到锚点后我们就能推算出游戏窗口的大致区域。例如如果你知道任务追踪栏在窗口内相对于左上角的固定偏移量就可以计算出窗口内任意UI元素的相对坐标再结合找到的锚点绝对坐标就能得到该元素在屏幕上的绝对坐标供PyAutoGUI操作。踩坑记录一开始我尝试用窗口标题来定位但发现《流放之路》的窗口标题并不总是唯一且容易被其他窗口遮挡。用图像锚点是最可靠的方法。另外匹配置信度confidence不要设得太高比如0.95因为游戏画面可能会有细微的颜色波动或抗锯齿差异0.8-0.9是一个比较稳健的范围。3.2 图像识别引擎模块这个模块负责“看”判断当前游戏处于什么状态。比如工艺台打开了吗装备放上去了吗工艺按钮是可点击状态吗词缀剥离成功了吗这都需要通过图像识别来实现。我们为每一种需要判断的状态准备一张“模板”小图。def locate_on_screen(image_path, regionNone, confidence0.7, grayscaleFalse): 在指定区域或全屏查找目标图片。 :param image_path: 目标模板图片路径 :param region: 搜索区域 (left, top, width, height)None则为全屏 :param confidence: 匹配置信度 :param grayscale: 是否转为灰度图匹配可以加速且对颜色变化不敏感的场景更稳定 :return: 目标中心点坐标 (x, y)未找到返回 None try: # pyautogui的locateOnScreen函数内部使用了pyscreeze它本身就用到了OpenCV或pillow location pyautogui.locateOnScreen(image_path, regionregion, confidenceconfidence, grayscalegrayscale) if location: center pyautogui.center(location) return center else: return None except Exception as e: print(f识别图像 {image_path} 时出错: {e}) return None模板图片的制作技巧截图要精准在游戏画面稳定时用截图工具截取最小的、能唯一标识该状态的区域。例如“工艺”按钮就只截那个黄色的按钮本身不要带太多背景。处理颜色变化有些UI元素状态会变如按钮可用为黄色不可用为灰色。你可能需要为同一种逻辑状态准备多个模板或者使用灰度匹配 (grayscaleTrue) 来忽略颜色只关注形状和亮度差异。应对多分辨率如果你的脚本需要在不同分辨率下运行要么准备多套模板要么在识别前将截图和模板缩放到同一尺度但这会引入复杂度。固定一个分辨率是最简单的。3.3 操作执行器与主控逻辑操作执行器封装了所有对游戏的具体操作如点击、拖拽、按键等待。关键是加入随机性和容错等待。import random import time def click_position(x, y, buttonleft, clicks1, interval0.1, offset_range5): 模拟鼠标点击加入随机偏移和间隔以防止被检测为机器人虽然主要目的是防误操作。 :param offset_range: 随机偏移像素范围如5表示在±5像素内随机偏移 if offset_range 0: x random.randint(-offset_range, offset_range) y random.randint(-offset_range, offset_range) pyautogui.moveTo(x, y, durationrandom.uniform(0.1, 0.3)) # 移动也加入随机耗时 time.sleep(random.uniform(0.05, 0.15)) pyautogui.click(x, y, buttonbutton, clicksclicks, intervalinterval) time.sleep(random.uniform(0.1, 0.3)) # 操作后等待 def wait_until(image_path, timeout10, interval0.5, **kwargs): 等待直到某个图像出现。 start_time time.time() while time.time() - start_time timeout: pos locate_on_screen(image_path, **kwargs) if pos: return pos time.sleep(interval) raise TimeoutError(f等待 {image_path} 超时)主控逻辑则是将这些模块串联起来的“大脑”。它通常是一个状态机。以“自动剥离后缀直到只剩一个”为例逻辑流程如下状态开始。定位游戏窗口打开仓库找到装备和剥离石。状态放置装备。将装备拖拽到工艺台物品槽。使用wait_until确认装备图标出现在槽位。状态执行剥离。 a. 点击工艺台中的“剥离石”工艺选项。 b. 点击“工艺”按钮。 c. 等待工艺完成可以通过识别工艺按钮变灰再变亮或者出现一个短暂的“工艺完成”特效帧来判断但更简单的方法是等待一个固定时间比如1.5秒。状态检查结果。 a. 对装备物品栏的装备图标进行截图。 b. 使用图像识别判断后缀数量这非常复杂通常需要OCR识别词缀文本或识别词缀旁边的图标标记。一个简化策略是如果你知道剥离前的后缀数量并且每次只剥离一个那么可以通过重复执行“剥离”操作的次数来判断。更高级的做法需要接入OCR库如pytesseract但受游戏字体、背景影响极大不稳定。状态判断如果后缀数量1回到状态3。如果后缀数量1则流程结束将装备放回仓库。异常处理在任何一步如果长时间未进入预期状态如“工艺”按钮一直找不到则抛出超时异常记录日志并尝试安全停止比如按ESC键关闭所有打开的面板。核心技巧网络延迟和电脑性能会导致游戏响应速度不同。所有“等待”操作尽量不要用固定的time.sleep(2)而应该用wait_until去等待一个确定性的视觉状态出现。比如点击“工艺”后应该等待“工艺”按钮从亮黄色变成灰色不可点击然后再变成亮黄色可进行下一次操作这才是工艺真正完成的标志。固定等待时间要么效率低下要么在卡顿时会出错。4. 实战案例自动剥离石流程详解让我们以一个相对简单但非常实用的案例——“使用剥离石剥离魔法装备的后缀直到只剩一条后缀”——来串联上述所有模块。我们假设目标是一件蓝色魔法装备初始有1条前缀和2条后缀目标是剥离掉1条后缀得到1前缀1后缀的蓝装。4.1 流程步骤拆解与代码实现首先我们需要准备以下模板图片保存在images/目录下window_corner.png: 游戏窗口左上角特征图标。crafting_bench_open.png: 工艺台打开后的界面特征比如台子本身的某部分。item_socketed.png: 装备已放入工艺台物品槽的状态可以截取槽位有物品时的边框高亮。annul_orb_option.png: 工艺台列表中“剥离石”选项的图标。craft_button.png: 亮黄色的“工艺”按钮。craft_button_grey.png: 灰色的“工艺”按钮不可点击状态。import time import random from pathlib import Path # 假设其他模块函数已定义 (find_game_window, locate_on_screen, click_position, wait_until) IMAGE_DIR Path(./images) def auto_annul_one_suffix(): 自动执行一次剥离石流程目标是移除一条后缀假设初始有两条后缀。 print( 开始自动剥离流程 ) # 1. 定位游戏窗口 window_pos find_game_window(IMAGE_DIR / window_corner.png) if not window_pos[0]: print(错误未找到游戏窗口) return False win_x, win_y window_pos print(f游戏窗口左上角位于: ({win_x}, {win_y})) # 定义一些基于窗口坐标的相对位置这些坐标需要你事先通过 displayMousePosition 获取并校准 # 例如仓库按钮相对窗口的偏移量 (rel_仓库_x, rel_仓库_y) rel_stash_x, rel_stash_y 50, 150 # 工艺台物品槽中心相对窗口的偏移 rel_socket_x, rel_socket_y 400, 300 # ... 其他坐标 # 2. 打开仓库并放置装备简化假设装备和剥离石已在固定位置 print(打开仓库...) click_position(win_x rel_stash_x, win_y rel_stash_y) time.sleep(1) # 等待仓库打开动画 print(将装备拖入工艺台...) # 假设装备在仓库第一格其相对仓库的坐标是 (rel_item_x, rel_item_y) item_x, item_y win_x rel_stash_x 100, win_y rel_stash_y 100 # 拖拽操作 pyautogui.moveTo(item_x, item_y, duration0.2) pyautogui.mouseDown() time.sleep(0.2) pyautogui.moveTo(win_x rel_socket_x, win_y rel_socket_y, duration0.3) time.sleep(0.2) pyautogui.mouseUp() time.sleep(0.5) # 3. 确认装备已放置 print(确认装备放置...) if not wait_until(IMAGE_DIR / item_socketed.png, region(win_x, win_y, 800, 600), timeout5): print(错误装备未成功放入工艺台) return False # 4. 选择剥离石工艺 print(选择剥离石工艺...) annul_option_pos locate_on_screen(IMAGE_DIR / annul_orb_option.png, region(win_x, win_y, 800, 600)) if annul_option_pos: click_position(annul_option_pos.x, annul_option_pos.y) time.sleep(0.5) else: print(错误未找到剥离石工艺选项) return False # 5. 点击工艺按钮并等待完成 print(点击工艺按钮...) craft_button_pos wait_until(IMAGE_DIR / craft_button.png, region(win_x, win_y, 800, 600), timeout3) if craft_button_pos: click_position(craft_button_pos.x, craft_button_pos.y) # 关键等待工艺按钮变灰表示工艺进行中再变亮表示完成 print(等待工艺执行...) wait_until(IMAGE_DIR / craft_button_grey.png, region(win_x, win_y, 800, 600), timeout2) wait_until(IMAGE_DIR / craft_button.png, region(win_x, win_y, 800, 600), timeout5) print(工艺执行完毕。) time.sleep(0.5) # 额外缓冲 else: print(错误工艺按钮不可点击或未找到) return False # 6. 这里本应检查词缀数量但如前述我们简化处理。 # 假设我们只执行一次剥离完成后将装备拿回仓库。 print(将装备取回仓库...) # 从工艺台拖回仓库 pyautogui.moveTo(win_x rel_socket_x, win_y rel_socket_y, duration0.2) pyautogui.mouseDown() time.sleep(0.2) pyautogui.moveTo(item_x, item_y, duration0.3) # 拖回原位置 time.sleep(0.2) pyautogui.mouseUp() print( 自动剥离流程结束 ) return True # 主循环例如连续对3件装备执行剥离 if __name__ __main__: for i in range(3): print(f\n处理第 {i1} 件装备...) if not auto_annul_one_suffix(): print(流程中断停止执行。) break time.sleep(2) # 处理下一件前的间隔4.2 流程中的难点与优化策略上面的代码是一个高度简化的框架。在实际操作中你会遇到很多问题坐标校准rel_stash_x,rel_socket_y这些相对坐标必须非常精确。最好的办法是写一个校准脚本让你用鼠标点击关键点程序自动记录相对于窗口锚点的偏移量并保存到配置文件。网络延迟与卡顿wait_until函数的timeout和interval参数需要根据你的网络情况调整。超时时间设得太短在卡顿时会误判失败设得太长效率低下。一个折中的办法是设置一个较长的超时如15秒但在循环内加入一些“心跳”检测比如每等待1秒就尝试重新查找一次窗口锚点防止游戏完全卡死无响应。意外弹窗干扰游戏内突然出现的提示如“你的仓库已满”、好友邀请等都会遮挡UI。脚本需要有一定的鲁棒性来处理。可以在每个关键步骤如点击前都重新确认目标按钮是否可见或者增加一个全局的“安全状态检查”函数定期检查游戏是否处于预期的界面。词缀数量判断真正的自动化核心这是本项目从“半自动”迈向“全自动”的最大挑战。纯图像匹配很难。可行的进阶方向有OCR文字识别使用pytesseract识别装备下方的词缀文本行数。但需要先精准裁剪出词缀区域且受游戏字体、颜色、背景影响极大准确率是个问题。图标特征识别有些插件或游戏本身会在词缀前加上小图标。可以尝试识别这些图标的数量。这比识别文字更稳定。颜色与像素分析对于非常固定的装备类型和词缀组合也许可以通过分析物品图标特定区域的颜色变化比如前缀/后缀区域的背景色差异来推断但这非常定制化且脆弱。妥协方案对于像剥离石这样“随机移除一个词缀”的操作如果你无法准确判断结果那么脚本的用途就局限于“代替你点击”你仍然需要人工检查结果并决定下一步。这依然是巨大的效率提升。5. 常见问题排查与稳定性提升技巧在实际运行中脚本可能会因为各种原因失败。下面是一个常见问题排查表以及我总结的一些提升稳定性的技巧。问题现象可能原因排查与解决方法脚本完全找不到游戏窗口1. 模板图片window_corner.png不匹配。2. 游戏窗口被最小化或其他窗口完全遮挡。3. 屏幕分辨率/缩放比例改变。1. 重新截取更精准的锚点模板确保截图时游戏画面静止。2. 确保游戏窗口在前台且可见。3. 检查并固定屏幕和游戏的显示设置。能找到窗口但点击位置总是偏移相对坐标 (rel_x, rel_y) 计算错误或窗口位置动态变化。1. 运行坐标校准脚本重新获取偏移量。2. 在每次关键操作前重新调用find_game_window获取最新的窗口位置动态计算坐标。图像识别时灵时不灵1. 游戏内UI特效、光线变化干扰。2. 匹配置信度confidence设置不当。3. 模板图片包含动态背景。1. 关闭不必要的游戏特效。2. 适当降低置信度如0.7并使用灰度匹配 (grayscaleTrue)。3. 重新截取模板确保目标UI元素突出背景干净。点击“工艺”后脚本卡住1. 网络延迟工艺响应慢。2. 用于判断“工艺完成”的图像状态识别失败。3. 游戏弹出其他确认框如通货不足。1. 增加wait_until的超时时间并加入更稳健的状态判断如组合判断按钮变灰变亮。2. 增加备用判断机制比如在等待工艺完成时同时检测是否出现了“通货不足”的提示图片。脚本运行一段时间后突然乱点1. 游戏或系统卡顿导致坐标累积误差。2. 脚本逻辑陷入死循环。1. 在每个主要操作步骤后加入time.sleep(random.uniform(0.1, 0.3))让系统喘息并重置坐标基准。2. 为所有循环添加最大次数限制或超时机制。被游戏检测为异常操作操作过于规律和迅速像机器人。1. 在所有鼠标移动、点击、等待操作中加入随机延迟和微小随机偏移如前面click_position函数所示。2. 模拟人类的不精确操作比如偶尔移动鼠标到错误位置再移回来。提升稳定性的独家技巧状态机与心跳机制将整个流程建模为一个状态机。每个状态都有明确的进入条件、执行动作、退出条件和超时处理。同时设置一个全局“心跳”线程每隔几秒检查一次游戏主窗口是否还在前台、脚本是否处于预期状态如果发现异常可以尝试安全恢复如按ESC返回初始界面或优雅停止。多层图像匹配对于关键按钮不要只依赖一张模板图。可以截取它在不同状态下的多张图正常、高亮、按下匹配时只要匹配上其中一张即可。或者匹配按钮的某个不变的特征部分比如按钮上的一个特定图标而不是整个按钮。日志与截图诊断在脚本中集成详细的日志记录。每一个步骤开始、成功、失败都记录到文件。更重要的是在识别失败或发生异常时自动截取当前屏幕并保存为带有时间戳的图片。这样当脚本出错时你可以通过查看日志和当时的截图快速定位是哪个UI元素没识别出来从而调整你的模板图片或识别参数。配置化不要将坐标、图片路径、等待时间等参数硬编码在代码里。应该使用一个配置文件如config.yaml或config.ini来管理。这样当你更换显示器、调整游戏UI布局或者只是想微调某个等待时间时只需要修改配置文件而无需改动代码逻辑。6. 项目总结与扩展思考实现一个稳定的《流放之路》自动做装脚本是一个典型的“看起来简单做起来坑多”的项目。它考验的不仅仅是Python编程能力更是对游戏机制的理解、对图像识别稳定性的把控以及编写鲁棒性代码的工程能力。我从这个项目中获得的最大体会是自动化脚本的难点10%在于实现核心功能90%在于处理各种边界情况和异常状态。这个脚本目前只是一个起点一个框架。它的价值在于解放你的双手去执行那些确定性的、重复的点击操作。你可以基于这个框架扩展出更多功能自动点金与重铸用于快速制作底子。自动化石/精华点装配合仓库管理批量对一堆底子进行相同的操作。更复杂的工艺流程比如“前缀无法被改变”后的 scour 洗后缀或者“无法附加攻击相关属性”后的锁前洗后等。这需要更强大的状态判断逻辑。最后必须强调一点使用此类自动化脚本需要非常谨慎。它处于一个灰色地带。虽然它只是模拟了你的鼠标键盘操作并没有直接读取或修改游戏内存但依然可能违反游戏的服务条款。我的建议是仅将其用于个人学习和技术研究在单人游戏或非竞争性场景下谨慎使用并绝对不要用于任何可能影响其他玩家体验或游戏经济系统的场合。技术的乐趣在于探索和实现的过程请务必尊重游戏规则和其他玩家的体验。