☰
Python+OpenCV+ADB实现微信跳一跳辅助:感知决策执行全链路
2026/10/12 14:53:49 网站建设 项目流程

简介:这份资源面向对Python游戏自动化与计算机视觉感兴趣的开发者,围绕微信小游戏「跳一跳」实现一套自动跳跃辅助工具,涉及图像处理、目标识别与模拟点击等技能点,适合具备Python基础、希望动手实践CV与自动化控制的读者。压缩包共279个文件,约26.76MB,以217个png截图与素材、36个json配置、6个py脚本为主,另含少量md说明、txt文档及adb相关可执行文件与dll依赖,目录结构便于按模块查阅。已有317人学习。源码完整呈现了从屏幕截图、OpenCV识别方块位置、计算跳跃距离到pyautogui模拟点击的闭环流程,并包含多机型配置文件与容错思路,读者可借此理解图像灰度化、边缘检测、模板匹配等知识在实际项目中的落地方式,同时体会自动化脚本的节奏控制与鲁棒性设计,仅建议用于学习研究。

1. 跳一跳辅助:用Python来玩微信跳一跳,到底在玩什么

跳一跳这个小游戏,规则简单到一句话能说完:按住屏幕蓄力,松开后棋子跳到下一个方块上,落在中心加分。但真拿手去点,连续跳几十次不出错的人不多。于是「用 Python 来玩跳一跳」这件事,本质不是写个外挂去刷分,而是搭一条完整的感知—决策—执行链路:截屏拿到当前画面,算出棋子和目标方块的位置,根据两点距离换算成按压时长,再把这个时长通过调试通道发给手机。整条链路里,图像识别是眼睛,距离到时间的换算是大脑,ADB 是手。

这套东西适合谁?适合已经会一点 Python、想找一个「有即时反馈、又不至于太复杂」的计算机视觉练手项目的人。它把 OpenCV 的模板匹配、颜色阈值、坐标计算、以及 adb 命令调用全串了一遍,做完你对「截图→处理→下发指令」这个闭环会有实感。下面按我实际搭过的顺序讲,参数和坑都摊开说。

2. 环境与链路:把截屏、算距离、发指令三段接起来

2.1 为什么选 ADB 而不是别的通道

常见做法是用 ADB(Android Debug Bridge)跟手机通信,原因很实际:它同时能干三件事——截屏、读屏幕分辨率、模拟按压。你不需要在手机上装任何东西,只要打开 USB 调试,电脑端 adb devices 能看到设备就行。另一条路是投屏工具自带的控制接口,但那些接口往往不开放按压时长这种细粒度参数,做跳一跳这种「按毫秒级控制蓄力」的场景不够用。

链路是这样:adb shell screencap把当前帧存到手机临时目录,adb pull拉到电脑;OpenCV 读图算出棋子底部中心和目标方块中心;两点欧氏距离乘以一个经验系数得到按压毫秒数;adb shell input swipe x y x y duration在屏幕同一点上「滑动」指定时长,等效于长按。注意最后这条命令的四个坐标写成同一个点,duration 就是按压时间。

2.2 最小可运行骨架

先装依赖,再写主循环。依赖只有两个:opencv-python 和纯标准库的 subprocess。

pip install opencv-python numpy adb devices # 确认设备已授权,输出里有 device 字样
import subprocess import time import cv2 import numpy as np def screencap(path="screen.png"): # 截屏到手机临时目录再拉回本地,避免直接 stdout 传二进制被截断 subprocess.run(["adb", "shell", "screencap", "-p", "/sdcard/s.png"], check=True) subprocess.run(["adb", "pull", "/sdcard/s.png", path], check=True) return cv2.imread(path) def press(ms): # 同一点 swipe,duration 即按压时长;坐标取屏幕中下部任意固定点 subprocess.run( ["adb", "shell", "input", "swipe", "500", "1600", "500", "1600", str(ms)], check=True ) if __name__ == "__main__": for i in range(30): img = screencap() # 这里先留空,识别逻辑在下一章补 ms = 600 # 占位,实际由距离换算 press(ms) time.sleep(1.2) # 等棋子落地动画结束再截下一帧

逻辑说明:screencap -p的-p是 PNG 格式,不加会输出 raw 格式,OpenCV 读不了。input swipe用同点坐标是社区里验证过的长按等效写法,比input tap更能控制时长。time.sleep(1.2)是给落地动画留时间,太短会截到棋子还在空中的帧,识别直接崩。

参数说明:swipe 的坐标要落在游戏可交互区域内,一般取屏幕宽度一半、高度约 80% 处;duration 单位是毫秒,跳一跳里大致在 200 到 1500 之间,超过 1500 基本是跳飞。这个骨架跑起来棋子会乱跳,因为 ms 是写死的,下一步就是把识别接上。

3. 图像识别:棋子定位与目标方块中心怎么算

3.1 棋子定位:模板匹配比颜色阈值稳

棋子是一个固定形状的小人,颜色随皮肤变,但轮廓稳定。我一般用模板匹配:先手动截一张只含棋子的图存成 template.png,之后每帧用cv2.matchTemplate找最匹配的位置。比颜色阈值稳的原因是,背景方块颜色会变(尤其连续跳到不同色块时),纯靠颜色分割容易把方块误判成棋子。

def find_piece(img, tpl): res = cv2.matchTemplate(img, tpl, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(res) if max_val < 0.7: # 匹配度过低说明这一帧没找到,直接跳过 return None h, w = tpl.shape[:2] # 返回棋子底部中心:跳一跳的落点是棋子脚底,不是几何中心 return (max_loc[0] + w // 2, max_loc[1] + h) tpl = cv2.imread("template.png") piece = find_piece(img, tpl)

逻辑说明:TM_CCOEFF_NORMED返回归一化相关系数,越接近 1 越像。minMaxLoc取最大值位置就是匹配左上角。关键在返回值——落点用棋子底部中心,因为游戏判定的是脚底接触方块的位置,用几何中心会系统性偏上,跳出来总是差一点。

参数说明:阈值 0.7 是经验值,光照变化大可以降到 0.6,但太低会误匹配到背景。模板图要单独截,别从整屏里抠,边缘留 2 像素背景反而匹配更准。

3.2 目标方块中心:从棋子位置往右上找

跳一跳里下一个方块永远在棋子的右上方(偶尔正上方)。所以不用全图找,只在棋子右上区域做处理,速度快也不容易误判。方块顶面是个近似菱形或圆形的色块,用边缘检测加轮廓筛选比较通用。

def find_target(img, piece): px, py = piece # 只取棋子右上方区域,减少干扰 roi = img[0:py, px:] gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150) contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) best = None for c in contours: area = cv2.contourArea(c) if area < 500: # 太小的噪点丢掉 continue x, y, w, h = cv2.boundingRect(c) cx, cy = x + w // 2 + px, y + h // 2 # 换算回全图坐标 if best is None or area > best[0]: best = (area, cx, cy) return None if best is None else (best[1], best[2])

逻辑说明:ROI 裁剪把搜索范围压到棋子右上方,Canny 找边缘,findContours拿外轮廓,按面积挑最大的那个当目标方块。RETR_EXTERNAL只取最外层,避免方块内部纹理产生嵌套轮廓。坐标换算别忘了加回px,这是新手最容易漏的一步,漏了目标点会整体左移。

参数说明:Canny 的 50/150 是通用起点,方块和背景对比弱时把低阈值降到 30。面积阈值 500 对应常见分辨率下的方块大小,屏幕分辨率高就往上调。如果方块是纯色且和背景接近,边缘检测会失效,这时改用颜色阈值cv2.inRange更靠谱,具体阈值用取色工具量一下方块颜色再定。

3.3 距离到按压时长的换算

拿到棋子底部中心 (px, py) 和目标中心 (tx, ty),距离就是欧氏距离。按压时长和距离近似线性,系数靠实测标定。

import math def distance_to_ms(piece, target, k=1.35): d = math.hypot(target[0] - piece[0], target[1] - piece[1]) return int(d * k)

逻辑说明:k是核心系数,物理含义是「每像素距离需要多少毫秒蓄力」。它跟屏幕分辨率、游戏版本都有关,必须自己标。标定方法:先随便给个 k 跑一次,看跳近了还是跳远了,近了就调大 k,远了调小,反复三五次就能收敛到稳定值。

参数说明:我这边 1080 宽度的屏,k 落在 1.3 到 1.4 之间。换手机要重新标,别直接抄。另外距离算出来要加个下限保护,太近的目标如果 ms 太小,input swipe可能被系统当成点击,加个max(ms, 200)兜底。

4. 避坑与排查:那些让辅助当场翻车的细节

4.1 截屏拿到的是黑图或旧帧

现象:识别结果乱跳,或者连续几帧棋子位置完全一样。原因通常是screencap还没写完就去 pull,或者手机锁屏了。解决:在 screencap 和 pull 之间不用加 sleep(check=True会等命令返回),但要确认手机常亮、游戏在前台。连续几帧一样基本是 pull 到了缓存文件,把手机端临时文件名每次改成带时间戳的,或者 pull 完就删。

4.2 棋子匹配突然失效

现象:跑了几十次后max_val掉到 0.5 以下,返回 None,程序卡住。原因是棋子跳到了和模板背景颜色接近的方块上,或者游戏出了新皮肤。解决:模板图选在纯色背景下截,别带花哨背景;代码里对 None 做处理,跳过这一帧重新截,而不是直接崩。连续三次 None 就停,说明环境变了,得重新截模板。

4.3 按压时长对但总是跳偏

现象:距离算得没错,k 也标过,但落点总是偏左或偏右。原因是落点用了棋子几何中心而不是脚底,或者目标中心取的是方块外接矩形中心而方块是斜的。解决:棋子返回值改成底部中心(前面代码已经这么做了);目标方块如果是菱形,外接矩形中心其实约等于菱形中心,问题不大,但如果是长条形方块,得取顶面中心而不是整个轮廓中心。

4.4 连续跳时节奏被打乱

现象:前几次准,后面越跳越偏。原因是time.sleep固定 1.2 秒,但游戏加分动画、方块生成动画时长会变,截到了过渡帧。解决:别用固定 sleep,改成轮询——截一帧判断棋子是否静止(连续两帧棋子位置差小于 3 像素),静止了再算下一步。这样节奏自适应,代价是每步多截一两次图。

4.5 系数 k 换了分辨率就废

现象:同一台电脑换了个手机,或者改了游戏窗口大小,全乱。原因是 k 和像素距离强相关。解决:把 k 做成按屏幕宽度归一化的形式,比如k = base_k * (screen_width / 1080),换设备时只调 base_k。更稳的做法是首次运行时自动标定:故意跳一次,根据落点和目标的偏差反推 k,省得手动试。

5. 进阶:让这套辅助从「能跳」到「跳得准」

前面四章跑通,你已经能连续跳个几十次了。但想让它稳定上高分,还有两件事值得做。

第一件是自适应标定 k。手动试系数太玄学,我一般写成自动的:第一次按压用一个保守的 k,跳完后截屏,用模板匹配找到棋子新位置,和预期落点比较,偏差比例直接反馈修正 k。跑三五次就收敛,换设备也不用重调。核心就一行比例修正:

def calibrate(k, actual_d, expect_d): # actual_d 是实际落点到目标的距离,expect_d 是期望落点距离 # 跳远了说明 k 偏大,按比例缩回来 return k * (expect_d / actual_d) if actual_d > 0 else k

逻辑说明:expect_d理想情况是 0(正好落在中心),但实际用「目标中心到棋子新位置的距离」当反馈量更稳,避免除零。这个修正每跳一次做一次,k 会自己往真值靠。

第二件是落点微调。跳一跳落在方块中心有额外加分,而中心位置可以通过方块轮廓的几何中心更精确地估计。前面用外接矩形中心是图省事,进阶做法是对轮廓做cv2.minAreaRect拿最小外接旋转矩形,取它的中心,对斜方块更准。代价是计算量略大,但一帧几十毫秒,完全够用。

还有个容易被忽略的点:按压时长的整数毫秒精度其实不够。input swipe的 duration 是毫秒整数,但相邻两次跳跃需要的时长可能只差几毫秒,四舍五入就偏了。缓解办法是让 k 的标定更细,把系统误差摊到系数里,而不是指望单次精度。

最后说个我踩过的坑:别在游戏正在加载或弹窗的时候发指令,那时候截到的帧根本没有方块,识别返回 None,程序要么崩要么乱跳。加一个「连续 N 帧找不到目标就暂停并提示」的保护,比事后 debug 省事得多。这套东西的价值不在刷多高分,而在于它是一条完整的、你能完全掌控的感知—决策—执行链路,改改识别逻辑就能迁移到别的场景。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询