

绝区零这版本有个钓鱼小游戏,由于全图鉴需要钓的次数很多,所以我想写个自动化脚本。经过了不少踩坑(,现在终于大功告成了,运行脚本后就可以自动钓鱼了,连续钓多久都没问题。
如何使用
脚本和所需软件可以从这里下载:
https://pixeldrain.com/l/ZgiPagcp
打开这个分享链接后,你可以点击左侧的“DL all files” 打包下载:
下载后解压,然后安装 AutoHotkey(官网),也就是 AutoHotkey_2.0.19_setup.exe
。
再然后安装 Tesseract,也就是 tesseract-ocr-w64-setup-5.4.0.20240606.exe
,它是一个开源的 OCR 识别引擎(Github 主页)。
之后把 tesseract.exe 的路径添加到系统的环境变量里:
tesseract.exe 的默认安装路径是 C:\Program Files\Tesseract-OCR\tesseract.exe"
,此时把 C:\Program Files\Tesseract-OCR\
添加到 PATH 里即可。然后运行 cmd,执行 tesseract
, 如果看到出现了对应的提示就表明成功了。
这样就可以使用脚本了。
先运行绝区零(全屏),和钓鱼点进行交互,进入初始状态(即准备抛杆):
接下来根据屏幕分辨率选择使用哪个脚本文件:
我的屏幕分辨率是 2560x1440,使用的脚本是 fishing-1440P.ahk
。1920x1080 分辨率需要使用 fishing-1080P.ahk
。(我把 1440p 上的坐标按比例转换成 1080p 上的坐标了,理论上没问题,但我没有测试过)。
在脚本上右键,选择“以管理员身份运行”:
脚本就会运行,它会自动切换到绝区零,并自动开始钓鱼。
挂机让脚本自动操作即可(不能切换到其他窗口)。
如果要停止运行,切换到其他窗口使任务栏显示出来,再右键托盘里的 AutoHotkey 图标,选择 Exit 即可:
一些说明:
- 需要以管理员身份运行脚本,否则无法向游戏内输入按键
- 全部的 4 个钓点都可以用。游戏内的所有时间段都可以用。
- 脚本会自动处理所有情况,从开始钓鱼到钓鱼成功,再自动开始下一次钓鱼。
- 有很小的概率可能会空杆(提杆时机错误),脚本没有针对性检测此问题,但有防超时处理,会在 90 秒后自动开始下一次钓鱼,所以不需要手动处理。
- 按 Ctrl + R 可以重置状态,使脚本开始新的循环(从抛杆开始)。如果你看到空杆了,不想等待自动处理,可以手动按 Ctrl + R 开始下一次钓鱼。
研发过程
由于我是第一次做这种自动化脚本(我连 AutoHotkey 脚本的基本语法都不懂),所以我让 ChatGPT 帮助我,但即便如此还是踩了很多坑,也走了不少弯路,花了几个小时才做得比较完善。
软件选择
一开始我问 ChatGPT 有什么推荐的软件,它给了两个选择:AutoHotkey 和 Python。虽然后者大名鼎鼎,但是在自动模拟用户操作的时候还是 AutoHotkey 更方便。
版本区别
在编写 AutoHotkey 脚本时,上来我就踩了个坑。
AutoHotkey 的脚本分为 V1 和 V2 两个版本,不同版本的语法和一些内置函数的差别很大。我使用 V2,但 ChatGPT 似乎默认是 V1,坑了我数次。由于我是新手,所以即使它给我的是 V1 代码,我也看不出问题,太无语了。后来我跟它强调使用 V2 语法才好了。
模拟按键输入
在 AutoHotkey 里输入按键是很简单的,比如:
Send ("F") ; 发送 F 键
但是在绝区零里无效。我问 ChatGPT,它说考虑到游戏与普通的文本输入框不同,所以可能需要换成别的输入方式,比如:
Send("{vk46}")
SendInput("F")
ControlSend("{F}", , "ahk_class UnityWndClass")
实际上全都无效。最后我搜了别人写的脚本,比如 绝区零零号业绩刷取脚本,看到里面是这样操作的,发送一个按键时分为按下和抬起两个动作:
Send ("{F Down}") ; 按下 F 键
Sleep 200 ; 持续
Send ("{F Up}") ; 释放 F 键
试了下果然有效。看来 ChatGPT 也还是有没考虑到的地方啊。
PS:要向游戏里输入按键,还需要以管理器身份运行脚本。我之前就知道这一点,所以这里没有踩坑。
另外我还在上面那个脚本里学到了一点,就是获取某个程序的窗口,并激活它(切换到它)。例如:
global ZZZ := "ahk_exe ZenlessZoneZero.exe"
WinActivate(ZZZ)
图像匹配的大坑
在钓鱼时,需要根据提示进行对应的操作,并且屏幕上会出现对应的按钮(图标),所以我一开始很自然地想“出现对应图标时执行操作”,于是踩进了图像匹配的大坑。
抛杆:
提杆:
长按或连续点击按键:
技能充满时可以使用:
AutoHotkey 自带了图像匹配(查找)功能,还是挺让我惊喜的。需要先给各种图标截图,然后使用 ImageSearch
函数搜索,如:
if ImageSearch(&X, &Y, 800, 300, 1900, 600, "*100 .\img\F0.png") {
; ...
}
关于它的详细用法,我直接贴 ChatGPT 说的吧:
在图片路径里可以设置相似度,如 *100
,值的范围是 0 - 255,越小就要求匹配越精确。
我在图像匹配上浪费了大量的时间。
第一个坑:ChatGPT 一开始给我的代码是 V1 语法,而且还不会报错,但效果不对,浪费了我很多时间。不管屏幕上有没有对应的图像,它都找不到,然后在一段时间后会执行找到图像的代码。后来我问了一位大佬才知道这个语法是 V1 的,改成 V2 的就好了。
第二个坑是根据钓鱼地点和时间的不同,同一个按钮下面的背景会变化。比如:
这就需要在不同场景下分别截一张图,并全部进行查找(匹配)。还有些图标上带有动画效果或颜色变化的,也可能需要截多张图片进行查找,才能比较准确的匹配到。所以最后我截了很多图,下面的只是一部分:
不过匹配某些图标时可以优化。一开始我从视觉习惯上出发,截取了完整的图标,但实际上可以只截取一部分,比如:
这部分是不会变的,就可以大大减少它的截图数量。
但接下来还有第三个坑,AutoHotkey 的图像查找似乎并不是很准确,有些相似度高的图片总是会混淆,比如下面这两种:
在查找它们的时候,当我设置的相似度低于 *130
时,总是查找不到。但一旦达到 *130
,就会混淆,区分不清:查找图片时哪个图片的代码写在前面(第一个),它就总是会查找到第一个。
后来我把“连点”和“A”分成两个截图,“长按”和“D”也分别截图,但 AutoHotkey 依然无法区分它们。
还有些其他按钮也有类似的问题,很难查找到。
使用 OCR
我向 ChatGPT 询问该如何解决上面的问题,它让我考虑 OCR。我试了下果然好多了。先安装 Tesseract,再下载 OCR.ahk
(Github 地址),就可以在 AutoHotkey 脚本里调用 Tesseract 来识别屏幕上的文字了。
简单的代码如下:
OCRResult := OCR.FromRect(1070, 230, 400, 60)
find := OCRResult.Text
MsgBox find
if (find != "" && InStr(find, "点 击")) {
Press("F") ; 按下 F 键抛杆
}
FromRect
方法的前两个参数是起始点坐标,后两个参数是宽度和高度,而非结束点坐标。
OCRResult.Text
返回序列化的识别结果,但是每个文字之间都有个空格,比如下面这个测试时的截图:
可以用 AHK 脚本的 StrReplace(OCRResult.Text, " ")
替换掉空格,也可以不处理,匹配时就按有空格的来。
但是 Tesseract 的识别结果经常不是很准确,比如:
而且多次执行时(识别同一个图像),产生的结果也可能不一样。所以在使用它的处理结果时,需要先测试几次,看哪些文字能够比较容易被识别出来,就判断这些文字。比如下图的“长”字容易被识别:
所以我把两个字分开判断,这样即使只识别出其中一个也可以匹配到:
if (InStr(find, "长") || InStr(find, "按"))
后来我把所有的图像识别代码都换成 OCR 识别了,更省事而且准确率还更高。
比如这个提杆的图标:
用图像识别时需要准备多个截图,依次匹配。后来我换成用 OCR 识别对应的提示文字:
这样不管游戏画面是白天还是黑夜,不管画面背景是什么地方,OCR 都可以识别出来,一份代码就够了。
提高效率
可以使用条件判断来减少不必要的操作。比如抛杆的按钮(和提示文字)只在钓鱼最开始时出现:
抛杆之后就不需要识别它了。所以我用一个变量保存抛杆状态,如果是 false 则查找抛杆的提示,在抛杆后改为 true,之后就不再查找它了。还有其他一些操作也使用了这个方法进行优化,这样每次循环中就不会有无效操作降低效率了。
简化操作
比如有个技能需要充能,未充满时的图标是灰色的,充满后变成蓝色的,此时才可以使用。
一开始我按照手动操作时的思路,判断“技能图标变蓝”这个条件,但后来我意识到,虽然没充满时用了没效果,但也没副作用,所以不管它有没有充满,直接用就完事了。这样就不需要判断这个图标的状态了,无脑使用即可。
再比如钓鱼中会出现四种操作:“连点A”、“长按A”、“连点D”、“长按D”。下面是“长按 A”:
一开始我试图判断出现的到底是 A 还是 D,还花费了很多时间(因为图像识别无法区分它们,OCR 也没有识别出按钮上的 A、D)。但之后我一想,虽然按键有 A、D 两种,但同时只会出现一种,所以不管出现的是什么,A 和 D 我全都按,这样就不需要区分到底是 A 还是 D 了,只需要判断是“连点”还是“长按”即可。
看来写脚本的思路和正常操作的思路还是有区别的。
其他的坑
虽然前面已经踩了不少坑,但还是会有些意料之外的事情。比如钓鱼成功后有两种结算界面。普通的鱼是灰色背景,稀有的鱼是青蓝色背景:
当我用 OCR 识别到底部的“点击空白处关闭”的文字时,就模拟鼠标点击一下屏幕,使这个界面消失。当结算界面是灰色时效果正常,但是当结算界面是青蓝色背景时,点击无效,所以这个界面也就不会消失。
一开始我以为是因为背景颜色和花纹导致 OCR 无法正确识别到文字,但是调试时发现是可以识别的。最后我终于找到了原因:在稀有鱼的结算界面里,鱼出现时有个简短的动画,此时点击是没有效果的。所以识别出文字之后不能立刻点击,需要等待几百毫秒,等动画结束后再点击。
使用AI写自动化脚本最大的问题可能就写脚本30秒,调试30分钟🤦♀️,是不是可以转换个思路,换个AI帮忙调试一下,主打一个榨干AI,哈哈