按键精灵学习笔记

概念

按键精灵可以使用各种标准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
2
3
GetCursorPos mx,my
MessageBox "当前鼠标位置"& mx &","&my
TracePrint "当前鼠标位置"& mx &","&my

注意GetCursorPos获取的鼠标位置信息,是相对于整个屏幕左上角的坐标值,不是相对具体某个窗口的坐标值。

获取鼠标相对当前窗口的位置

1
2
3
4
5
6
7
8
9
10
Hwnd= Plugin.Window.MousePoint()
sRect = Plugin.Window.GetWindowRect(Hwnd)
Dim MyArray
MyArray=Split(sRect,"|")
// CLng:把字符串类型转换为长整形
x=CLng(MyArray(0))
y = CLng(MyArray(1))
这样得到的x,y就是当前窗口左上角的前台坐标,如果前台鼠标位置ux,uy需要换算成屏幕上的后台鼠标bx.by的话
bx = ux -x
by = uy -y

数据结构

变量与作用域

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
2
3
4
5
6
7
8
9
Dim Arr(3)
Arr(0) = "abcder"
Arr(1) = "ghijkl"
Arr(2) = "mnopqr"
Arr(3) = "stopqr"
getArr = filter(Arr, "opqr")
For i = 0 To UBound(getArr)
TracePrint "getArr("&i&")="&getArr(i)//输出为getArr(0)=mnopqr,getArr(1)=stopqr
Next

二维数组

https://blog.csdn.net/weixin_49567506/article/details/108999814

键值对象

按键精灵是没有键值对的,需要我们自己实现这个数据结构。

流程控制

http://zy.anjian.com/?action-model-name-qmdn-itemid-274

for循环

1
2
3
For i=0 to 19 step 2  
Delay 100
Next

while循环

1
2
3
4
i=0  
While i=0
Msgbox "我停不下来啦!!快住手"
EndWhile

函数(Function)定义和调用

如何返回值

如何返回多个值

方案一:返回一个数组

1
2
3
4
5
6
7
8
9
10
Function 函数()
//任意函数内容代码
//可以直接定义一个数组后返回, 也可以直接生成一个数组返回
函数 = Array("第一个值", "第二个值")
End Function


Dim 数组 = 函数()
TracePrint 数组(0)
TracePrint 数组(1)

方案二:传入参数,改变参数的值

退出函数

1
Exit Function

调用函数

1
2
3
4
5
// 错误:当对函数返回值进行赋值时,参数没有加括号
Dim coords: coords = getCoordinatesForDoor Hwnd, "blue"

// 正确:无需返回值时,参数可以不加括号
getCoordinatesForDoor Hwnd, "blue"

如果函数有多个参数,且又想使用函数的返回值的话,则会出现问题:

1
2
3
4
5
6
7
8
9
10
11
12
```



## 后台脚本

### 获取窗口句柄

```shell
Hwnd = Plugin.Window.GetKeyFocusWnd() 获得当前激活的窗口句柄,激活的窗口鼠标不一定在上面
Hwnd = Plugin.Window.MousePoint() 获得鼠标当前停留的窗口的句柄,当前窗口状态未必激活(被点选)
Hwnd = Plugin.Window.Find(0, "无标题 - 记事本") 获取窗口标题栏为“无标题 - 记事本”的窗口的句柄

一般优先使用Plugin.Window.Find,如果窗口标题不固定,再考虑使用其他两个函数

注意,如果不打开窗口,是无法获得句柄的。标题名也不能写错,写错也无法获取。但是会有返回值。

常用窗口操作

1
2
3
4
5
6
7
Call Plugin.Window.Foreground()
Call Plugin.Window.Find()
Call Plugin.Bkgnd.KeyPress()
Call Plugin.Window.Foreground()
Call Plugin.Window.Max()
Call Plugin.Window.Min()
Call Plugin.Window.Restore()

延时

注意后台的延时,不用指定窗口句柄,直接Delay即可,类似这样:

1
2
3
4
Hwnd = Plugin.Window.MousePoint()  //获取鼠标所在位置的句柄
Call Plugin.Bkgnd.KeyPress(Hwnd, 112)// F1
Delay(Hwnd, 300000)// 直接延时即可
Call Plugin.Bkgnd.KeyPress(Hwnd, 51)// 数字键3

窗口

1
2
3
4
5
6
7
8
9
Hwnd = Plugin.Window.MousePoint()//获取鼠标所在位置的句柄

// 置顶/置底
Call Plugin.Window.Top(Hwnd, 0)
// 还原,一般是从最小化还原为非最小化
Call Plugin.Window.Restore(Hwnd)
// 隐藏,即转为后台进程,工具栏看不到这个进程和图标了
Call Plugin.Window.Hide(Hwnd)

区域找字

1
2
3
4
5
6
7
8
9
10
11
12
Call Plugin.Bkgnd.FindWordShape()
参数说明:
参数0,句柄:后台句柄
参数1,整数型:区域左上角X坐标
参数2,整数型:区域左上角Y坐标
参数3,整数型:区域右下角X坐标
参数4,整数型:区域右下角Y坐标
参数5,字符串型:文字内容
参数6,字符串型:字体
参数7,整数型:字号大小
参数8,整数型:查找方向
参数9,整数型:颜色相似度

可以参考这个前台的文档:http://www.vrbrothers.com/cn/qmacro/qmdn/htmls/plugin/pic.findwordshape.htm

注意这个找字并不精准,似乎是只要前缀符合就会被找到,比如“自动行驶”和“自动退出”,谁在左上角谁就会被先识别到。

区域统计颜色数量

用来判断身边怪物多不多,不多时切换随机,增加打怪效率。

按键自带的CountColor是不支持后台模式的,因此想要后台操作,得借助其他插件,比如大漠插件的GetColorNum函数。

如果必须只能用按键,那么还有个方案,就是按像素值逐个检查屏幕上的点的颜色:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
hwnd=1446860
Call 区域颜色数量(500,400,509, 406,"00FFFF")
Function 区域颜色数量(起点x,起点y,落点x,落点y,颜色值)
备份起点x=起点x
x次数=落点x-起点x+1
y次数=落点y-起点y+1
For y次数
起点x=备份起点x
For x次数
颜色= Plugin.Bkgnd.GetPixelColor(Hwnd, 起点x,起点y)
颜色1=颜色1&颜色&"/"
起点x=起点x+1
Next
起点y=起点y+1
Next
数组拆分=Split(颜色1,"/")
数量=UBound(数组拆分)
For i=0 To 数量
颜色=数组拆分(i)
If 颜色=颜色值 Then
个数=个数+1
End If
Next
MessageBox 颜色1&" 颜色数量"&数量&"符合"&颜色值&"的数量是"&个数
End Function

不过可以想见的是,这个会耗费很多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
nircmd.exe clipboard copyimage "image-file"

亲测好使。

配置文件

http://zy.anjian.com/?action-model-name-qmdn-itemid-219

1
2
Text = Plugin.File.ReadINI("e249b8eb-065f-4733-ad8c-a18e2882d94d", "Form1.StrikeMode_Remote.Value", "D:\software\按键精灵\按键精灵2014\test\uservar.ini")
Msgbox "得到一个配置文件键值为:"&Text

注意:如果通过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
Call RunApp("cmd.exe /c start """" /d ""D:\game\传奇3-泥塘"" ""Zircon.exe""")

导出脚本

如果脚本包含有界面或者附件,一定要用导出的方式进行传递,否则会因为丢失这些文件而无法使用。

按键小精灵

可以将你的脚本单独打包为一个exe可执行程序,方便别人使用,这种exe就被称为按键小精灵程序。

按键小精灵程序可以自定义内容,方便用户自己设置参数。

Q&A

按键小精灵需要本地预先安装按键精灵么?

不需要,打包好的程序已经包含了执行所需的内容,可以看到打包程序有8M多。

如何自定义内容?

可以通过UserVar(用于支持用户设置脚本参数)来实现,文档参考:http://zy.anjian.com/?action-model-name-qmdn-itemid-290

1
2
3
4
5
6
7
8
9
10
11
//标准型自定义变量
//格式:UserVar{变量}={默认值}{注释}
//解释:这个命令主要用于用户自定义的设置功能
UserVar var1=30 "当HP<多少%时,使用红药"
UserVar var2=30 "当MP<多少%时,使用蓝药"
//当用户使用这个脚本的时候,可以对这var1和var2这两个变量的取值进行设置
//下拉型自定义变量
//格式:UserVar变量=DropList{选项0:值0|选项1:值1|选项2:值2}=默认显示的选项编号{注释}
//解释:这个命令预先设定多个变量值,用户可在基面通过下拉方式进行选择
UserVar Var1=DropList{"低于10%时补血":0.1|"低于20%时补血":0.2|"低于30%时补血":0.3}=2 "自动补血设置"
//特别提醒:默认显示的选项编号从0开始

但是如果程序中用到了多线程,UserVar默认是无法传递到多线程中的;这个时候要借助下DimEnv,类似这样:

1
2
3
4
5
6
7
UserVar var1=30 "当HP<多少%时,使用红药" 
UserVar var2=30 "当MP<多少%时,使用蓝药"
UserVar Var3=DropList{"低于10%时补血":0.1|"低于20%时补血":0.2|"低于30%时补血":0.3}=2 "自动补血设置"

Dimenv v1 : v1 = var1
Dimenv v2 : v2 = var2
Dimenv v3 : v3 = var3

因为DimEnv是支持多线程的,所以这样就可以将用户定义的变量传递下去了。

(待验证)如何设置复选框?

可以通过按键精灵的界面功能来实现。

复选框只能绑定点击事件。

1
2
3
4
5
6
7
8
9
10
11
//首先 在界面上放1个“多选框控件”和1个“按钮控件”。   
//其次 复制以下脚本进行调试:

//判断选中情况
Event Form1.Button1.Click
If Form1.CheckBox1.Value = 1 Then
Msgbox "您勾选了!"
Else
Msgbox "您取消勾选了!"
End If
End Event

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//简易注册大漠
PutAttachment ".\plugin", "RegDll.dll"
PutAttachment ".\plugin\dm", "dm.dll"
PutAttachment "c:\test_game", "*.*"
Plugin.RegDll.Reg (Plugin.sys.GetDir(0) + "\plugin\dm\dm.dll")
// 必须用Set,不能用Dim
Set dm = createobject("dm.dmsoft")


//获取鼠标指向句柄
hwnd = dm.GetMousePointWindow()


//绑定句柄
dm_ret = dm.BindWindow(hwnd,"dx2","windows","windows",0)


//设置大漠字库
dm_ret = dm.SetDict(0, "C:\字库.txt")


//识别区域文字
s = dm.Ocr(17,262 ,58,280 ,"ffffff-000000",0.9)


//区域找字
dm_ret = dm.FindStr(0, 0, 2000, 2000, "回收", "ffffff-000000", 1.0, X, Y)


//区域找图
Plugin.dm.FindPic 228,670,309,696,"c:\test_game\摊主.bmp","000000",0.8,0,X,Y


//区域找色
Plugin.dm.FindColor 0,0,2000,2000,"123456-000000|aabbcc-030303|ddeeff-202020",1.0,4,X,Y

提示信息

当出现异常时,我们希望能发出一些提示,让用户可以及时处理。

声音

窗口置顶+抖动

注意事项

RGB色值问题

FindColor的色值和我们平时的RGB不一样,顺序是GBR的

比如纯红色,不是FF0000,而是应该写成0000FF

调试颜色的时候,可以用windows的画图工具,画几个色块来辅助调试,效率会高很多。

制作bmp图片时,越小越好

越小越好,只要能标识你想要的效果就行;

越大越容易加入冗余像素,导致识别不出来。

后台运行时,无法执行鼠标点击操作

如果不点击/异常说明存在屏蔽机制 尝试第三方插件修改键盘/鼠标/图色绑定模式进行解决[例:大漠/361/大兵],如果点击查看最后一句确认是句柄或坐标错误[如果原先句柄与坐标正确 本次点击 则说明游戏存在需要前台鼠标也在指定位置的屏蔽机制 尝试先Call Plugin.Bkgnd.MoveTo(Hwnd,,)后点击等方式

大漠插件也遇到这个问题:

请问下,你的这个问题解决了吗,我使用大漠插件也遇到了同样的问题,一直无法解决。
我绑定窗口句柄后,使用后台鼠标,打印鼠标位置,是两个负数,然后移动鼠标到一个位置,再次打印鼠标位置,是鼠标在该窗口内的位置(我确认过,打印的不是前台鼠标位置),因此我认为后台鼠标是可以移动的,但是接下来在该位置点击一个按钮,却是始终无效。
另外,如果使用dm.GetMousePointWindow()来获取句柄,并且运行脚本前先用鼠标点击一下窗口,那么后台鼠标的点击是可以正常使用的。如果运行前,只是将鼠标放在窗口而不点击,那么同样后台鼠标只能移动却不能点击。
测试所知道的就这些,但是既然使用后台,当然不想每次运行前都拿鼠标点一下窗口。所以想使用dm.FindWindow找到句柄来操作,不这样后天鼠标又不能点击,很蛋疼。

目前尚未找到解决方案,想到一个折中的办法,就是每次执行操作前,将窗口pop到最顶层:

1
2
// 后台模式下,无法执行鼠标点击操作,因此必须先将游戏窗口置顶
Call Plugin.Window.Active(Hwnd)

另外经过我测试,按键自带的后台鼠标操作,和大漠的,是差不多的,因此没必要用大漠插件,直接用自带的即可。

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
2
3
4
5
6
7
iCoord = Plugin.Bkgnd.FindWordShape(Hwnd, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, "拾取过滤", FONT, FONT_SIZE, 0, 1)
XY = Split(iCoord, "|")
intX = Clng(XY(0))
intY = Clng(XY(1))
If intX > 0 And intY > 0 Then
TracePrint "找到设置页卡了:" & intX & "," & intY
End If

因为我没有通过Dim定义变量iCoord、XY、intX、intY,导致重复执行这个脚本的时候,这些变量被当成全局变量,其值已经被污染了,就会导致无法识别到文字。

RunApp的路径问题

有的exe程序,执行的时候依赖于相对路径,如果直接以绝对路径执行,是会出错的:

1
Call RunApp("D:\game\传奇3-泥塘\Zircon.exe")

这种情况就必须制定程序执行的工作目录,类似这样:

1
Call RunApp("cmd.exe /c start """" /d ""D:\game\传奇3-泥塘"" ""Zircon.exe""")

  • 识字并不精准,比如”血”字识别就有问题;也可能和字体等有关系

遇到过的问题

自动随机乱飞

可能有如下几个原因:

  • 窗口被最小化了,不再刷新,导致无法识别到红点
  • 小地图窗口被其他界面窗口遮住了
  • 移动了小地图窗口的位置

缺少标识符、类没有被定义

下面这2个函数,很类似,但是第一个可以正常运行,第二个一旦调用就会报上面的错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 有问题
Function getCoordinatesForDoor(type)
TracePrint type
getCoordinatesForDoor = 123
End Function

Dim coordnates : coordnates = getCoordinatesForDoor("red")

// 没问题
Function findCoordinatesForText(text)
TracePrint text
findCoordinatesForText = 123
End Function

Dim coordnates1 : coordnates1 = findCoordinatesForText("red")

原因在于:参数不能是关键词!

我第一个函数的参数用了type,这应该是个关键词;改为其他的单词就没问题了

语法错误:(错误码0)没有找到合法的符号

常见于我们用括号的方式调用了一个函数,但是没有给其返回值赋值为变量:

1
dm.GetColorNum(0, 0, 1920, 1080, "FF0000-000000", 1.0)

正确的应该是这样:

1
Dim colorNumber: colorNumber = dm.GetColorNum(0, 0, 1920, 1080, "FF0000-000000", 1.0)

如果不需要返回值,则不应该用括号将参数括起来,应该这样:

1
dm.GetColorNum 0, 0, 1920, 1080, "FF0000-000000", 1.0

不能双开脚本

我给悠然做了一个脚本,然后发现复制脚本设置不同的快捷键启动,总是只能执行一个脚本,后面一个脚本启动后,前面一个脚本就不执行了。

经过排查,发现是窗口句柄的问题,不能在程序一开始就获取窗口句柄,而必须在子进程中再获取窗口句柄

运行一段时间程序崩溃

常见解决方案: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 是应用程序和应用程序之间进行消息传递的主要手段之一。

C#可以:https://docs.microsoft.com/zh-cn/dotnet/api/system.windows.forms.sendkeys?redirectedfrom=MSDN&view=net-5.0

可以用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元/月),可以提问,有人回答:

http://bbs.anjian.com/

视频课程:

https://ke.qq.com/course/68273

常用操作的代码示例:

https://www.cnblogs.com/erbing/p/7755747.html

大漠插件(文字识别、图像识别)制作字库:

http://www.dmwebsite.net/

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

https://blog.csdn.net/aloyz64466/article/details/101846712?utm_medium=distribute.pc_relevant_download.none-task-blog-2~default~blogcommendfrombaidu~default-1.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-2~default~blogcommendfrombaidu~default-1.nonecas

多线程内外不共通,因此必须在多线程内部重新绑定;若不绑定,则为前台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Hwnd = Plugin.Window.MousePoint()//获取鼠标所在位置的句柄

BeginThread AutoPressKey

Delay 1000
BeginThread AutoPressKey1

Sub AutoPressKey()
//必须在多线程内部重新绑定
Hwnd = Plugin.Window.MousePoint()//获取鼠标所在位置的句柄
While True
Call Plugin.Bkgnd.KeyPress(Hwnd, 51)// 数字键3,随机卷轴
TracePrint "触发按键:51"
Delay 1000
Wend
End Sub

Sub AutoPressKey1()
Hwnd = Plugin.Window.MousePoint()//获取鼠标所在位置的句柄
While True
Call Plugin.Bkgnd.KeyPress(Hwnd, 52)// 数字键3,随机卷轴
TracePrint "触发按键:52"
Delay 1000
Wend
End Sub

如何将外部参数传入多线程内部呢:通过环境变量DimEnv来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Dim xy(99)
// 环境变量
DimEnv a
i=0
for 100
xy(i)=i
i=i+1
next
a=xy(88)
BeginThread 测试
EndScript
Sub 测试()
MessageBox a
End Sub

这里还有个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