按键精灵学习笔记
概念
按键精灵可以使用各种标准VBS命令,比如数值操作、字符串操作等
Q语言
按键精灵使用的是Q语言。
Q语言是在VBScript的基础上发展起来,专用于按键精灵系列产品(包括但不限于按键精灵、按键小精灵、简单游等)的一种易学易用、功能强大的脚本语言。Q语言以VBScript为基础,去掉了少量不重要的功能,且增加了很多更易于使用,更符合国情的新功能。
这是一篇介绍Q语言的文章。
注意:
Q语言不支持参数传引用,仅支持参数传值,而VBScript两者都支持。
API文档
http://zy.anjian.com/index.php?action-viewnews-itemid-161
http://zy.anjian.com/?action-model-name-qmdn-itemid-274
命名规范
调试输出
https://cloud.tencent.com/developer/article/1492273
鼠标位置
1 | |
注意GetCursorPos获取的鼠标位置信息,是相对于整个屏幕左上角的坐标值,不是相对具体某个窗口的坐标值。
获取鼠标相对当前窗口的位置
1 | |
数据结构
变量与作用域
http://bbs.anjian.com/showtopic-545127-1.aspx
Dim:定义的变量仅在主线程有效,不支持多线程、QUI事件内、其他脚本内值的传递。
DimEnv:定义的变量可在主线程有效,且支持多线程、QUI事件内、其他脚本内值的传递。
Global:定义的变量可在主线程有效,半支持多线程(支持单独的线程,不支持线程中线程),不支持QUI事件内、其他脚本内值的传递。
DimEnv有2个需要注意的地方:
1、在子程序里定义变量不可以为环境变量
2、不支持数组定义为环境变量
所以要想传递数组,只能在主程序中定义为字符串,然后在子程序中将其拆分重新定义为数组。
数组
一维数组
https://blog.csdn.net/weixin_47678894/article/details/111465041
可以通过filter进行过滤:
1 | |
二维数组
https://blog.csdn.net/weixin_49567506/article/details/108999814
键值对象
按键精灵是没有键值对的,需要我们自己实现这个数据结构。
流程控制
http://zy.anjian.com/?action-model-name-qmdn-itemid-274
for循环
1 | |
while循环
1 | |
函数(Function)定义和调用
如何返回值
如何返回多个值
方案一:返回一个数组
1 | |
方案二:传入参数,改变参数的值
退出函数
1 | |
调用函数
1 | |
如果函数有多个参数,且又想使用函数的返回值的话,则会出现问题:
1 | |
一般优先使用Plugin.Window.Find,如果窗口标题不固定,再考虑使用其他两个函数
注意,如果不打开窗口,是无法获得句柄的。标题名也不能写错,写错也无法获取。但是会有返回值。
常用窗口操作
1 | |
延时
注意后台的延时,不用指定窗口句柄,直接Delay即可,类似这样:
1 | |
窗口
1 | |
区域找字
1 | |
可以参考这个前台的文档:http://www.vrbrothers.com/cn/qmacro/qmdn/htmls/plugin/pic.findwordshape.htm
注意这个找字并不精准,似乎是只要前缀符合就会被找到,比如“自动行驶”和“自动退出”,谁在左上角谁就会被先识别到。
区域统计颜色数量
用来判断身边怪物多不多,不多时切换随机,增加打怪效率。
按键自带的CountColor是不支持后台模式的,因此想要后台操作,得借助其他插件,比如大漠插件的GetColorNum函数。
如果必须只能用按键,那么还有个方案,就是按像素值逐个检查屏幕上的点的颜色:
1 | |
不过可以想见的是,这个会耗费很多CPU资源,因为会执行非常多的Plugin.Bkgnd.GetPixelColor操作。
(TODO)坐标系统
图片处理
截图
将图片写入剪贴板
http://loginapi.anjian.com/showtopic-221630-1.aspx
https://zimaoxy.com/q/post/sys.setclb/
windows默认的clip命令只能将文本写入剪贴板;如果想处理图片,需要用xclip:
https://stackoverflow.com/questions/21036409/put-image-in-clipboard-from-command-line
但是这个我试了不好使,然后换了这个NirCmd:
1 | |
亲测好使。
配置文件
http://zy.anjian.com/?action-model-name-qmdn-itemid-219
1 | |
注意:如果通过OpenFile打开了一个文件,那么一定要记得通过CloseFile关闭文件,否则会导致后续的诸如ReadINI等,无法成功读取文件信息。
界面
参考文档:http://zy.anjian.com/index.php?action-viewnews-itemid-160
注意事项:
跟C#制作界面差不多,也是双击界面的按钮进去编程。
注意:界面的背景窗体默认是Form1,写脚本的时候,
正确 form1.InputBox.Text
错误 InputBox.Text 认不出是哪个窗体的界面编写脚本的时候,要在右边脚本属性最下面,“界面设置”里面,选择“使用按键精灵制作界面”,脚本和界面才会结合到一起。
调试的时候,如果按F10,只是运行脚本,界面不会运行;要点击左下角“启动F10”上面的“ 自定义界面”,才会运行自定义界面。
注意:脚本中定义的UserVar,和通过界面定义的表单数据,似乎是不能并存的。比如我脚本中定义了一个UserVar,界面也定义了一些checkbox,最终打包的exe程序中,只出现了checkbox,脚本定义的UserVar丢失了。
按键精灵的这个控件设计,有一些不足,比如没有分组的概念,每个控件的名称都必须是唯一的,事件也需要单独绑定。假如我想设置在多个条件中单选,那么有几个条件就得绑定几次事件,比较麻烦。不像HTML有radio和checkbox、select等。
遇到这种情况,可以这样处理,用多选框,加上点击事件:点击事件子程序对点击进行判断,如选中,另一个设置为未选。
字体规范
左侧距离统一设置为50
一级标题:粗体,14
二级标题:粗体,12
正文:常规,10
转义
英文双引号:多加一个双引号即可,比如这样:
1 | |
导出脚本
如果脚本包含有界面或者附件,一定要用导出的方式进行传递,否则会因为丢失这些文件而无法使用。
按键小精灵
可以将你的脚本单独打包为一个exe可执行程序,方便别人使用,这种exe就被称为按键小精灵程序。
按键小精灵程序可以自定义内容,方便用户自己设置参数。
Q&A
按键小精灵需要本地预先安装按键精灵么?
不需要,打包好的程序已经包含了执行所需的内容,可以看到打包程序有8M多。
如何自定义内容?
可以通过UserVar(用于支持用户设置脚本参数)来实现,文档参考:http://zy.anjian.com/?action-model-name-qmdn-itemid-290
1 | |
但是如果程序中用到了多线程,UserVar默认是无法传递到多线程中的;这个时候要借助下DimEnv,类似这样:
1 | |
因为DimEnv是支持多线程的,所以这样就可以将用户定义的变量传递下去了。
(待验证)如何设置复选框?
可以通过按键精灵的界面功能来实现。
复选框只能绑定点击事件。
1 | |
http://zy.anjian.com/index.php?action-viewnews-itemid-167
复选框的内容,是可以保存的(会在同级目录下,生成一个uservar.ini),供下次打开直接使用。
如何输出过程日志?
可以考虑将过程信息放在变量中,当程序中断的时候,一次性通过MessageBox打印出来。
Sub子程序 VS Function
- Function返回值,Sub不返回值
- Function可以递归调用
比如你要求a,b两个数的和。主程序中要用到的是这个和,而不是直接在子程序中输出。这时你就要定义一个函数,将和返回,以便主程序使用。
Function 和(a,b)
和=a+b
End Function
这时如果主程序要输出(1+2)*3的结果9,就可以这样
traceprint 和(1,2)*3
而用子程序,因为它不能返回值,所以则很难实现上面的功能。
注意:Sub中读不到UserVar设置的值,需要通过中间介质进行数据传输。这个应该是我理解有误,不是Sub读不到,而是多线程读不到。
什么时候调用函数需要加括号?
似乎是需要返回值的时候;无需返回值的时候不能加括号?
日志
默认有提供LogStart和LogStop,将TracePrint的信息记录下来,参考这个文档:
http://zy.anjian.com/?action-model-name-qmdn-itemid-600
日志的注意事项:
1、日志功能定位是高级命令,因为对日志内容的分析需要一定的基础。
2、日志功能只针对每个线程有效,例如在一个独立线程中开启了日志功能(LogStart),就只会记录下这个线程中的日志信息,其他线程如果需要记录日志,需要重新调用LogStart命令。
3、如果多个线程或脚本中使用了同一个文件做为日志文件,一旦其中一个线程或者脚本关闭(LogStop)了日志功能,其他线程或者脚本中的日志信息也会关闭
4、在线程或者脚本结束时,系统会自动关闭日志功能。
(TODO)制作插件
参考这个文章:
https://wenku.baidu.com/view/b10572cc19e8b8f67c1cb9ae.html
等我空了再琢磨,看起来还是要耗费一些时间的,现在没时间。
我们自己编写、又经常用到函数,可以制作成DLL插件,在按键精灵里进行重复使用,而不需要反复复制粘贴。
用VB打开C:\Program Files\按键精灵9\source\QMPlugin插件制作模版(VB 6.0),文件–生成XXX.dll
再把这个DLL文件放入C:\Program Files\按键精灵9\plugin\文件夹下打开按键精灵,在“全部命令”–“插件命令”的最后,就有这个刚生成的插件了,里面编写的函数就可以使用了。
选中某个函数,在最下面,会有Plugin.QMVBPlugin.Test2 参数1,参数2,插入后,还不能正常使用,正确的使用应该是这样的:Plugin.QMVBPlugin.Test2(2,3),一定要加括号。
VC6版本的教程:http://zy.anjian.com/index.php?action-viewnews-itemid-155
VB6安装:http://www.downza.cn/soft/12196.html
大漠插件
这是一款很实用的插件,基于VC6写的。
按键精灵使用大漠插件的方式,可以参考这个:https://zhidao.baidu.com/question/2075641961608594068.html
这是按键精灵的大漠插件论坛:http://bbs.vrbrothers.com/showforum-140-1.aspx
识别文字
这里还有一个通过百度AI的API接口实现的教程:
https://blog.csdn.net/aitanxiaofeng/article/details/112351285
可以参考这个教程:
https://blog.csdn.net/weixin_42912498/article/details/106833515
大漠的文字识别,需要制作字库,假设我要识别所有数字和分割的斜杠,那么我就得制作0-9加上斜杠,共计11个子的字库。
批量制作字库的教程:
https://www.zhaokeli.com/article/8202.html
注意设置偏色,一般写222222或者333333就可以了
多线程中使用
多线程内均需重新创建大漠对象、加载字库等。
常用示例代码
1 | |
提示信息
当出现异常时,我们希望能发出一些提示,让用户可以及时处理。
声音
窗口置顶+抖动
注意事项
RGB色值问题
FindColor的色值和我们平时的RGB不一样,顺序是GBR的
比如纯红色,不是FF0000,而是应该写成0000FF
调试颜色的时候,可以用windows的画图工具,画几个色块来辅助调试,效率会高很多。
制作bmp图片时,越小越好
越小越好,只要能标识你想要的效果就行;
越大越容易加入冗余像素,导致识别不出来。
后台运行时,无法执行鼠标点击操作
如果不点击/异常说明存在屏蔽机制 尝试第三方插件修改键盘/鼠标/图色绑定模式进行解决[例:大漠/361/大兵],如果点击查看最后一句确认是句柄或坐标错误[如果原先句柄与坐标正确 本次点击 则说明游戏存在需要前台鼠标也在指定位置的屏蔽机制 尝试先Call Plugin.Bkgnd.MoveTo(Hwnd,,)后点击等方式
大漠插件也遇到这个问题:
请问下,你的这个问题解决了吗,我使用大漠插件也遇到了同样的问题,一直无法解决。
我绑定窗口句柄后,使用后台鼠标,打印鼠标位置,是两个负数,然后移动鼠标到一个位置,再次打印鼠标位置,是鼠标在该窗口内的位置(我确认过,打印的不是前台鼠标位置),因此我认为后台鼠标是可以移动的,但是接下来在该位置点击一个按钮,却是始终无效。
另外,如果使用dm.GetMousePointWindow()来获取句柄,并且运行脚本前先用鼠标点击一下窗口,那么后台鼠标的点击是可以正常使用的。如果运行前,只是将鼠标放在窗口而不点击,那么同样后台鼠标只能移动却不能点击。
测试所知道的就这些,但是既然使用后台,当然不想每次运行前都拿鼠标点一下窗口。所以想使用dm.FindWindow找到句柄来操作,不这样后天鼠标又不能点击,很蛋疼。
目前尚未找到解决方案,想到一个折中的办法,就是每次执行操作前,将窗口pop到最顶层:
1 | |
另外经过我测试,按键自带的后台鼠标操作,和大漠的,是差不多的,因此没必要用大漠插件,直接用自带的即可。
2022.02.25:遇到一个问题,鼠标不能移动,但是可以点击。
最小化后无法取色/识图的问题
搜索关键词:后台取色。
参考这个帖子:https://bbs.csdn.net/topics/391050909
最小化之后窗口为了节省性能开支就没有继续刷新了,所以取不到。
经过我测试,确实最小化之后,鼠标移动到窗口上,显示的缩略窗口是不刷新的。
这个文章提到的hook方法很有意思:检测到最小化,就把窗口还原,并移动出屏幕之外;检测到点击窗口,就把窗口移动回来。
思考:大漠插件行不行:
绝大部分游戏窗口最小化后是不刷新的.所以理论上不刷新的话就无法获取到当前的图色
但是大漠插件的公共属性中有个dx.public.fake.window.min属性
它的完整解释是这样的”dx.public.fake.window.min”此模式将允许目标窗口在最小化状态时,仍然能够像非最小化一样操作..另注意,此模式会导致任务栏顺序重排,所以如果是多开模式下,会看起来比较混乱,建议单开使用,多开不建议使用.同时此模式不是万能的,有些情况下最小化以后图色会不刷新或者黑屏.
你可以去尝试一下
这里找到一个文章,提到是绑定模式的问题:
http://blog.sina.com.cn/s/blog_183353fac0102yfce.html
换行符
不能用\r\n,而是要用& chr(13)
变量作用域
函数内的局部变量,一定要通过Dim定义,否则如果A函数里面调用了B函数,那么AB函数中的同名变量,会在执行B函数的逻辑的时候,被重新赋值,导致后续A函数中的值发生了变化,造成变量污染。
(TODO)Call的作用是什么?
一个案例
有一个识字定位的功能,里面多次调用了识字定位函数,类似这样:
1 | |
因为我没有通过Dim定义变量iCoord、XY、intX、intY,导致重复执行这个脚本的时候,这些变量被当成全局变量,其值已经被污染了,就会导致无法识别到文字。
RunApp的路径问题
有的exe程序,执行的时候依赖于相对路径,如果直接以绝对路径执行,是会出错的:
1 | |
这种情况就必须制定程序执行的工作目录,类似这样:
1 | |
坑
- 识字并不精准,比如”血”字识别就有问题;也可能和字体等有关系
遇到过的问题
自动随机乱飞
可能有如下几个原因:
- 窗口被最小化了,不再刷新,导致无法识别到红点
- 小地图窗口被其他界面窗口遮住了
- 移动了小地图窗口的位置
缺少标识符、类没有被定义
下面这2个函数,很类似,但是第一个可以正常运行,第二个一旦调用就会报上面的错误:
1 | |
原因在于:参数不能是关键词!
我第一个函数的参数用了type,这应该是个关键词;改为其他的单词就没问题了
语法错误:(错误码0)没有找到合法的符号
常见于我们用括号的方式调用了一个函数,但是没有给其返回值赋值为变量:
1 | |
正确的应该是这样:
1 | |
如果不需要返回值,则不应该用括号将参数括起来,应该这样:
1 | |
不能双开脚本
我给悠然做了一个脚本,然后发现复制脚本设置不同的快捷键启动,总是只能执行一个脚本,后面一个脚本启动后,前面一个脚本就不执行了。
经过排查,发现是窗口句柄的问题,不能在程序一开始就获取窗口句柄,而必须在子进程中再获取窗口句柄。
运行一段时间程序崩溃
常见解决方案:http://bbs.anjian.com/showtopic-406232-1.aspx
可以考虑:
- 尽量少用DimEnv
- 将程序打包为小精灵的方式使用
- 定时中断并重启程序
- 打印日志,根据日志排查问题
(TODO)脚本无法通过设置的快捷键停止
如何自己实现和按键精灵类似的功能?
支持后台操作
应该都是通过和窗口进行send message通信来实现的
C++:https://blog.csdn.net/fanxueya1322/article/details/97612547
https://www.cnblogs.com/techdreaming/p/7230241.html
Windows是一个消息驱动式系统,SendMessage 是应用程序和应用程序之间进行消息传递的主要手段之一。
可以用Java的JNI来调用C、C++实现:https://blog.csdn.net/qq_44643051/article/details/108327266
不支持后台操作
JavaScript的RobotJS
Java的robot类
识别像素
C++看起来得先搞个图片:
https://www.cnblogs.com/Toya/p/11431992.html
图像转换操作:https://blog.csdn.net/akadiao/article/details/78836384
资料
(精)API文档:
https://zimaoxy.com/m/post/findstr/
(精)论坛,可以在这里搜索问题、提问,寻找志同道合的人;也可以直接搜索游戏名寻找现成脚本;另外充值会员(15元/月),可以提问,有人回答:
视频课程:
https://ke.qq.com/course/68273
常用操作的代码示例:
https://www.cnblogs.com/erbing/p/7755747.html
大漠插件(文字识别、图像识别)制作字库:
https://www.cnblogs.com/zoro-robin/p/5591455.html
http://www.360doc.com/showweb/0/0/962760710.aspx
区域找色:
http://zy.anjian.com/?action-model-name-qmdn-itemid-613
找色原理:
https://www.cnblogs.com/qq2806933146xiaobai/p/11177690.html
多线程:
http://zy.anjian.com/?action-model-name-qmdn-itemid-543
多线程内外不共通,因此必须在多线程内部重新绑定;若不绑定,则为前台
1 | |
如何将外部参数传入多线程内部呢:通过环境变量DimEnv来实现:
1 | |
这里还有个361插件可以支持传参:http://bbs.vrbrothers.com/showtopic-593145-1.aspx
JavaScript开发按键精灵:
https://stackoverflow.com/questions/46916722/how-to-send-inupt-keys-to-a-window-with-node-jss-ffi