简介:本资源是一个面向《阴阳师》手游玩家的Python自动化辅助脚本项目,专为希望提升日常副本效率、减少重复操作的中高级玩家设计。项目基于OpenCV等图像识别技术实现界面元素精准定位与交互逻辑控制,覆盖魂十一单人速刷、困二十八层挂机、源赖光经验副本自动挑战及御灵副本智能通关四大核心场景,显著降低操作门槛并释放碎片时间。压缩包共9个文件(4个.py主逻辑与UI模块、3个.txt配置与说明文件、1个.ui界面定义、1个.md文档),总大小仅18KB,轻量易部署,结构清晰便于二次开发与调试。目前已有86人学习下载,读者可直接获取完整可运行脚本、环境依赖清单(requirements.txt)、图形界面封装(form.ui)、多副本状态识别策略及模块化任务调度逻辑(yys_work.py + widget.py + linklist.py),具备良好的工程实践参考价值。
1. 项目缘起:从手动“肝”到自动“刷”的转变
如果你也玩过《阴阳师》这类回合制养成游戏,一定对“肝”这个字深有体会。每天上线,重复着“魂十一”、“困二十八”、“经验副本”、“御灵副本”这些日常任务,一刷就是几个小时,手指点得发麻,眼睛看得发酸,但为了提升式神、获取御魂,又不得不做。这种重复性极高的操作,不仅消耗时间,更消磨游戏的乐趣。作为一名有多年开发经验的玩家,我一直在想,能不能用技术解放双手,把我们从这种机械劳动中解脱出来?这就是我启动这个“阴阳师自动化辅助脚本”项目的初衷。
这个项目的核心目标非常明确:利用Python编程和图像识别技术,模拟玩家在游戏内的操作,实现日常副本的自动化运行。具体来说,它要能稳定、高效地完成几个核心场景:魂十一的单人速刷、困二十八层的高效挂机、源赖光经验副本的自动挑战以及御灵副本的智能通关。这几乎覆盖了玩家日常“搬砖”的绝大部分需求。它不是外挂,不修改游戏内存,不发送异常封包,其原理仅仅是“看”屏幕、“想”策略、“点”鼠标,模拟一个真实玩家的操作逻辑,因此从行为模式上讲,风险相对可控。当然,任何自动化操作都需谨慎,并仅建议在个人单机环境下用于学习与研究。
接下来,我将从技术选型、核心模块拆解、实战避坑指南以及未来优化空间四个维度,为你完整复盘这个项目的构建过程。无论你是想学习图像识别在自动化领域的应用,还是想为自己心爱的游戏打造一个“后勤管家”,相信这篇超过五千字的详实记录都能给你带来启发。
2. 技术栈选型:为什么是Python + 图像识别?
在项目启动前,技术选型是首要问题。市面上实现自动化操作的技术路线不少,比如基于游戏内存读取的“真·外挂”,基于安卓ADB指令的操控,或是基于Windows API的键鼠模拟。经过权衡,我最终选择了“Python + 图像识别”这条路径,原因如下:
2.1 选择Python的核心考量
Python并非执行效率最高的语言,但在自动化脚本领域,它拥有无与伦比的生态优势和开发效率。
- 丰富的库支持:
PyAutoGUI提供了跨平台的鼠标键盘控制;Pillow (PIL)是图像处理的基石;OpenCV和PyTorch/TensorFlow为高级图像识别提供可能;NumPy让数值计算变得简单。这些库成熟、稳定,文档齐全,能极大降低开发门槛。 - 快速原型开发:自动化脚本需要频繁调整点击坐标、等待时间、识别阈值等参数。Python的交互式特性(如Jupyter Notebook)和简洁语法,允许我们快速修改代码并看到效果,进行“试错式”开发,这对调试脚本逻辑至关重要。
- 跨平台潜力:虽然本项目主要针对Windows端的桌面版阴阳师,但PyAutoGUI等库也支持macOS和Linux。核心的图像识别逻辑可以相对容易地迁移,为后续适配其他平台(如模拟器)留有余地。
2.2 图像识别 vs. 其他方案
- 与内存挂对比:直接读取游戏内存数据无疑是最精准、最高效的方式,能直接获取怪物血量、技能CD、战斗结果等状态。但这需要逆向工程,触及游戏核心数据,风险极高,极易被检测并封号,且违反用户协议。我们追求的是“辅助”而非“破坏”,因此首先排除。
- 与ADB操控对比:通过ADB(Android Debug Bridge)控制安卓模拟器,可以获取屏幕截图并模拟点击。这种方式稳定,且不依赖前台窗口。但它的局限性在于,必须运行在模拟器内,对于直接使用Windows桌面版客户端的玩家不够友好。此外,ADB的截图和点击速度有时会成为瓶颈。
- 与纯坐标点击对比:有些简单脚本通过记录固定的屏幕坐标进行点击。这种方式极其脆弱,一旦游戏窗口位置改变、分辨率调整、UI更新,脚本就会完全失效,毫无健壮性可言。
因此,图像识别成为了平衡“安全性”、“通用性”和“智能性”的最佳选择。它的工作原理是:脚本实时捕获游戏窗口的截图,在截图中寻找预先准备好的“模板图片”(比如“挑战”按钮、胜利后的“结算图标”、特定怪物的形象等),找到后计算出该图案在屏幕上的位置,然后驱动鼠标去点击。只要游戏UI逻辑不变,即使窗口位置移动,脚本也能通过“看图找茬”的方式准确定位到目标。
注意:图像识别脚本的稳定性高度依赖于游戏UI的稳定性。如果游戏版本大更新,按钮样式或位置发生改变,就需要更新对应的模板图片。这是采用此方案必须接受的维护成本。
3. 核心模块深度拆解:脚本如何“看见”并“思考”
整个自动化脚本可以看作一个状态机,它循环执行“截图 -> 分析 -> 决策 -> 操作”这个过程。下面我们深入每个模块的细节。
3.1 环境感知模块:精准截图与图像预处理
一切始于“看见”。我们首先要能稳定地获取游戏画面。
import pyautogui import cv2 from PIL import ImageGrab def capture_game_window(window_title="阴阳师"): # 方案一:使用PyAutoGUI定位窗口并截图(推荐) try: window = pyautogui.getWindowsWithTitle(window_title)[0] if window.isMinimized: window.restore() window.activate() # 激活窗口,确保在最前 # 获取窗口位置和大小 left, top, width, height = window.left, window.top, window.width, window.height screenshot = pyautogui.screenshot(region=(left, top, width, height)) return cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR), (left, top) except IndexError: raise Exception(f"未找到标题包含'{window_title}'的窗口") # 方案二:使用ImageGrab指定区域(需已知坐标) # region = (0, 0, 1920, 1080) # 示例坐标,需自行调整 # screenshot = ImageGrab.grab(bbox=region)这里有两个关键点:
- 窗口激活:
window.activate()非常重要。有些游戏在后台时渲染会停止或降低频率,导致截图内容停滞。确保窗口在前台能获得实时画面。 - 坐标转换:
pyautogui.screenshot返回的是PIL图像,而OpenCV处理需要numpy数组的BGR格式。cv2.cvtColor完成了这个转换。同时,我们记录下窗口左上角的坐标(left, top),这样识别出的图片坐标(相对于截图)才能转换为全局屏幕坐标。
图像预处理是为了提高识别成功率。原始截图可能因为游戏特效、亮度变化而产生干扰。
def preprocess_image(image): # 转换为灰度图,大部分识别不需要颜色信息,且能提升速度 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 可选:二值化,使特征更突出 # _, binary = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY) # 可选:降噪 # denoised = cv2.medianBlur(gray, 3) return gray对于阴阳师这类UI对比度较高的游戏,通常转换为灰度图就足够了。二值化在背景复杂时有用,但也可能丢失细节,需要根据实际模板测试。
3.2 决策中枢模块:基于模板匹配的状态判断
这是脚本的“大脑”。它通过比对预先准备好的模板图片,来判断当前游戏处于什么状态,并决定下一步做什么。
首先,我们需要一个可靠的模板匹配函数:
def find_template(screenshot, template_path, threshold=0.8): """ 在screenshot中寻找template_path指定的模板。 threshold: 匹配置信度阈值,越高要求越严格。 返回匹配到的最高置信度及中心点坐标(相对于screenshot),未找到返回None。 """ template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE) if template is None: raise FileNotFoundError(f"模板图片未找到: {template_path}") screen_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) result = cv2.matchTemplate(screen_gray, template, cv2.TM_CCOEFF_NORMED) min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result) if max_val >= threshold: h, w = template.shape center_x = max_loc[0] + w // 2 center_y = max_loc[1] + h // 2 return max_val, (center_x, center_y) else: return Nonecv2.matchTemplate提供了多种匹配方法,TM_CCOEFF_NORMED(归一化相关系数匹配)对光照变化有一定鲁棒性,效果较好。threshold(阈值)是这个函数的核心参数,需要反复调试。设得太高(如0.95),轻微的画面抖动或特效就可能导致匹配失败;设得太低(如0.7),则可能匹配到错误的相似区域。
基于这个函数,我们可以构建状态判断逻辑。以“魂十一”单人挑战为例,一个简化的状态机如下:
def decide_action(screenshot, window_pos): # 状态1:是否在副本选择界面?(寻找“魂十一”标签的模板) state = check_state(screenshot, ['template_team_up.png', 'template_soul_11.png']) if state == "IN_SELECTION": click_on_template(screenshot, 'template_challenge.png', window_pos) return "CLICKED_CHALLENGE" # 状态2:是否在队伍配置界面?(寻找“准备”按钮) state = check_state(screenshot, ['template_ready.png']) if state == "IN_TEAM_SETUP": click_on_template(screenshot, 'template_ready.png', window_pos) return "CLICKED_READY" # 状态3:是否在战斗中?(寻找战斗中特有的UI,如倒计时、技能图标) state = check_state(screenshot, ['template_in_battle.png']) if state == "IN_BATTLE": # 战斗中可以执行策略,比如检测是否该标记目标,或者单纯等待 pyautogui.sleep(15) # 估算战斗时间,等待 return "IN_BATTLE_WAITING" # 状态4:是否战斗胜利?(寻找胜利的“结算”或“勋章”图标) state = check_state(screenshot, ['template_victory.png', 'template_chest.png']) if state == "VICTORY": # 连续点击屏幕,快速跳过奖励动画 for _ in range(5): pyautogui.click(button='left') pyautogui.sleep(0.5) return "VICTORY_CLICKING" # 状态5:是否体力不足?(寻找体力不足的弹窗) state = check_state(screenshot, ['template_no_ap.png']) if state == "NO_AP": # 执行处理逻辑,比如使用勾玉补充,或者停止脚本 handle_no_ap() return "STOP_SCRIPT" # ... 更多状态判断 return "UNKNOWN_STATE"这个decide_action函数会循环执行。关键在于check_state函数的实现。它应该按优先级顺序检查多个模板。例如,胜利图标和战斗图标可能不会同时出现,但为了保险,可以先检查是否有“体力不足”这种需要立即处理的异常状态,再检查常规流程状态。
3.3 执行器模块:稳健的鼠标操作与防呆设计
操作模块看似简单,就是pyautogui.click(x, y),但里面有很多细节关乎脚本的稳定性。
- 随机化操作:完全固定的点击点和时间间隔是“机器人”的典型特征。加入随机性可以模拟人类操作。
import random import time def smart_click(center_x, center_y, window_pos): # 将截图内的坐标转换为全局屏幕坐标 global_x = window_pos[0] + center_x global_y = window_pos[1] + center_y # 在目标点附近随机一个偏移量(例如±5像素) offset_x = random.randint(-5, 5) offset_y = random.randint(-5, 5) target_x = global_x + offset_x target_y = global_y + offset_y # 移动鼠标,加入随机延迟 pyautogui.moveTo(target_x, target_y, duration=random.uniform(0.1, 0.3)) # 点击前稍作停顿,模拟反应时间 time.sleep(random.uniform(0.05, 0.15)) pyautogui.click() # 点击后也随机等待一下 time.sleep(random.uniform(0.1, 0.3)) - 操作失败重试与超时:不能因为一次识别或点击失败就让脚本卡死。每个关键操作步骤都应该有重试机制和超时判断。
def click_with_retry(template_path, max_retries=5, timeout=30): start_time = time.time() for i in range(max_retries): screenshot, pos = capture_game_window() result = find_template(screenshot, template_path) if result: confidence, center = result smart_click(center[0], center[1], pos) return True # 点击成功 # 未找到模板,等待一段时间再试 time.sleep(1) if time.time() - start_time > timeout: print(f"超时:未找到模板 {template_path}") return False print(f"重试{max_retries}次后失败:未找到模板 {template_path}") return False - 异常状态监听:脚本需要能处理游戏中的各种弹窗(如网络重连、公告、活动提示)。这需要准备一套覆盖常见干扰项的模板库,并在主循环中定期进行“巡检”。
4. 分场景实战与避坑指南
有了核心框架,针对不同的副本,我们需要定制化的策略和模板。这里分享几个关键场景的实现心得和踩过的坑。
4.1 魂十一单人速刷:速度与稳定的博弈
魂十一是御魂主要产出地,追求的是速刷。自动化脚本在这里的挑战在于:
- 战斗节奏快:从进入副本到战斗结束,时间很短。要求状态判断必须迅速准确。
- 结算动画:胜利后的结算动画有多个可跳过节点,点击太快或太慢都会影响效率。
我的策略:
- 模板准备:准备高对比度的“挑战”按钮、“准备”按钮、战斗中的“自动”图标、胜利后的“勋章”图标以及“再次挑战”按钮的模板。特别注意,“再次挑战”按钮在结算界面可能出现的位置有多个(取决于是否有掉落),需要选取一个最稳定的特征点。
- 循环逻辑:
开始 -> 识别并点击“挑战” -> 识别并点击“准备” -> 等待(估算战斗时间,或识别“胜利”图标)-> 识别并点击“再次挑战” -> 循环 - 关键避坑点:
- “自动”战斗状态:在进入战斗后,脚本应能检测是否已开启“自动”战斗。如果没有,需要点击技能区域或“自动”按钮。这里不能依赖固定坐标,因为队伍配置不同,式神位置会变。一个可行的方案是识别“自动”二字的小图标,或者直接识别式神脚下的绿色“自动”光圈(作为备选模板)。
- 网络延迟容忍:在点击“准备”或“再次挑战”后,游戏会有网络通信过程。脚本必须等待界面稳定(新UI元素加载出来)后再进行下一轮识别,否则极易误点。我的经验是加入一个
pyautogui.sleep(1.5)的基础等待,再结合模板识别确认。 - 体力检测:必须在主循环中嵌入体力检测。一旦弹出体力不足的窗口,脚本应能识别并执行预设操作(如使用勾玉补充、使用饭团、或停止脚本并通知用户)。
4.2 困二十八层高效挂机:持久战的稳定性
困二十八是经验副本,通常挂机时间很长,对脚本的稳定性要求极高。
- 挑战:战斗时间更长,场景单一,但需要防止长时间运行后出现的各种意外(如游戏弹窗、模拟器/客户端卡顿、Windows系统通知)。
- 策略:与魂十一类似,但等待时间更长。这里可以引入更宽松的超时机制。例如,如果在预计战斗时间结束后仍未检测到胜利图标,可以尝试点击屏幕中央(防止游戏因未操作而进入待机),或者重启检测循环。
- 重要技巧——心跳检测:我设计了一个“心跳”线程,每隔一段时间(比如10分钟)就执行一次
capture_game_window,并尝试找一个必定存在的UI元素(如屏幕右上角的“金币”图标)进行匹配。如果连续多次匹配失败,可能意味着游戏窗口丢失、最小化或卡死。此时脚本可以尝试重新激活窗口,或者记录日志并安全暂停,而不是一直死循环。
4.3 御灵副本智能通关:动态决策的尝试
御灵副本的机制稍微复杂,BOSS会召唤小怪,有时需要集火BOSS,有时需要清理小怪。纯模板匹配的“无脑”点击在这里效率不高。
- 进阶策略:我们可以尝试简单的“动态决策”。例如:
- 在战斗界面,定期截图。
- 使用多个模板同时识别屏幕中是否存在“BOSS图标”和“小怪图标”。
- 如果识别到多个小怪,且小怪模板的匹配数量和位置符合“威胁较大”(比如三个小怪都出现了),则决策为“点击群体技能图标”或“点击小怪”。
- 如果只有BOSS或小怪威胁不大,则决策为“点击BOSS”或“使用单体技能”。
- 实现难点:这需要更精准的模板(小怪和BOSS在不同角度下的图像),以及一个简单的决策权重系统。
cv2.matchTemplate可以设置阈值来筛选出所有匹配位置,通过cv2.minMaxLoc只能找到最佳匹配点,对于多目标,需要使用np.where(result >= threshold)来获取所有位置,然后通过聚类去除相邻的重复框。
这个功能实现起来代码量会大增,且非常依赖模板质量和阈值调优,是项目中的一个进阶挑战。def find_all_templates(screenshot, template_path, threshold=0.7): template = cv2.imread(template_path, cv2.IMREAD_GRAYSCALE) screen_gray = cv2.cvtColor(screenshot, cv2.COLOR_BGR2GRAY) result = cv2.matchTemplate(screen_gray, template, cv2.TM_CCOEFF_NORMED) locations = np.where(result >= threshold) points = [] for pt in zip(*locations[::-1]): # 交换宽高,因为返回的是(col, row) points.append(pt) # 简单的非极大值抑制,去除过于接近的重复框 points = non_max_suppression(points, template.shape) return points # 返回所有匹配到的左上角坐标列表
5. 工程化与优化:让脚本从玩具变成工具
一个能跑起来的脚本和一个能稳定挂机数小时的脚本,中间隔着工程化的鸿沟。
5.1 配置与模板管理不要将模板图片路径、点击坐标、等待时间等参数硬编码在代码里。应该使用配置文件(如JSON、YAML)来管理。
{ "templates": { "challenge": "./assets/challenge_button.png", "ready": "./assets/ready_button.png", "victory": "./assets/victory_medal.png" }, "thresholds": { "challenge": 0.85, "ready": 0.9, "victory": 0.8 }, "delays": { "after_click": 1.5, "battle_wait": 18 } }这样,当游戏更新UI时,我们只需要替换assets文件夹下的图片,并微调配置文件中的阈值和延迟,而无需修改核心代码。
5.2 日志与错误处理完善的日志系统是调试和监控的基石。记录脚本的状态转换、识别结果、操作记录以及发生的异常。
import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.FileHandler('bot.log'), logging.StreamHandler()]) try: result = find_template(screenshot, template_path) if result: logging.info(f"成功匹配到 {template_path}, 置信度: {result[0]:.2f}") smart_click(...) else: logging.warning(f"未匹配到 {template_path}, 当前阈值: {threshold}") except Exception as e: logging.error(f"在处理 {template_path} 时发生错误: {e}", exc_info=True)当脚本在半夜挂机出错时,查看第二天的日志文件就能快速定位问题。
5.3 性能优化
- 截图区域优化:不要每次都截取全屏。根据模板可能出现的大致区域,只截取屏幕的一部分,可以大幅提升处理速度。例如,结算图标通常出现在屏幕中下方。
- 模板图片优化:模板图片应尽可能小,只包含特征鲜明的部分,去除无关背景。使用PNG格式(支持透明背景)有时比JPG更好。
- 识别频率优化:在等待战斗的长时间段内,不需要高频进行图像识别。可以采用“休眠+间歇性检测”的策略。
5.4 用户体验为脚本增加一个简单的GUI控制界面(可以用Tkinter、PyQt实现)是非常有价值的。界面可以显示当前状态(“运行中”、“已停止”、“遇到错误”)、日志预览,以及提供“开始”、“暂停”、“停止”按钮和配置修改入口。这能让脚本更像一个产品,而非一段冷冰冰的代码。
回顾整个项目,从构思到实现,最大的收获不是做出了一个能“代肝”的工具,而是系统地实践了“感知-决策-执行”这一自动化核心框架。图像识别技术就像项目的“眼睛”,而健壮的逻辑和异常处理则是“大脑”和“小脑”。每一个等待时间的微调,每一个识别阈值的测试,都是对细节的打磨。这个过程让我对Python在自动化领域的应用、OpenCV的基本操作以及软件鲁棒性设计有了更深的理解。当然,我必须再次强调,此类脚本应严格用于个人学习与研究,并充分尊重游戏运营方的规则,避免对游戏环境和其他玩家造成影响。技术的乐趣在于创造和解决问题的过程本身,这才是这个项目带给我的最大价值。
本文还有配套的精品资源,点击获取