Saber 酱的抱枕

Fly me to the moon

03/12
2025
游戏

使用 AutoHotkey + OCR 制作绝区零的自动钓鱼脚本

绝区零钓鱼脚本 no_lazy

绝区零这版本有个钓鱼小游戏,由于全图鉴需要钓的次数很多,所以我想写个自动化脚本。经过了不少踩坑(,现在终于大功告成了,运行脚本后就可以自动钓鱼了,连续钓多久都没问题。

如何使用

脚本和所需软件可以从这里下载:
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.ahkGithub 地址),就可以在 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 无法正确识别到文字,但是调试时发现是可以识别的。最后我终于找到了原因:在稀有鱼的结算界面里,鱼出现时有个简短的动画,此时点击是没有效果的。所以识别出文字之后不能立刻点击,需要等待几百毫秒,等动画结束后再点击。

使用 AutoHotkey + OCR 制作绝区零的自动钓鱼脚本

  1. saber 文章作者
    Google Chrome 134Google Chrome 134WindowsWindows

    AutoHotkey 的匹配图像应该只是简单的检查像素点的相似度,所以如果截图里的背景变了,就容易识别不到。
    有没有基于神经网络或者说 AI 的图像匹配引擎呢?能让其他程序调用的那种。

    回复
  2. saber 文章作者
    Google Chrome 134Google Chrome 134WindowsWindows

    开钓鱼图鉴相关的有 4 个成就,现在我还没集齐全图鉴,所以有个还没开:

    /f/20250312_013339.webp
    /f/20250312_013355.webp

    PS:钓鱼时掉到废弃电池之后,可以拿给阿寻教练,有 10 菲林和一些蚊子腿奖励:
    /f/20250312_014501.webp

    回复