ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python+adb+OpenCV实战:阴阳师后台自动战斗脚本的模板匹配与稳定运行

Python+adb+OpenCV实战:阴阳师后台自动战斗脚本的模板匹配与稳定运行 简介这是一份基于Python的阴阳师后台辅助工具源码面向阴阳师玩家和自动化脚本学习者解决手动重复刷副本、刷勾玉的痛点。工具利用adb对模拟器进行截图再通过OpenCV图像匹配完成找图找色与模拟点击从而实现后台挂机操作兼容雷电模拟器和网易MuMu适合在电脑前处理其他事务时使用。资源包共18个文件核心为6个Python脚本分别负责地域鬼王、结界突破、秘闻副本和御魂突破券等玩法另含5个bmp位图与3个png图片作为界面识别素材以及依赖清单和许可证说明整体压缩后仅112KB轻量且便于部署。目前已有2224人学习下载代码内刻意加入2至3秒随机等待以降低行为风险虽然整体节奏偏慢但也体现了较完善的自动控制思路。读者可以直接参考这一整套源码来部署挂机方案也可以借此学习adb指令调用、OpenCV模板匹配、随机延时策略以及多模块脚本组织方式便于后续二次开发和功能扩展。1. 后台刷勾玉为什么选择 adb 截图而不是窗口前端点击玩阴阳师手动刷日常真的很消磨精力尤其地域鬼王、结界突破这种每天必做的内容。这套基于 Python 3.8 的辅助工具把“自动战斗”拆成了截图、找图、点击三个动作关键点在于它用的是 adb 操作模拟器内部事件而不是用 pyautogui 操控电脑鼠标所以模拟器最小化到后台后依然能正常截屏和点击。源码里 diyu.py、yuhun.py、tupo.py、liaotupo.py、miwen.py 分别对应地域鬼王、御魂、个人结界突破、寮突破和秘闻副本配合 img 目录下的模板截图完成识别。它牺牲了运行速度来换取稳定性每一次操作后随机等待 2 到 3 秒目的就是降低账号风险。下文从环境配置、依赖安装讲到核心模块实现最后给出稳定性调优和排错的具体做法。2. Python 3.8 与雷电模拟器的可复现配置2.1 依赖环境requirements.txt 与虚拟环境这一套脚本对运行环境的要求其实很死板。项目说明里明确写了 Python 3.8这不是偷懒而是 OpenCV 和 numpy 在 3.8 下的二进制包最稳定。如果你电脑上装的是 Python 3.10 或更高版本运行时会遇见 numpy 的np.int属性被移除导致报错或者 openssl 与 adb 的交互异常。所以第一步是安装 Python 3.8。Windows 用户下载安装包时记得勾选“Add Python to PATH”否则后面python命令不可用macOS 用户建议用 pyenv 管理版本避免和系统自带 Python 冲突。cd onmyoji python -m venv venv source venv/bin/activate # Windows: venv\Scripts\activate python --version # 输出 Python 3.8.x pip install -r requirements.txt逻辑说明先创建虚拟环境再激活最后安装依赖。requirements.txt里通常包含 opencv-python、numpy、Pillow 和 adb 相关的 Python 包。这里不要直接pip install opencv-python装最新版因为新版 OpenCV 对旧版模板图片读取方式有变化部分.bmp截图读进来会出现通道顺序颠倒找图相似度会大幅下降。2.2 模拟器分辨率与渲染模式模拟器分辨率必须设置成 1024×576这个尺寸与 img 目录下所有模板截图严格对应。如果模拟器还是 1280×720模板匹配相似度会骤降find_template返回的点位会偏到按钮外。雷电模拟器的设置路径是模拟器右上角菜单栏 → 软件设置 → 高级设置 → 分辨率选择自定义 → 宽度 1024高度 576保存后重启模拟器。网易 MuMu 用户则需要进入设置中心修改分辨率并且在显卡渲染模式里选“兼容OpenGL”。这一步非常关键MuMu 默认的 DirectX/Vulkan 渲染模式最小化后不会刷新画面adb 截图出来的始终是最后一帧脚本会一直认为界面没变。雷电模拟器在后台最小化时默认也能刷新但仍建议把渲染模式调整为兼容模式减少显卡负担。配置项雷电模拟器网易 MuMu分辨率自定义 1024×576自定义 1024×576渲染模式默认或兼容兼容OpenGLadb 端口555516384/16386后台最小化窗口可最小化窗口关闭无效设置完成后进入模拟器打开一次阴阳师再最小化窗口用下面的 adb 命令确认分辨率已生效。很多后台脚本跑不动都是因为改完分辨率没有重启模拟器截图尺寸还是旧值导致的。2.3 adb 连接与后台运行基础雷电模拟器自带 adb 可执行文件路径一般在安装目录下。我建议把 platform-tools 加入系统 PATH然后在项目根目录执行adb devices adb shell wm size如果输出包含Physical size: 1024x576说明分辨率正确。如果adb devices里查不到模拟器雷电模拟器执行adb connect 127.0.0.1:5555 adb -s 127.0.0.1:5555 shell wm sizeMuMu 的 adb 端口不固定需要在模拟器设置里的“其他”选项找到端口号再执行adb connect 127.0.0.1:端口。另外要特别注意如果系统 PATH 里的 adb 和模拟器自带 adb 版本不一致优先用模拟器自带的否则可能出现adb offline或者截图返回空数据。后台辅助这个词在 Windows 下还有一层含义模拟器窗口可以最小化但不能设置为“关闭后最小化到托盘”的退出模式。脚本依赖的是模拟器进程进程一旦结束后台任务也就断了。我在实际跑的时候是把模拟器放到独立桌面或屏幕角落避免误关。3. tools.py截图、找图、模拟点击的统一封装3.1 利用 adb 拉取 PNG 流并清洗换行符号tools.py 是源码里的公共库diyu.py、yuhun.py、tupo.py 都依赖它。它把 adb 指令和 OpenCV 算法包装成几个顶层函数业务模块完全不关心 Android 底层通信细节。核心截图函数的思路是用subprocess.check_output而不是os.system因为前者能直接拿到字节流后者只能返回退出码。import subprocess import numpy as np import cv2 def shot(deviceemulator-5554): cmd [adb, -s, device, exec-out, screencap, -p] raw subprocess.check_output(cmd) if raw.startswith(b\xff\xd8): raw raw.replace(b\r\n, b\n) arr np.frombuffer(raw, dtypenp.uint8) return cv2.imdecode(arr, cv2.IMREAD_COLOR)参数说明exec-out会把屏幕 PNG 的原始字节流输出到 stdout-p指定 PNG 格式np.frombuffer把字节转成 numpy 数组cv2.imdecode再解码成 OpenCV 的 BGR 图像。换行清洗是因为 Windows 下 adb.exe 可能把\r\n转成\r\r\n不解码这步会导致图片下半部分扭曲。如果你发现截图只有上半部分基本都是这个原因。3.2 模板匹配与多尺度匹配细节img 目录下的 tiaozhan.bmp、zhunbei.bmp、shengli.bmp、qianshengli.bmp 等都是界面关键位置的截图。匹配函数常见写法如下def find(screen, template, threshold0.8): h, w template.shape[:2] res cv2.matchTemplate(screen, template, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc cv2.minMaxLoc(res) if max_val threshold: return max_loc[0] w // 2, max_loc[1] h // 2, max_val return None参数解读threshold默认 0.8。TM_CCOEFF_NORMED是标准化相关系数范围 -1 到 1对光照变化相对不敏感。相同分辨率下模板来自同一画面0.8 阈值足够如果模拟器画面比例被调整就要调低到 0.7 并开启多尺度匹配。多尺度匹配流程是缩放模板 90% 到 110%每次缩放后重新匹配取最高相似度点和坐标。但注意多尺度会带来 5 到 8 倍耗时建议只在diyu.py启动时用主循环内不要用。另外cv2.imread读.bmp时默认返回 BGR 顺序读.png时会带 alpha 通道。如果模板图片流失效常见做法是在加载时统一转换template cv2.imread(img/tiaozhan.bmp) template cv2.cvtColor(template, cv2.COLOR_BGRA2BGR)这行代码放在循环外避免重复解码浪费 CPU。对于需要频繁匹配的按钮最好在启动时把所有模板一次性读入内存不要每次循环都访问磁盘。源码中 img 文件不多完全可以做到。3.3 点击与随机等待的安全节奏为了防止被判定为脚本源码里故意把点击间隔设为random.uniform(2.0, 3.0)。配合 1024×576 的低分辨率实际刷一轮地域鬼王大约需要 5 分钟比手速快一点但远不如连点器疯狂。点击动作如下import random import time import subprocess def tap(device, x, y): offset_x random.randint(-3, 3) offset_y random.randint(-3, 3) subprocess.run( [adb, -s, device, shell, input, tap, str(x offset_x), str(y offset_y)] ) def wait(): time.sleep(random.uniform(2.0, 3.0))代码说明点击前给坐标加 ±3 像素的偏移模仿真实手指落点抖动等待时间用均匀分布打散操作节奏。如果你要多开模拟器建议把等待基础值改成比例系数例如BASE_WAIT 2.5每次实际等待BASE_WAIT * random.uniform(0.8, 1.2)避免两个窗口操作节奏完全同步反而更容易触发检测。4. 地域鬼王、结界突破与御魂副本的模块级实现逻辑4.1 diyu.py地域鬼王从筛选到结算的流程控制diyu.py 的核心是每天稳定刷勾玉所以它被设计成一个带状态判断的循环。每一步都用find函数判断当前界面找到对应模板后再点击。以地域鬼王为例完整顺序是探索界面 → 点击地域鬼王图标 → 点击筛选 → 点击热门标签 → 点击鬼王头像 → 点击挑战 → 等待战斗结算。项目里 remen.png 和 remen1.png 分别对应“热门”标签和热门列表第一项shaixuan.png 是筛选按钮。如果界面加载稍慢模板暂时找不到循环会等待 3 次再报错退出而不是无限卡住。状态匹配模板点击动作超时重试在地域鬼王界面shaixuan.png点击筛选3 次筛选面板remen.png点击热门列表3 次选择鬼王remen1.png点击第一项3 次阵容确认zhunbei.bmp点击准备5 次战斗中无模板等待胜利180 秒胜利结算shengli.bmp点击继续3 次对应代码逻辑用 while 循环表达每次判断一个状态while True: ret find(screen, remen1.png) if ret: tap(device, ret[0], ret[1]) wait() continue ret find(screen, zhunbei.bmp) if ret: tap(device, ret[0], ret[1]) wait() continue ret find(screen, shengli.bmp) if ret: tap(device, ret[0], ret[1]) break这里的continue很关键每次只处理一个动作处理完立刻重新截图避免界面状态变化后坐标时效性问题。如果直接把多个模板判断写在一次截图里可能会出现点击顺序错乱。4.2 tupo.py 与 liaotupo.py个人突破和寮突破的差异个人结界突破与寮突破逻辑类似区别在票的来源和入口坐标。tupo.py 进入“结界突破”界面后按顺序点击可选对手匹配 tiaozhan.bmp 之后立刻点击挑战。如果连续 5 次没有匹配到挑战按钮说明当前没有可打的对手脚本回城重新进入等待下一轮刷新。这个模块里一个值得保留的设计是每次打完一个对手都会回到主界面后重新点击“结界突破”按钮而不是停留在关卡选择界面。原因是防止模拟器卡在结算动画里。这个“全部重置再进入”的思路对稳定性非常有效代价是每次额外耗时约 5 秒。liaotupo.py 面向寮突破入口在“阴阳寮 - 结界”里。因为寮成员击破顺序有限制所以代码里增加了判断当前是否已经激活下一关的逻辑。这里我一般会直接读取截图上文字区域的像素均值来判断是否高亮比再找一套模板快。如果想手动调整可以把retry_limit参数从 5 改到 8给慢网络更多时间。4.3 yuhun.py 与 miwen.py没有战斗模板时用颜色统计兜底yuhun.py 主要为了刷突破券它的流程相对简单进入御魂副本 → 等待战斗结束 → 领取奖励 → 再次进入。难点在于御魂十层怪物分布随机有时候进入副本后要等一小段时间才开战截图里没有固定的“战斗中”模板可匹配所以源码用底部技能栏区域的颜色统计来判断战斗状态。roi screen[450:576, 0:1024] # 底部技能区域 mean_val roi.mean() if mean_val 120: # 战斗中不做操作 pass else: # 战斗结束寻找下一步按钮 ret find(screen, shengli.bmp)参数说明mean_val是选定区域的平均像素亮度。技能栏在战斗中的特效颜色更亮数值一般高于 120回到主界面后该区域颜色偏暗低于 100。这个阈值不是绝对的使用前先在目标模拟器上截两张图算一下均值。yuhun.py的一个常见误用是直接拿整张截图求均值结果因为场景整体亮度高而误判必须限定在技能栏区域。miwen.py 是秘闻副本层数高、技能特效复杂而且部分副本有特殊机制比如“犬神反击”动画很长。源码用 color 统计加超时退出两种方式配合。我在跑的时候会额外加一个保护当等待超过 90 秒后主动截取一张全屏并用shengli.bmp做最终检测如果匹配到就立刻跳出等待。这里的关键是超时不能设太短八岐大蛇传记动画可能长达 20 秒建议按实际结界突破场景调整。5. 从跑通到稳定验证命令、重截图与 adb 排错5.1 三个命令确认脚本健康脚本并非放到后台就完事建议在一轮循环结束后手动执行三个命令检查adb devices adb shell dumpsys window | grep mCurrentFocus python diyu.py --dry-run第一个命令看设备是否在线第二个查看当前前台 Activity 是否为阴阳师如果显示的是桌面说明脚本点错了返回键第三个是我建议你在主函数里加的模拟运行参数--dry-run只执行截图和找图并输出日志不触发点击。这样能快速定位是识别失败还是点击坐标错。5.2 模拟器分辨率修改后的重新截图技巧如果不想用 1024×576可以改分辨率但必须重新截取 img 目录下的模板。我一般先用 adb 截一张全屏图然后用 OpenCV 的选择框把目标区域保存下来import cv2 img cv2.imread(full.png) rect cv2.selectROI(select, img) cv2.imwrite(button.bmp, img[rect[1]:rect[1] rect[3], rect[0]:rect[0] rect[2]])这段代码会打开一个窗口鼠标框选按钮区域后按空格保存。保存成 bmp 是为了保持原始像素避免 jpg 压缩改变边缘颜色。模板命名要对应该含义比如tiaozhan.bmp对应挑战按钮zhunbei.bmp对应准备按钮。新模板直接替换 img 目录下同名文件脚本无需修改。5.3 最后一个技巧adb 设备枚举不到时的恢复顺序模拟器后台运行久了adb 偶尔会掉线。恢复顺序不要乱先执行adb kill-server再执行adb start-server最后执行adb devices。如果还看不到设备才重启模拟器。注意雷电模拟器自带 adb 版本可能和系统 PATH 里的老版本冲突建议项目目录放一个 platform-tools 目录并在 tools.py 开头覆盖环境变量import os os.environ[PATH] ( os.path.dirname(__file__) os.sep platform-tools os.sep os.environ[PATH] )这一行放到源码头部能解决绝大多数 Windows 下 adb 设备消失的问题。然后重新运行 Python 脚本观察终端输出中的截图尺寸和模板匹配率即可。本文还有配套的精品资源点击获取
返回列表