从零实现《三角洲行动》手游自动跑刀脚本:ADB 直控 + OpenCV 视觉识别 + 固定点位搜刮)三角洲自动跑刀教程
从零实现《三角洲行动》手游自动跑刀脚本:ADB 直控 + OpenCV 视觉识别 + 固定点位搜刮
- 从零实现《三角洲行动》手游自动跑刀脚本:ADB 直控 + OpenCV 视觉识别 + 固定点位搜刮
- 一、前言
- 二、整体架构与技术栈
- 三、ADB 控制层:截图、点击、滑动
- 四、核心难点一:怎么判断"真进游戏了"?
- 五、核心难点二:识别"这把刷在哪个出生点"
- 5.1 用圆检测把小地图"圆形抠出来"
- 5.2 模板匹配识别出生点
- 六、核心难点三:自动拾取物资
- 七、把它串起来:固定路线
- 八、一个诚实的工程结论
- 九、总结
- 源码链接:
从零实现《三角洲行动》手游自动跑刀脚本:ADB 直控 + OpenCV 视觉识别 + 固定点位搜刮
声明:本文仅用于分享 Android 自动化与计算机视觉的工程实践,所有内容面向技术学习。竞技游戏的用户协议通常禁止第三方自动化工具,请勿用于线上对局。Demo 与思路仅供研究,后果自负。
作者小金龙,代码可获取
先看看效果:
三角洲
https://www.bilibili.com/video/BV1AP7X6AEfh/?spm_id_from=333.1387.homepage.video_card.click
视频效果
一、前言
很多人见过 B 站、CSDN 上的"自动跑刀"脚本——进图后自动跑到几个固定点位搜刮物资。这类脚本到底怎么实现?本文以《三角洲行动》手游为例,完整复盘一套**「电脑 ADB 直控真机 + OpenCV 视觉识别 + 固定路线」**的实现方案,重点讲清楚四个工程难点:
- 怎么控制手机(截图 / 点击 / 滑动)
- 怎么判断"真进游戏了"(霍夫圆检测小地图)
- 怎么识别"这把刷在哪个出生点"(小地图模板匹配)
- 怎么自动拾取物资(物资标签检测)
以及一个绕不开的结论:为什么"固定点位"方案可行,而"自主智能跑图"不可行。
二、整体架构与技术栈
PC (Python) ├─ ADB 控制层 —— 截图 / input tap / input swipe ├─ 视觉识别层(OpenCV) │ ├─ 霍夫圆检测 —— 检测小地图 → 判定是否进图 │ ├─ 模板匹配 —— 小地图比对 → 识别出生点 │ └─ 区域裁剪/遮罩 —— 物资标签、按钮定位 └─ 决策/路线层 —— 每个出生点一条固定动作序列 ↓ ADB Android 真机 / 模拟器技术栈:
| 模块 | 技术 |
|---|---|
| 设备控制 | ADB(platform-tools)input tap/swipe、exec-out screencap |
| 图像处理 | OpenCV-Python(HoughCircles、matchTemplate、掩膜) |
| 数值计算 | NumPy |
| 语言 | Python 3.10 |
关键前提:MIUI / HyperOS 等系统默认禁用 ADB 模拟点击(
INJECT_EVENTS权限),需在开发者选项中开启**「USB 调试(安全设置)」**(需插 SIM 卡 + 登录账号)。开启后adb shell input tap/swipe才可用,这是整套方案的基础。
三、ADB 控制层:截图、点击、滑动
一切的基础是「能截屏、能操作」。用subprocess包一层 ADB:
importsubprocess,timeimportnumpyasnpimportcv2 ADB=r"D:\platform-tools\adb.exe"SERIAL="你的设备序列号"# adb devices 查看defadb(*args,binary=False,timeout=20):cmd=[ADB,"-s",SERIAL]+list(args)r=subprocess.run(cmd,capture_output=True,timeout=timeout)returnr.stdoutifbinaryelser.stdout.decode("utf-8","ignore")defscreenshot():"""exec-out screencap 直接拿二进制 PNG,OpenCV 解码,无需落盘。"""raw=adb("exec-out","screencap","-p",binary=True)returncv2.imdecode(np.frombuffer(raw,np.uint8),cv2.IMREAD_COLOR)deftap(x,y,wait=1.2):adb("shell","input","tap",str(x),str(y))time.sleep(wait)defswipe(x1,y1,x2,y2,dur_ms=300):adb("shell","input","swipe",str(x1),str(y1),str(x2),str(y2),str(dur_ms))对局内的"走位"和"转视角",本质也是 swipe:
- 走位= 在左手虚拟摇杆上按住拖动一段时间(
swipe的 duration 就是按住时长)。注意摇杆死区大,必须大幅度推满才会走:
defmove_forward(seconds):# 摇杆中心(430,760),向上推满 → 前进swipe(430,780,430,350,int(seconds*1000))deflook_right_90():# 右侧视角区横向滑动,约 480px ≈ 转 90°swipe(1700,500,2180,500,350)横屏分辨率 2400×1080 下,这些坐标都是实测标定出来的。
四、核心难点一:怎么判断"真进游戏了"?
这是最坑的一步。游戏从「点击出发」到「真正进图」要经历:匹配 → 选干员 → 着陆确认 → 加载(约30s)→ 进图。早期我用"固定等 30 秒"或"识别匹配界面文字",都不稳——匹配时间不固定,加载界面文字时有时无,经常还没进游戏就开始乱跑刀。
突破口:对局内左上角有个圆形小地图,而菜单/加载界面没有。于是用霍夫圆检测(HoughCircles):检测到小地图圆 = 真进游戏。
defdetect_minimap_circle(screen=None):"""检测左上角小地图圆,返回 (cx,cy,r) 或 None。"""ifscreenisNone:screen=screenshot()roi=screen[0:460,0:500]# 只在左上角找,提速g=cv2.medianBlur(cv2.cvtColor(roi,cv2.COLOR_BGR2GRAY),5)cs=cv2.HoughCircles(g,cv2.HOUGH_GRADIENT,dp=1,minDist=300,param1=100,param2=40,minRadius=140,maxRadius=215)ifcsisNone:returnNonec=cs[0][0]return(int(c[0]),int(c[1]),int(c[2]))defis_in_game(screen=None):returndetect_minimap_circle(screen)isnotNone实测圆心稳定在(~277, 210)、半径~145,几乎每把一致。is_in_game()就是可靠的"进图判定",彻底解决了时序错位问题。
五、核心难点二:识别"这把刷在哪个出生点"
关键前提:很多人以为出生点随机,其实不少撤离类地图的出生点就那固定的几个。这意味着——只要识别出本把刷在哪个出生点,就能跑预先调好的对应路线。这正是"固定点位脚本"成立的根基。
识别靠小地图:它是北朝上 + 玩家居中的,所以同一个出生点,周围地形布局每次都一样,非常适合模板匹配。
5.1 用圆检测把小地图"圆形抠出来"
直接方形裁剪会带进会变化的世界背景(四个角),干扰匹配。更好的做法是:圆检测定位 → 圆形掩膜把圆外涂黑 → 归一化尺寸:
defget_minimap(screen=None):"""抠出圆形小地图(圆外涂黑),归一化 360x360。"""ifscreenisNone:screen=screenshot()circ=detect_minimap_circle(screen)ifcircisNone:returnNonecx,cy,r=circ r=min(r+8,cx,cy)crop=screen[cy-r:cy+r,cx-r:cx+r].copy()mask=np.zeros(crop.shape[:2],np.uint8)cv2.circle(mask,(crop.shape[1]//2,crop.shape[0]//2),r,255,-1)crop=cv2.bitwise_and(crop,crop,mask=mask)# 圆外涂黑returncv2.resize(crop,(360,360))配图:圆检测 + 掩膜后抠出的干净圆形小地图。
有些出生点小地图上直接带地名(如"军营"“帐篷区”),还能用 OCR 读字辅助判断:
5.2 模板匹配识别出生点
提前在每个出生点出生瞬间采集一张圆形小地图存为模板(spawn1.png、spawn2.png…),进图后拿当前小地图逐一比对,取最高分:
importosdefdetect_spawn(screen=None,threshold=0.6):mm=get_minimap(screen)ifmmisNone:returnNonebest=Noneforfinos.listdir("templates/spawns"):tpl=cv2.imread(os.path.join("templates/spawns",f))iftplisNoneortpl.shape!=mm.shape:continueres=cv2.matchTemplate(mm,tpl,cv2.TM_CCOEFF_NORMED)_,maxv,_,_=cv2.minMaxLoc(res)ifbestisNoneormaxv>best[1]:best=(os.path.splitext(f)[0],float(maxv))returnbestifbestandbest[1]>=thresholdelseNone采集规范(踩坑经验):模板必须统一风格才匹配得准——① 都在出生瞬间、未移动时采集(玩家居中、初始朝向);② 统一用"圆检测 + 掩膜 + resize 360×360";③ 小地图上的玩家箭头、队友标记、区域线是动态噪声,进阶可把中心圆区再挖掉,只比静态地形。
六、核心难点三:自动拾取物资
走到物资点后,画面右上方会弹出物资标签(如"9V电池")。坑点:拾取不是点屏幕固定的"交互键",而是点这个标签本身。
PICKUP_LABEL=(1600,280)# 右上角物资标签位置(横屏)defpickup():tap(*PICKUP_LABEL,wait=0.6)点中后画面提示"已拾取 XX 到保险箱",即成功。
进阶可用模板匹配/颜色检测先判断"标签框是否存在",存在再点,避免空点。
七、把它串起来:固定路线
每个出生点配一条固定动作序列,识别到哪个出生点就跑哪条:
ROUTES={# 降落点:出生 → 前进2秒 → 右转90° → 点标签拾取"spawn2":[("forward",2),("look",480,0),("pickup",)],# 其它出生点继续补……}defrun_route(name):forstepinROUTES.get(name,[]):ifstep[0]=="forward":move_forward(step[1])elifstep[0]=="look":swipe(1700,500,1700+step[1],500,350)elifstep[0]=="pickup":pickup()主循环:
defmain_loop():deploy()# 自动点出发/指挥出战进局ifnotwait_in_game(timeout=60):# 圆检测等进图returnspawn=detect_spawn()# 识别出生点ifspawn:run_route(spawn[0])# 跑对应固定路线extract()# 撤离/自杀back_to_lobby()# 退出结算 → 回大厅八、一个诚实的工程结论
做下来最大的体会:"识别"不难,"导航"才难。
- 能做到:CV 识别进图、识别出生点、识别物资标签;固定出生点 + 固定路线 + 自动进局/撤离/循环。
- 做不到(靠脚本盲操作):像人一样"自主跑图、避开墙和障碍、找到好货"。3D 场景里盲走 ADB 反复撞墙撞围栏,这需要深度感知 + 路径规划(等于游戏 AI 自主导航),是另一个数量级的工程;而真正的"自动打人"= 目标检测自瞄 = 顶格外挂,必被反作弊封号。
所以"固定点位"方案之所以流行,正是因为它用确定性(固定出生点+固定路线)绕开了最难的自主导航——这是工程上的务实取舍。
九、总结
| 难点 | 解法 |
|---|---|
| 控制手机 | ADBinput tap/swipe+screencap |
| 进图判定 | 霍夫圆检测小地图is_in_game() |
| 出生点识别 | 圆形小地图掩膜 +matchTemplate |
| 物资拾取 | 点右上角物资标签 |
| 智能跑图 | ❌ 用"固定出生点+固定路线"务实绕开 |
整套方案把Android 自动化和OpenCV 模板匹配 / 霍夫圆检测结合起来,是一个很好的"CV + 自动化"工程练习。
再次提醒:仅供学习计算机视觉与自动化技术,切勿用于线上竞技对局
源码链接:
如果这篇对你有帮助,欢迎点赞收藏。技术交流,理性使用。
