基于图像识别的适用于模拟器的fgo脚本,目前支持稳定3T的刷本 您所在的位置:网站首页 明日方舟刷本脚本推荐 基于图像识别的适用于模拟器的fgo脚本,目前支持稳定3T的刷本

基于图像识别的适用于模拟器的fgo脚本,目前支持稳定3T的刷本

2024-01-19 13:15| 来源: 网络整理| 查看: 265

已完成的内容

需自主添加的内容

使用的资源

主要思路

界面及设置说明

遇到的问题及目前的解决办法

已完成的内容

实现了按脚本设置释放技能和选择指令卡、战斗指定次数、吃指定类型苹果(指未打包时,打包后问题多多,对的,所以稳定指的是3T能够稳定)

ps:本作为整活性质,起初仅为自己使用考虑,顺带锻炼做项目的能力,类似自发的“大学生的第一篇论文”。灵感来自maa,是明日方舟的一个挂机脚本,相当优秀,界面以及功能一定程度上模仿了maa。各方面都惨不忍睹,不过好在东西基本都是自己脑子想出来的。同时现在已经有许多成熟的脚本方案如FGO-py,以及BBchannel等,连接如下。

FGO-py的GitHub地址:https://github.com/hgjazhgj/FGO-py/blob/master/doc/contact.png

BBchannel的B站地址:https://www.bilibili.com/read/cv12976388?spm_id_from=333.880.0.0

ps2:分辨率需高于512*288,另外得是16:9的。

ps3:现已加入GitHub开源豪华套餐(大概算),网址为https://github.com/qcfei/fgoScript

需自主添加的内容

airtest(现以无需下载,但airtest是一个优秀的软件,有效降低自主编写脚本门槛,有点想法的可以下载):前往airtest官网下载airtestIDE,按照教程连接模拟器一次。如果我道行够高的话,自己的脚本说不定也能实现这个操作,有点不现实,可以当梦想(梦想完成辣)。

Airtest Project地址:http://airtest.netease.com/

使用的资源

重要的有

scrcpy,airtest,paddlepaddle,pyqt5,CV2,minicap,pyminitouch(minitouch)

山路十八弯的Android机子屏幕获取方法汇总

adb screencap:方便,最简单,有adb就能用。问题是巨慢无比,传一张图要个两秒左右,延迟太大影响脚本正常运行。

scrcpy+grabWIndow(在win32gui库里面):比上一个快许多。但是grabWindow要求窗口不在最小化状态,同时其依赖的win32gui库打包后不能正常引用。

scrcpy:利用自带的录屏功能,不使用grabWindow。但代价是速度慢。

minicap:快,其他什么的也都好的很。缺点是不好理解,写代码巨麻烦,scrcpy就是将minicap集成的例子,不过最后方案使用的这个,毕竟这个困难可以克服。

airtest:同样利用minicap,缺点是......缺点是我没法通过pip下载,而IDE不能和本来的python兼容。最后方案利用了这个软件在模拟器中安装minicap,不过这个可以被绕过去,现在的脚本已经内置了minicap的安装方法。

图像识别的算法(现从SIFT迭代至matchTemplate+OCR迭代至仅有matchTemplate)

SIFT

CV2中自带的SIFT算法,即尺度不变特征变换,通过像素点周围环境变化情况给出八个方向(即八个近邻点)的颜色变化向量,并且生成图片上的特征点。判断图像上这些特征点上的向量差值大小,就能判断图片是否相似(包含相同要素)。真正的原理里面还涉及空间尺度滤波器什么的,据我所理解似乎与高斯卷积生成模糊图像然后与原图相减获得梯度图相关?没怎么看明白。

反正可以通过这个函数获得相似特征点对(对比的两图像中两个相似的特征点即特征点对)的数目,限制这一数目的阈值就能判断图片两图片是否相似以至于一个图片是否包含于另一个图片,毕竟特征点是一样的。

在无旋转的两图(用于本脚本中尤其适合)中还可以将两特征点坐标相减得到图片的坐标原点,几乎所有正确的特征点对将会得到基本相同的坐标原点,据此可以剔除不正确的坐标原点,也就剔除了不正确的特征点对(当然也可能是两个相同的图像出现在同一幅母图之中),提高判断的精度。

若想将所有的图像都识别出来(如果由相同图像的话)可以识别出一个图像后将其剔除(染成黑色),再次进行识别,循环至无合适的特征点对为止。

要想实现这个功能还有许多其他的算法。

matchTemplate

比SIFT算法简单不少,SIFT直接调用返回的是图像的特征点信息,而matchTemplate返回的是一个相似度矩阵,只需要验证相似度矩阵的最大值大于阈值,即可知道图像是否存在以及在哪个位置。

OCR

使用了paddlepaddle飞桨提供的OCR的API接口,简单,方便,但不快捷。分辨率高的图像识别的还挺快,但是不知道为什么,分辨率降到512*288之后识别速率骤降。而且识别程序明明在QThread的线程里面跑的,OCR识别过程中窗口居然假死了。好在不会全程使用OCR,只有在助战选择的时候会调用一下,因为本来的选择框定在第一个助战位置,识别失败就往下滑一格。本来想的挺好的,但是下滑一个不够精准,同一个参数时,一次滑多一点,一次滑少一点。不过......现在用matchTemplate应该能达到同样的效果?还不如matchTemplate呢,已替换。

主要思路

屏幕更新线程

点击连接开始时,屏幕更新线程开始。

低配版将使用adb获取模拟器界面,保存到电脑文件中,读取文件,显示在屏幕中。

高配版将使用minicap获取屏幕转换为tcp数据流向端口转发,获取这些数据并重新编码生成图片文件。

点击连接开始后,该按钮将兼具暂停、恢复功能,点击连接结束式结束屏幕更新线程。

动作线程

将流程分状态识别,每个状态配备一个动作:

1、战斗前

2、嗑药

3、助战选择

4、战斗准备

5、战斗

6、结算

7、重复战斗

状态之间转移关系为

1->2,3 以体力是否足够区分 不够则进入2 够则进入3

2->3

3->4,5 以是否为重复战斗区分 重复则进入5 否则进入4

4->5

5->6

6->7

7->2,3 以体力是否足够区分 不够则进入2 够则进入3

其中5、战斗分为六个线性状态,操作在内部完成,封装成一个战斗总状态

1)第一回合技能选择

2)第一回合指令卡选择

3)第二回合技能选择

4)第二回合指令卡选择

5)第三回合技能选择

6)第三回合指令卡选择

每一个状态转移对象总是下一个状态,最后一个状态结束将结束战斗总状态

点击运行开始,动作线程开始

图像识别获取所处状态,当识别成功时执行该状态的动作,执行完毕后待机进入下一个状态的判定。

每当出现重复战斗界面,战斗次数+1,当战斗次数足够时,动作线程停止

界面及设置说明包含界面说明、单个环节运行按钮、连接模拟器按钮、全局脚本按钮、运行日志,主要用于展示运行界面,观察是否正常运行

上侧第二三行为测试按钮,现已删除,留有reset按钮,当显示状态于实际状态不符合时,点击此按钮,将重新识别状态并进入

test按钮为测试用,因为后续开发还会有用,暂且不删,勿用

点击连接开始,经过初始化后(这将花费一些时间),界面将不断更新模拟器屏幕,同时连接结束按钮将可用。点击后该按钮变为暂停按钮,再次点击后变为恢复,此后二者循环。同时运行开始将可用。

点击连接结束,左侧按钮重新变为连接开始按钮,自身重新变为不可用状态。

点击运行开始,将执行脚本。

ps:受不明因素干扰,即便照搬左侧对应的代码,脚本仍不能实现暂停、恢复以及结束后再重启。目前要想重新开始脚本的话,关了重开吧。

点击清空日志,将发生显而易见的事情,日志将被清空。

右侧为运行日志,现阶段有点繁琐,将精简。

设置界面,分为四个部分:1、策略设置策略设置的后续

策略有点抽象,但不复杂。一场战斗分为三个回合,记作Turn i,i可取1,2,3。每个Turn有两个输入框,第一个表征技能,第二个表征指令卡。

技能由一系列三位数字描述,由空格分割,每三位数字描述一个技能。第一位数字是释放技能的从者编号,可取1,2,3,4,5(前三个容易理解,4为御主礼装技能,5为换人技能,单独由530描述)。第二位数字是释放的技能编号,可取1,2,3,每个可释放技能单位均有且只有三个技能,无例外。第三个数字是释放的对象编号,可取0,1,2,3(0代表无释放对象如群冲技能,1,2,3容易理解)

指令卡由三位字符描述,前后代表选取顺序。

共有两个策略可以备选(方便保存一个副本),用下方的策略选择替换

2、助战设置

助战选择小门道:进入初始选择界面时,点击更新将助战替换为第一位的助战。要想更新为更多的助战,需要把助战界面向下拉,让其处于第一位置。

为了涵盖单个从者的数个再临形态,助战选择包括三个备用的选项。

为了适配开发时的白情活动(这个活动的礼装太多了,如果总是关心礼装,有时将老找不到对应的从者+礼装,后续活动的话可以把关心礼装勾上),设置了是否关心礼装选项,若不勾选,将无视礼装,仅关心从者来选人。

3、多次战斗设置

勾选是否多次战斗则根据战斗次数进行战斗,似乎多余,不过还是留着把。

体力不够时将自动吃苹果,可以指定苹果类型。

如果想花完所有体力,不吃苹果,那么应该怎么办呢?算一算floor(体力/40),动动脑更健康。

4、模拟器选择

模块介绍:

模拟器选择:目前加入两个模拟器(分别是mumu和夜神),不过其他模拟器用户请安心,模拟器选项可以自行添加,详见后续

模拟器连接测试:想要确认连接地址是否正确,在模拟器开启的时候点击此按钮即可,注意模拟器选择应在正确的位置。

minicap和minitouch安装:第一次使用时不能直接启动屏幕更新线程,将报错(我没写,所以大概率就是闪退),因为模拟器内没有对应的minicap和minitouch文件。确认模拟器连接成功后可以点击模拟器添加nimicap及minitouch安装按钮,完成安装。完成后下方标签将显示模拟器的架构和sdk版本以及'success'。由于这个功能测试较少,并且我觉得这些版本的弯弯绕多的很,极容易出现意料之外的错误。比如可能是因为模拟器上的版本正好没有对应的文件,而且某个版本之前似乎要额外的文件才能正常运行。出错的话可以查看一下模拟器内data/local/tmp是否有对应的三个文件(分别是minitouch、minicap、minicap.so)

自主添加模拟器选择:在文本框中输入模拟器的名称+' '+连接地址,再点击模拟器添加,即成功添加,重启后查看模拟器选择选项,确认是否添加成功。注意,这里的模拟器名称可以按照自己喜好来,而非官方名称,使用英文最好,如夜神模拟器用nox表示。

自主删除模拟器选择:为了避免过多的模拟器选择扰乱视线,添加了删除模拟器选择功能,在文本框中输入模拟器名称,点击模拟器删除按钮即可删除

遇到的问题及目前的解决办法

一:模拟器界面获取相关

最主要的时如何将模拟器中的画面截取到目录中,最开始直接抓取窗口句柄,失败。

后了解到scrcpy能用adb输出模拟器的视频流,于是抓取scrcpy窗口句柄获取窗口画面(这个b东西为什么没有自带的截屏,只有录屏)。存在问题有需要保持scrcpy窗口不能最小化,一个不小心就会让脚本出错;而且同时出现的窗口过多,容易混乱;最严中的问题时,打包成exe之后老提示我没有win32gui相关的dll(就是获取窗口句柄和界面的库)。

再后想到利用不断录屏获取录像并保存到本地,用opencv库解析视频获取画面信息,同样能够达到效果,并且绕过了win32gui这个库,打包后也不会报错。然而麻烦总是从一个地方转移到另一个地方。scrcpy没有录屏指定时间的指令,只能通过结束程序产生录屏文件。我给出的方法是......不断打开关闭scrcpy,我愿称之为开关大法。这个方法可以但比较......非常精神污染,毕竟有个窗口不断出现消失的。并且,有时候会截屏失败(scrcpy启动的初始化需要一点时间,而且不那么固定,若是还没启动就被噶了,自然也没有录屏文件),不过通过程序可以容忍这个问题。

再再后,找到了方法,使用vbs命令可以隐藏调用程序的窗口,于是,问题被解决了......并不,打包之后vbs隐藏窗口的指令还是会让cmd窗口出现一小会儿,这意味着,还是有东西会在脚本正常运行的时候不断的闪不断的闪。

再再再后,接触到了网易的airtest还有其他几厂的基于图像识别的开发工具。其中airtest有自己的IDE,直接从官网下载就能使用。有傻瓜脚本模式,和按键精灵几乎一模一样,但是功能比按键精灵更加深入。单纯写脚本的话开发起来很舒服,同时可以用python模式实现更多功能。问题依旧存在,python对airtest可以通过pip下载airtest库进行引用,但是pip下载出现了问题,并且它告诉我说不是pip自身的问题?浏览了网上的一般解决方案(几乎都是降python版本之类的操作),最终依旧没有解决。而下载airtestIDE时会自行安装一个新环境的python,这意味着 ......分别存储,互不干扰。也就是在这边写的python代码没法在另一边运行。

再再再再后,看到了一个天大的好消息。airtestIDE成功与模拟器建立连接后会在模拟器上自行安装合适版本的minicap(实际上scrcpy就是用minicap集成出来的这些功能),而这minicap解释输出模拟器视频流的关键,在minicap上建立tcp连接,转发端口,利用socket可以获取数据流,进行解码就能得到图片。虽然非常憨憨的半天没搞明白tcp传输数据到底咋接收,但是一天之后,磕磕绊绊的实现了图片传输。虽然传过来的画面不知道为什么会抖动(估计可以用解码的技巧解决),但是不影响程序判定。并且再度偷懒的使用了开关大法(不这样的话好像对数据流不能正常分割,解码出来的数据自然不正确,重启就没有这样的烦恼)。天大的坏消息:打包后还是会有cmd窗口不断闪现。由于只有打包后才会出现这个现象,我甚至不确定是哪句代码启动了这个窗口,只能怀疑是socket建立连接时自然产生的。如果能够克服数据传输问题,摒弃开关大法,那么,未来可期。

再再再再再后,弹窗问题实际由python内部os库引起。但凡cmd命令和os库沾点边,都会在执行的时候弹出一个弹窗,然后消失,也就是所谓的闪现。使用subprocess库里的run,并且设置run的参数shell为true,能够避免弹窗出现。但是此时屏幕刷新率低到要命,估计只有个半帧每秒。

再再再再再再后,可以将minicap的代码中将屏幕照片从模拟器中获取至本地文件中再获取至程序中。这时不再使用开关大法,因为我已经参透了socket(并没有),成功获取到了自己想要的数据,也就是屏幕画面。似乎同时读写同一个文件会出问题,这句疑似为废话,本来就不应该同时打开一个文件两次。所以不用两个线程读写同一个文件,用同一个线程进行这个操作。

自此,屏幕获取杀青,不会有更多问题了,应该吧。

二:指令输入

十分必要的输入指令包括触摸、滑动,非必要但可以整的包括启动程序,回到主界面等。

初始使用adb shell input指令,触摸为在input后附加'tap x y',滑动为 'swipe x0 y0 x1 y1 s',s为滑动时间。缺点是资源占用率相当高,在先前诸多阶段都还正常,但是一旦到了战斗状态,高频的触摸操作能让电脑本来30%的cpu占用率飙升至60%。当然我的资源规划一塌糊涂,但是adb input模式当背大锅。

后使用minitouch模式,minitouch与minicap是配套的,对比adb原初的方式,也就是screencap和input,最大的优点就是高效、节能,也不知道怎么做到这种魔法一般的效果的,哪天我说不定会去了解一下这里面的细节机制,还蛮有兴趣的。最初用于哪个很大的项目来着?反正minitouch和minicap好用但不太好用。好用是指完成之后运行效率高,资源占用少;而不好用是指很难完成(类似linux用户梗)。相比minicap更友好的一点是minitouch有高人做了一个很牛的封装pyminitouch,里面的接口只需要使用device、device.tap、device.swipe就能完成本来复杂的要命的指令。所以,现在的资源占用率一直在30%多点了,优化的相当成功。

三:如何识别指令卡,如只出红卡或只出绿卡,但留作更新

悲报:识别指令卡什么的其实不难,同样轻松用图像识别解决。真正的难点在于,如果需要指令卡补足伤害输出的话,意味着如果没有指令卡,将无法完成击杀,也就是需要4t,这在脚本中绝难处理,这样的翻车将意味着程序卡死在某个进程不在动弹......似乎也还能接受?相比起全手动而言,四五个小时后突然宕机也是已经赚麻。总之,开发中,或许可以利用机器学习判断怪血量选择最合适的策略也说不定。

ps1:本人水平略捞,代码是一个文件里面的一千五百多行字,我知道这样不好,但是脑子一抽就写下来了。现在正在努力把这坨代码分开,增加点可读性。敬请读者提出宝贵建议或者指导

ps2:已在GitHub上发布,源文件可在qcfei/fgoScript中找到



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有