当前位置:首页 > 综合 > 正文

手游自动挂机脚本开发历程

事件起因

国庆节期间,因为工作相对闲暇,自己鬼使神差地下载了一个梦幻西游手游玩了几天,结果一周就这么过去了,一玩起游戏来,那就是白驹过隙啊。节日过后,游戏自然是没时间再玩了,毕竟有一大堆开发任务正一个个排着队等着去搞。时间一晃就到了两周前,大概是11月底的时候,一个朋友跟我说他玩碧蓝航线,每个月都要花20多块钱买脚本。当时听到脚本居然还要花钱去买时,作为一个程序员,不知道哪里来的正义感,决定自己给他写个免费的脚本,能让朋友省去一个月20块的脚本钱去叫外卖也算是用脚本挣钱了。当然如果能有别人也来用自己写的脚本,那当然更好,能帮到更多的人,由此小弟便踏上了一条游戏脚本不归路。。。

初识按键精灵

初次写脚本,小弟我也是一脸懵逼,以前完全没写过游戏脚本,当时牛皮吹的爽,现在不知咋搞心理有点慌。无奈只能调研下现在市面上写脚本的工具了,当时了解按键精灵,触动精灵等现在用得比较广泛的脚本工具。按键精灵应该是用的最多了,所以小弟就选择了按键精灵来写脚本。按键精灵分为PC版和手机版,小弟写的手游挂机脚本,当然是选择手机版了,然后按照教程,在模拟器上安装了按键精灵APP并且在PC上安装了按键精灵手机助手,脚本编写之路,就这么正式开始了,加油!!

按键精灵学习

初次上手按键精灵,还是有点蒙圈。不得不说,按键精灵是写程序小白们使用的,所以函数的使用都封装得很简单,而且变量可以定义中文,较复杂的代码可以自动生成,所以小弟我花了一天的时间基本上就把按键精灵的各个部分都熟悉了(这里没有给按键精灵打广告的意思,大家不要误会。。。)按键精灵虽然说是脚本编写,但是用的最多就恐怕就是用来写游戏脚本了吧。虽然按键精灵确实写脚本比较方便,但是还是有一些不足之处的。让小弟最想吐槽的就是按键精灵的OCR功能,简直是形同虚设,根本不能用,文字识别总是失败(大概率失败,一旦失败,就又得重新找点,重新让这个精灵识别,然后又是大概率失败,过程之痛苦,我觉得程序员不应该忍受这种痛苦),即使识别成功了,实际用的时候也不一定能识别出来,令人不得不吐槽一下。所以下面小弟我就稍微总结一下按键精灵让我感受到的优势和缺点吧。

按键精灵的优缺点 优点缺点

语法简单 并且配有代码示例

部分颜色函数使用效果不好

配有实时抓取手机屏幕工具 方便逻辑编写

OCR模块效果很差 使用麻烦

调试方便 有基本的调试工具

上面说到的OCR模块效果很差,可以说是一个比较重要的问题,因为在游戏脚本编写中,经常需要用到文字识别,只有识别到文字了,才能据此进行下面的操作。比如在《梦幻西游》手游中,找到了”师门任务“四个字,才能决定是否要开始师门任务,或者是返回任务已经被完成。个人认为,一个好用的OCR,才是游戏脚本的灵魂所在,其他的逻辑只是辅助而已。当然,我们还可以退而求其次,用判断像素点颜色的方法来判断是否有任务存在,小弟我在按键精灵OCR尝试未果的情况下就是用了这种方式,对于PC版的挂机脚本,可以使用”大漠插件“来作文字识别,就小弟了解,这个插件效果还是不错的,解决了文字识别的问题,让很多PC上的挂机脚本稳定了很多。但是安卓系统下无法使用,所以这跟小弟无关,也就没深入了解。另外顺便再提一句,现在都9102年了,上开源的中文OCR引擎咱整一个不香吗,话题扯偏了,小弟我目前也没用到AI的程度,后面会加入进来。。。

上面的截图是按键精灵版的师门任务部分截图,从图中可以看出,按键精灵支持变量中文,并且没有复杂语法,上手很容易,历经了两天的开发,小弟最终把师门挂机脚本,主线任务挂机脚本,抓鬼脚本写完了,师门脚本和抓鬼脚本稳定都还可以,除了按键精灵运行一段时间自动崩溃以外,这个问题让小弟束手无策,难道要再写一个脚本在脚本崩溃以后再把脚本启动起来?

和按键精灵分手

本来小弟满怀期待地打算把写好的脚本打包发送给同学,让他试试,但是一道晴天霹雳让小弟我几近崩溃,打包居然要收费!!!,就19年下半年刚开始收费的,按月收费,最低30元(好像是),最高一百多。缴费越高,写的脚本就能给越多的人使用,最低的30元脚本,只能给几十个人使用。可是小弟我就自己用啊喂,我又不拿去卖钱,这也要收费。关于收费,小弟这里就不提了,毕竟这是人家自己的东西,咱也不好多说什么,没办法,只能和按键精灵说拜拜了,不是因为这区区30块钱,而是小弟觉得这里不应该花这个钱,一来自己是程序员,本来就应该以更”程序员“的方式做这件事,二来是小弟是给别人无偿帮忙写脚本,这个钱不应该掏。

另一条路

放弃了按键精灵,就意味着放弃了那一套方便快捷的工具,放弃那两天幸苦产出的代码。小弟没办法,所有东西都要自己想办法来解决了,通过使用按键精灵,小弟我发现,写游戏脚本最重要的是解决两个问题:

像素颜色查找问题,即捕获屏幕上某个坐标点的颜色(核心问题,用于判断当前屏幕是什么内容)交互功能(点击,滑动 等基本模拟操作)

只要解决了上面这两个问题,做一个基本的手游挂机脚本就足够了,所以小弟我针对这两个问题,开始网上新一轮的调研,在此期间也走过一些弯路,但是功夫不负有心人,最终还是摸索出了一条可行的路,下面就讲述下小弟我的学习路程。

艰难的摸索

因为是手游脚本,游戏本身是运行在安卓模拟器上的,那么挂机脚本在小弟认为可以分为两种,一种是直接运行在模拟器下的,另一种是运行在PC上,通过发送远程命令操作模拟器。小弟当时调研时理解还不够深,所以选择了第二种方式,通过adb( debug )将PC与模拟器连接起来的方式操作模拟器。实际上用第二种方式,将脚本打包在模拟器环境下直接运行效果才是最好的,当然,难度也会大不少。

的学习

为了模拟用户操作,也就是解决第二个问题,小弟我首先了解到的是使用,是一个用于系统的自动化测试工具,里面提供了很多模拟用户操作的功能,包括点击,滑动,截屏等等。当然他最强的之处并不在此,能够解析出当前App的前端控件,相比起之前按键精灵的点击某个坐标的模拟操作方式,能够根据控件的id等属性定位到某个具体的控件,定位之后做后续操作。这种方式的好处就在于,通过控件属性定位坐标,跟屏幕分辨率无关。如果单单写死一个坐标位置去点击,手机换个分辨率就失效了,也是为什么手游脚本都固定一个分辨率的原因所在。

上图是连接成功后的配置截图,其实核心参数就是,指定本地的模拟器地址就可以。在搭建环境时,小弟我也是花了两天的时间到处查阅资料和学习相关的用法,一开始使用的雷电模拟器,但是始终连接不上,最后换了个模拟器,用夜神模拟器后就成功了,所以如果连接不上模拟器,不妨换个模拟器试试,当然首先还是看看日志,有没有打印什么错误。

初代脚本形成

小弟我使用编写此脚本,核心流程如下:

脚本启动 -> 启动 -> 调用截屏 -> 判断当前页面状态 -> 调用模拟操作 -> 任务结束 退出脚本

连接的代码如下:

#python
desired_caps = {
    'platformName': 'Android',
    'udid':'127.0.0.1:62001',
    'deviceName': 'deviceName',
    'platformVersion':'5.1.1',
    #'appPackage': 'com.tencent.mm',
    #'appActivity':'com.tencent.mm.plugin.account.ui.WelcomeActivity',
    'unicodekeyboard':True
}
# 连接android 客户端
driver = app_web.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

突然醒悟 二代脚本形成

上面也说过了,能解析页面,类似于解析html的 一样,但是游戏页面哪有”前端“这种说法呢,哪有树状的控件能让我解析呢,我用捕获到的页面永远都是一个大的Frame,然后里面什么也没有,更别提解析了。。。

但是底层也是调用的adb,那为何我不直接使用adb来完成所有这一切操作呢,既然的优势不复存在,使用他反而成为了累赘。所以小弟我把去掉,重新更新了一版脚本,用调用adb命令完成截图,模拟操作等命令

脚本核心思路如下:

脚本启动 -> 截图到本地 -> 比对坐标点颜色 -> 判断出于什么页面 -> 执行具体操作 -> 删除本地图片 -> 重新截图新一轮判断

下面把核心代码贴出来,完整代码小弟我在文章后面会给出git仓库地址,感兴趣的同学可以clone一下看看

首先是用到的adb基本命令如下:

SCREENSHOT = "adb exec-out screencap -p > " # 末尾需加上截屏地址
TAP = "adb shell input tap " # 点击屏幕 x y
SWIPE = "adb shell input swipe " # 滑动屏幕

以上分别是截屏保存到PC本地,点击模拟器坐标x y 以及滑动模拟器屏幕,当然后面还有一些参数,参数根据需要加在命令后就行了,这里就省略掉参数吧

PC命令行中执行 adb 截屏命令

    def get_screen_shot(self, path):
        cmd = Constants.SCREENSHOT + path
        os.popen(cmd)

核心函数 图片像素颜色采集和比对

# 对比图片像素颜色值
    def comparePixelColor(self, srcImg, color, position, simThreshold):
        # 计算欧式空间距离
        # 首先获取 x 和 y 坐标的颜色值
        targetColor = self.getPixelColor(srcImg, position)
        # 再分别获取 R G B 值
        red = targetColor[0]
        green = targetColor[1]
        blue = targetColor[2]
        diff_red = red - color[0]
        diff_green = green - color[1]
        diff_blue = blue - color[2]
        similarity = (1 - math.sqrt(diff_red*diff_red + diff_blue*diff_blue + diff_green*diff_green) / math.sqrt(255*255 + 255*255 + 255*255))
        if similarity > simThreshold:
            return True
        else:
            return False
    
    # 获取图片坐标点颜色值
    def getPixelColor(self, srcImg, positon):
        srcImg = srcImg.convert('RGBA')
        str_strlist = srcImg.load()
        RGBA = str_strlist[positon[0], positon[1]]
        return RGBA

以上两个函数分别是获取图片中某个坐标点的颜色以及比对图片中某个坐标点颜色的函数,对于比对颜色函数,计算欧式空间距离,然后按照比例算出相似度,在脚本中,小弟认为相似度大于 0.9以上才认为是相似的

还有一些就是逻辑部分的代码了,这部分代码并非核心代码,小弟这里就不贴出来了,有兴趣的同学可以上看

一段路程的结束 若有所思

至此,游戏挂机脚本就到一段落了,游戏脚本小弟自己使用没有什么问题,但是离给别人使用还有很长一段距离,主要是adb截屏到本地耗时实在是太久了,截屏一次2s,这个是硬伤。

如果要优化这个问题,就必须让脚本执行在系统中,通过读取系统下的设备文件实时获取屏幕数据(需要root),我想这也是为什么按键精灵能做到颜色比对如此之快的原因,因为前面也提到了,按键精灵手机版由两个部分组成,一个是”按键精灵手机助手“,这是安装在PC上的;另一个是”按键精灵手机版“,这是安装在手机上的APP

在调试开始前,按键精灵会要求两者连接。我想,按键精灵是在PC上完成脚本的编写(手机助手编写脚本),然后连通手机上的APP,把写好的脚本传到APP中去运行,由于是运行在手机测,和我的脚本就不同,它可以直接读取设备文件。这也解释了为什么按键精灵要root权限,因为没有root权限就无法读取文件获取屏幕像素信息。另外,按键精灵的”抓抓“里有一个截屏功能,如下图:

通过这个按钮的截屏时间也差不多2s左右,我想按键精灵这个2s应该跟我上面的使用adb传文件到本地耗时2s原理是一样的,因为都是从模拟器上截图到本地,应该都是走的adb,所以都是2s耗时

接下来的优化

上文提到了最好的方式是读取设备文件获取屏幕像素信息,而非截图,我自然是打算把优化方向放在脚本的执行环境上了,接下来可能会改用java编写,让脚本运行在系统下,关于读取屏幕数据(),这里有一篇写得比较好的文章大家可以学习一下,牵涉到底层代码,读取获取屏幕数据,路虽难,但只要肯琢磨,一定能理解

以上就是两周以来对脚本的研究近况,因为笔者水平有限,文中难免有表述不准确的地方,还请大家指出,共同学习进步~

项目地址:

你可能想看:

有话要说...

取消
扫码支持 支付码