Skip to content

Latest commit

 

History

History
547 lines (419 loc) · 31.8 KB

File metadata and controls

547 lines (419 loc) · 31.8 KB

GoRobotScript

基于 Go 语言(Go 1.25+)开发的轻量级 Windows 自动化框架,支持键盘、鼠标(含 3D/VR 游戏视角)、虚拟 Xbox 手柄的精准录制与回放,内置 Lua 自动化脚本引擎识图关键点对齐(录制时截图取样,回放时重新定位起点/终点并把路径旋转拉伸对齐)。

文档导航

文档 面向 内容
本 README 所有人 安装、录制、回放、打包快速上手
doc/LUA_API.md 开发者 Lua 接口完整手册(参数表、返回值、示例、陷阱)
doc/LUA_API_AI.md AI / 代码生成 紧凑接口清单(精确签名、语义、失败模式)
doc/ARCHITECTURE.md 维护者 模块架构、打包机制、缓存布局、命名规范

核心组件与工具说明

通过一键编译后,bin/ 下的每个产物都与 Core/ 中的一个模块目录一一对应

Core 模块 产物 说明与功能定位
Core/GoRunner GoRunner.exe 录制 + 回放。无参数双击进入智能录制模式;带参数回放 .script 或执行 .lua
Core/GoPacker GoPacker.exe 打包工具。把 .lua / .script 打包为单文件 EXE,或用 -unpak 生成明文调试目录。
Core/GoLua GoLua.apppak Lua 运行时载荷(实为可执行程序,用 .apppak 后缀区别于工具、也避免与资源包 .pak 混淆)。
Core/GoPacker/SingleLoader SingleLoader.apppak 单文件引导器载荷。打包时被拼到产物开头,双击产物时由它解压并启动真正程序。无需也无法手工运行

用户实际只需要认识 GoRunner.exeGoPacker.exe 两个工具;两个 .apppak 是内部载荷。 其余 Core/ 模块(GoInputGoVisionGoFSMGoPak)均为,不产出可执行文件。


快速上手

1. 操作录制(双击即用)

  1. 直接双击打开 bin/GoRunner.exe,程序进入录制待机状态。
  2. 切换至目标游戏或应用窗口。
  3. 全局热键控制与音频提示(物理级扫描,全屏游戏内同样有效):
    • 首次按 PageUp:开始录制(双声上扬提示音)
    • 再次按 PageUp:暂停 / 继续 切换(暂停双低音 / 继续单高音)
    • Pause识图开启 / 关闭 切换(状态就地刷新,见下节)
    • 短按 Home:识图开启时预设槽前进一个(长按 Home 则开关光标绘图框)
    • End:识图开启时预设槽后退一个
    • PageDown:结束录制并自动保存(两声下降提示音,同时退出识图与绘图框)
  4. 录制产物为 .script 文件,自动保存在 bin/script/,以时间戳命名。

录制内容涵盖:键盘、鼠标(含 3D/VR 视角的原始相对位移)、手柄全部轴向与按键

也可指定输出路径:

bin\GoRunner.exe record
bin\GoRunner.exe record bin\script\MyTask.script

1.1 录制识图样本(关键点对齐)

录制过程中按 Pause 即可开启识图:此后每次鼠标左键/右键按下与松开, 都会以光标为中心截取一张样本图保存下来,回放时用这张图把该关键点重新定位到 "画面实际位置"上,并对关键点之间的鼠标路径做旋转 / 拉伸对齐

控制台状态固定在同一行同一列就地刷新,不换行、不撑长控制台:

[Vision] 识图关闭
[Vision] 识图开启-图像32*32p  槽位1/6  绘图框:关  (短按Home切槽/长按开关框)
[Vision] 识图开启-颜色64*64p  槽位5/6  绘图框:开  (短按Home切槽/长按开关框)
  • 6 个预设槽Home 短按前进一个 / End 后退一个,环形切换):

    槽位 1 2 3 4 5 6
    方式 图像 图像 图像 颜色 颜色 颜色
    尺寸 32 64 128 32 64 128

    —— 图像 走模板匹配,颜色 走颜色卷积匹配。

  • 长按 Home(≥450ms)开关绘图框:一个红色 3 像素边框的空心方框挂在光标上, 尺寸实时等于当前预设槽的采样尺寸 —— 也就是"这一枪会截取多大范围"的可视化。 详见下节"绘图框"。

  • 样本命名状态+按键+序号,序号 3 位,各组合独立计数: md-left-001.pngmd-right-001.pngmu-left-001.png

  • 每次点击只存一张图(尺寸 = 当前预设槽)。加速靠回放端的识别流水线, 不需要额外多存样本(详见下面"识别流水线")。

  • 样本尺寸恒定:每张样本图永远是 vz×vz(32/64/128)的正方形。 光标贴到屏幕边缘时不把图裁小,而是把整块采样区域向屏幕内平移 —— 这样样本尺寸、颜色网格描述、匹配尺度全都保持一致,便于后续统一处理; 代价是此时 vx/vy(光标在图内的偏移)不再是 vz/2,回放侧已按记录值还原。

  • 样本图不含绘图框:红框画在采样区域外侧(见下节"绘图框"), 开框/关框截出来的样本逐像素完全一致,模板不会被红框污染。

  • 样本目录:与脚本同级同名的 <脚本名>.vision\ (例:bin\script\A.scriptbin\script\A.vision\)。

  • 脚本内会写入独立标记行,方便后续处理:

    {"dt":0,"op":"vision","vm":"image","vz":32,"von":true,"vsl":1}
    {"dt":0,"op":"vision","von":false}
    {"dt":0,"op":"vslot","vm":"color","vz":64,"vsl":5}
  • 识图状态不跨轮保留PageDown 结束录制即退出识图,下一轮录制需重新按 Pause


1.2 光标绘图框(长按 Home 开关)

识图开启后**长按 Home(约 0.45 秒)**即可让一个方框挂在光标上:

  • 红色边框、线宽 3 像素,方框尺寸 = 当前预设槽的采样尺寸 + 边框宽度×2(38/70/134), 也就是实时显示"这一枪会截取多大范围";短按 Home 换槽时方框会同步改变大小。

  • 边框画在采样区域之外:方框的空心内圈正好等于会被截取的 size×size, 所以红色边框永远不会被截进样本图里 —— 模板保持干净,不会污染后续匹配。

  • 不改变鼠标指针:窗口过程处理 WM_SETCURSOR 且不调用 SetCursor, 因此方框在鼠标下方移动时不会把指针重置成系统箭头(不会出现 画笔↔箭头 反复闪烁)。

  • 方框始终是完整满尺寸:光标贴到屏幕边缘时,方框会像采样区域一样贴住屏幕边而不是被裁小, 因此你看到的框永远等于实际会写到磁盘的那张图的尺寸。

  • 跟随鼠标移动,只在光标位置变化时才移动窗口,不做任何全屏重绘。

  • 采用标准的拖动虚影窗口做法(置顶 WS_POPUP + 用窗口区域挖空内部只留边框):

    性质 取值 意义
    WS_EX_TRANSPARENT + WM_NCHITTEST=HTTRANSPARENT 鼠标完全穿透,不挡游戏里的点击与拖拽
    WM_SETCURSOR 返回 TRUE 且不调用 SetCursor 不改动鼠标指针形状,跟随移动时不会闪成系统箭头
    WS_EX_TOPMOST 始终浮在游戏画面之上
    WS_EX_TOOLWINDOW 不出现在 Alt+Tab / 任务栏
    WS_EX_NOACTIVATE 永不抢焦点,不会把游戏切到后台
  • 再次长按 Home 关闭;按 Pause 关闭识图、按 PgDn 结束录制时都会自动收起并销毁, 不会在下一轮残留。

  • 短按(<450ms)仍是"切换预设槽",长按才开关绘图框 —— 两者互不干扰。


2. 脚本回放执行

支持以下几种回放方式:

交互回放模式(推荐)

直接双击将 .script 脚本拖拽到 GoRunner.exe 上,或在命令行中运行:

bin\GoRunner.exe bin\script\MyTask.script
  • PageUp 开始执行(提供充足准备时间切换至目标窗口);
  • 回放过程中可按 PageUp 随时暂停/恢复;
  • 遇到意外情况可按 PageDown 紧急强制停止退出。

快速执行模式(无人值守)

加上 -f 参数,程序启动后直接开始执行到脚本结束,无需按键等待:

bin\GoRunner.exe -f bin\script\MyTask.script

倍速回放

加上 -t 参数指定时间缩放速率(如 1.5 倍速加快,0.8 倍速减慢):

bin\GoRunner.exe -t 1.5 bin\script\MyTask.script

识图对齐(关键点重定位 + 路径旋转/拉伸)

脚本中带识图样本时,回放会自动启用关键点对齐。启动横幅与每段的识别结果都会打印出来 (匹配度是百分比,越大越像):

[Vision] 检测到 6 个识图关键点,5 段路径将按识别结果旋转/拉伸对齐
[Vision] 样本目录: bin\script\A.vision (匹配度阈值 85%, 区域置信度 97%, 缩放 0.90~1.10, 预取窗口 5, 快速区域 4 倍边长)
[Vision] 路径段#1 帧108~109  纯平移对齐(起点可信)  ← 放弃旋转/拉伸:两端录制位置重合(同一次点击) => 纯平移
         起点(1868,613)->(1974,615) [匹配度 99.9%]  终点(1868,613)->(1974,614) [匹配度 98.8%]
[Vision] 路径段#2 帧109~177  旋转/拉伸对齐
         (位移比例0.448x 超出常规 0.50~2.00,但两端都是高置信度命中 => 照做)
         起点(1974,613)->(1968,613) [匹配度 99.7%]  终点(1975,610)->(1975,718) [匹配度 99.9%]  旋转91.1° 缩放0.981x
...
[Vision] 识别流水线统计: 全屏标准识别 2 次,同键对复用 1 次,受限区域命中 15/26 次(置信度不足扩大范围 4 次),后台预取 16 次(命中 16),复核 33 次(通过 33 / 重新定位 0)

参数一览

参数 默认 作用
-vs 85 匹配度阈值(百分比,越大越严格)
-vconf 97 受限区域结果的置信度门槛0 或负数关闭门控,见下文)
-vblur 3 匹配前高斯模糊核(≤1 关闭),抵消动态模糊/锐化/分辨率差异
-vmin / -vmax 0.9 / 1.1 匹配时的缩放搜索范围(游戏分辨率与录制时不同就放宽
-vrot 180 允许的最大旋转角(度);≥180 等于不限制(整整一圈)
-vpair 32 同键对阈值(像素):按下/松开位移不超过它时视为"同一处"
-vsettle 30 点击那一刻光标若还需再挪一下(位置刚被修正),挪过去后等这么久再按下/松开(0 关闭)
-vfast 5 预取窗口:提前识别后续几个关键点(0 关闭预取)
-vcache 4 快速区域边长 = 样本边长 × 该系数
-vtol 6 识别结果复核的位置容差(像素)
-vdir 自动 样本目录,默认按 <脚本名>.vision 推导

匹配度是怎么算的

回放识别用的是 ZNCC(零均值归一化互相关):先各自减掉平均亮度再算相关, 因此对整体亮度、对比度,以及动态模糊、局部锐化、分辨率差异都不敏感 —— 它比的是结构/特征,而不是逐个像素的值;结果天然落在 0~100%

实测(2560×1440,32×32 样本,同机同屏):

情形 匹配度
完全相同 100.0%
画面被 5×5 模糊(分辨率偏软) 99.1% ~ 99.8%
画面被 9×9 重度模糊 98.2% ~ 99.7%
画面被锐化 99.9%
无关内容(随机噪声模板) 约 10%

所以默认阈值 85%:真实命中(哪怕画面糊成一片)都能过,无关内容一律挡掉。 匹配度只比阈值高一点点的会在日志里标注 偏低,提示这次属于"勉强通过"。

界面很糊、样本本身纹理又弱(例如只有一个笔画的数字)时,可以把 -vblur 0 关掉预模糊: 模糊会把唯一的区分特征也一起抹平。

对齐规则

  1. 鼠标按下/松开的每一帧都是一个关键点;关键点之间的整段路径用一个 相似变换(旋转 + 等比拉伸 + 平移)对齐,该变换恰好把 录制起点→识别起点录制终点→识别终点,因此两端严丝合缝、段内不跳变
  2. 这一轮的结束点就是下一轮的起点(相邻段在关键点处共用同一个识别结果)。
  3. 中途关闭过识图时关键点链在此断开:关闭期间的路径按原坐标回放, 重新开启后的起点不沿用上一轮的终点
  4. 宁可不对齐,也不乱跳。识别失败或两端结果互相矛盾时自动降级: 旋转/拉伸对齐纯平移对齐(形状不变)按原坐标回放, 降级原因会打印在段信息后面(例如 ← 放弃旋转/拉伸:旋转-159.1° 超出 ±15°)。 分级细则见下表。
  5. 某个关键点失败只回退它自己:其它关键点匹配成功的结果继续作为参考 (日志里就是 纯平移对齐(终点可信) —— 起点没用上,终点照样对齐)。

段变换的可信度分级

情形 行为
两端都命中、旋转在 -vrot 内,且两端都是高置信度命中(≥ -vconf 旋转/拉伸对齐,起终点精确对齐(位移比例多离谱都照做,见下)
两端都命中、旋转在 -vrot 内,但至少一端只是勉强命中(< -vconf 位移比例落在 [0.5, 2.0] 才做旋转/拉伸;越界则降级为纯平移(取匹配度更高的一端)
两端都命中但旋转超出 -vrot 降级为纯平移(取匹配度更高的一端)
两端录制位置重合(同一次点击的按下/松开) 相似变换无定义 => 纯平移
只有一端命中 该端做纯平移,路径形状不变
两端都没命中 按原坐标回放

为什么位移比例不做门限

内容每轮重排的界面(舒尔特方格每轮随机打乱)里,同一段的两端在新一轮可能落在任意两格 —— 录制时相距 300px、这一轮只相距 100px(比例 0.45x)完全正常,这不是"两端矛盾", 而是这一轮的真实布局。以前拿 [0.5, 2.0] 去卡它,会把这类段降级成"纯平移(起点可信)", 也就是按录制时的旧位置回放:路径朝下一个点击目标的反方向走,到点击那一刻才跳回来。 所以现在只有"至少一端是勉强命中"时才用几何比例兜误匹配。 相似变换本身也不做缩放限幅 —— 限幅会让端点对不齐(路径少走一截再跳过去)。

关于旋转与"360 度":路径旋转的完整范围确实是一整圈,但"旋转 θ"与"旋转 θ−360°"是同一个旋转, 所以用有符号角表示时值域天然就是 (−180°, +180°],两端接起来正好是 360° 全圆。 段日志按这个约定打印:+90° = 顺时针四分之一圈,-159° = 逆时针 159°(等价顺时针 201°)。 因此限制值 ≥ 180 就等于不限制-vrot 360 同样)。

界面类型 该怎么做 原因
内容每轮重排(舒尔特方格每轮随机打乱 9 个数字) 保持默认 -vrot 180 同一个数字回放时本来就可能跑到另一个格子,A→B 的方位翻转 180° 都正常;限死旋转会让端点落不到识别位置,点击偏格
固定布局(元素不会重排) -vrot 15 之类收小 大旋转往往意味着有一端打到了"长得几乎一样"的另一个元素;限死旋转会自动放弃旋转/拉伸、退回纯平移,避免路径被横拉过去

识别流水线(加速策略)

回放时整份脚本一次性读入内存,关键点、连通关系、每帧归属全部提前聚合完成; 识别本身则由一条流水线负责:

环节 做什么
同键对并发扫描 按下/松开两张图位置几乎相同时,让它们在同一个区域里同时找:谁先命中就用谁的结果,另一张按录制位移直接换算,另一侧还在跑的扫描立刻作罢("谁先亮灯用谁的")
同键对一致性 位移 ≤ -vpair(默认 32px)时,按下与松开必须落在同一处:后来那张只在"位移+容差"的极小区里复核,极小区没命中就按录制位移与先命中那侧对齐 —— 绝不到更大范围另找一个位置(那会把一次点击拆成一次拖拽)。32px 样本、2px 位移时这块区域只有 60px 见方
快速区域缓存 某个关键点识别成功后,在它周围留下边长 = 样本边长 × -vcache 的区域;后续关键点优先只在这个区域里找
区域置信度门控 受限区域里的最优只是局部最优:区域外可能还有更像的目标。因此区域结果必须达到 -vconf(默认 97%),达不到就逐级扩大范围重找
逐级扩大范围 极小区(同键对复用)→ 快速区域 → 扩大区域(-vcache × 3)→ 全屏。只有全屏是全局最优,所以全屏只要求阈值 -vs
后台预取 开启预取窗口(默认提前 5 个关键点),用协程在回放空档里把这些关键点的大概位置先标出来(候选)
采用前复核 真正轮到某个关键点时,用实时画面在候选附近复核一次:通过就直接采用(加速完成),不通过立刻重新定位(逐级扩大范围,必要时全屏)
窗口滑动 滑出窗口的在途预取协程被取消,不做无用功

加速策略只影响"在哪找":任何一级没接住或不够可信,都会完整回到常规路径 (同键对复用 → 快速区域 → 扩大区域 → 全屏),判定标准不受加速策略影响。 全屏识别最多同时跑 2 个(避免多个预取把 CPU 抢光),抓屏串行化。

点击与路径的正确性保证(回放主线)

识图对齐会修正关键点的位置,因此主线遵守三条硬规则,保证"点到的就是识别到的位置":

  1. 关键点帧的坐标 = 该关键点自己最终确认的识别位置,不经过"可能还是旧值的段变换"。
  2. 位置一旦变化,引用它的段变换立即作废重建(含最后一个关键点之后的收尾路径), 所以不会出现"点完又跳回旧位置"。
  3. 停顿开始前,光标就已经放到该关键点当前已知的位置上。录制的语义是 "鼠标先到位 → 停一下 → 点击";停顿期间光标就等在正确位置上,界面看到的悬停/焦点状态 也和录制时一致,点击因此不会被算在旧位置上。若点击那一刻光标还需要再挪一下(位置刚被修正), 挪过去后先等 -vsettle(默认 30ms)再按下/松开。

常见症状 → 原因 → 处理

症状 原因 现在的行为 / 参数
先朝错误方向走、停在错的地方,点击那一刻横跳到正确位置 提前标记是在受限区域里找的,区域里有相似目标时它会挑到"最不坏"的错误位置 区域置信度门控:区域结果必须 ≥ -vconf,否则扩大范围,直到全屏的全局最优。可把 -vconf 调高(如 99)
点击被算在旧位置上("先点了再移动") 位置在点击瞬间才被修正,光标是"瞬移"过去的,界面还没看到它到位 停顿前先把光标放到已知位置;点击瞬间被修正时等 -vsettle(默认 30ms)
按下与松开落在两个地方(一次点击变成拖拽) 松开那侧跑到更大范围里另找了一个位置 同键对一致性:-vpair 内只在极小区复核,没命中就按录制位移与按下侧对齐
两段之间朝目标反方向走,快到了才跳到正确位置 该段的位移比例越界被降级为"按录制旧坐标回放"(重排布局里比例本来就可以任意) 两端都是高置信度命中时照做旋转/拉伸;只有勉强命中才用 [0.5, 2.0] 兜底
整段被横拉/乱转 有一端打到了"长得几乎一样"的另一个元素 固定布局界面把 -vrot 收小(如 15),旋转超限即退回纯平移
明明有样本却没走加速 匹配度不够、区域里没有、或预取没跑起来 GOROBOT_VISION_DEBUG=1 跑一次,见下

排查与调试

$env:GOROBOT_VISION_DEBUG = "1"
bin\GoRunner.exe -f bin\script\A.script
# [Vision/debug] 锚点#0 无预取候选 => 标准识别
# [Vision/debug] 锚点#0 全屏识别命中 (1974,615) 匹配度=99.9%
# [Vision/debug] 启动预取 5 个关键点 (窗口 2~6)
# [Vision/debug] 预取成功 锚点#2 -> (1975,718) 匹配度=99.9%
# [Vision/debug] 锚点#1 同键对复用命中 (1974,614) 匹配度=98.8%      <- 按下/松开复用
# [Vision/debug] 锚点#3 标准识别(快速区域 (1705,647)-(1865,807))命中 (1769,711) 匹配度=100.0%
# [Vision/debug] 锚点#7 快速区域 (616,432)-(776,592) 命中但置信度不足 (96.2% < 97%) => 扩大范围
# [Vision/debug] 锚点#9 就近复核未通过 (原标记 1866,604 存储匹配度=100.0%;复核 1866,604 匹配度=88.1% 命中=true,门槛 97%) => 重新定位

现场实测(2560×1440,9 组点击 = 18 个关键点):

[Vision] 识别流水线统计: 全屏标准识别 2 次,同键对复用 1 次,受限区域命中 15/26 次(置信度不足扩大范围 4 次),后台预取 16 次(命中 16),复核 33 次(通过 33 / 重新定位 0)

即:只有没有预测来源的少数关键点走了全屏搜索,其余都由快速区域 / 同键对解决, 且采用前复核全部通过(0 次重新定位)。

另外两个已修掉的隐患: ① 匹配结果矩阵的极值定位原先在 Go 侧逐像素 GetFloatAt(全屏约 300 万次 cgo 调用), 改用 OpenCV 原生 MinMaxLoc; ② 预取协程并发累加统计量存在数据竞争,改用原子计数(go test -race 全绿)。

只读约定(重要)

录制时按正常方式录制,不做任何特殊处理;回放时把整份 .script 一次性读入内存, 在内存里完成关键点聚合与路径调整,原脚本文件全程只读、一字节都不会被改写 (回放前后哈希与修改时间完全一致)。调整结果只作用于当次回放的内存副本, 因此脚本可以放心留档、反复回放。


3. Lua 脚本编写与执行

GoRunner.exe 内置完备的 Lua 运行时,支持调用 Go 原生封装的高性能键鼠控制、多尺度模板匹配、颜色匹配与有限状态机:

bin\GoRunner.exe MyScript.lua

常用 Lua 内置模块

模块 能力
GoInput(别名 SuKey 键盘/组合键、鼠标移动与点击、后台窗口点击、滚轮;虚拟 Xbox 360 手柄(双摇杆 / 双扳机 / 14 个按键);全局热键检测;系统提示音
GoVision(别名 SuScreen 窗口截图、多尺度模板匹配、颜色卷积匹配、单点取色、多点比色
GoFSM 有限状态机调度,适合复杂流程自动化
CallGo 旧版兼容(系统对话框等)

完整接口手册见 doc/LUA_API.md(含全部参数、返回值、错误约定与陷阱)。 AI / 代码生成请用 doc/LUA_API_AI.md

最小示例

local input  = require("GoInput")
local vision = require("GoVision")

-- 视觉:截窗口并匹配模板,命中则点击中心
vision.CaptureClient("MyGame", "shot.png")
local r = vision.MatchTemplate("shot.png", AssetPath("btn_start.png"), 0.9, 1.1, 0.05)
if r and r.percent >= 85 then        -- percent = 匹配度百分比 (0~100,越大越像)
    input.Move(r.centerX, r.centerY)
    input.Click()
end

-- 键盘
input.KeyTap("e")                   -- 单键
input.KeyTap("s", "ctrl")           -- Ctrl+S
input.Sleep(200)

-- 热键循环控制(PgUp 开始/暂停,PgDn 结束)
while not input.CheckPgDnTrigger() do
    if input.CheckPgUpTrigger() then print("切换开始/暂停") end
    input.Sleep(20)                 -- 热键是上升沿,需高频轮询
end

虚拟手柄接口(Xbox 360)

local pad = require("GoInput")

pad.Gamepad{ ly = 32767 }                                   -- 左摇杆推满向上
pad.Gamepad{ lx = 0, ly = -32768 }                          -- 左摇杆推满向下
pad.Gamepad{ buttons = pad.BTN.A }                          -- 按下 A
pad.Gamepad{ buttons = pad.BTN.START + pad.BTN.LB }         -- 组合键(掩码相加)
pad.Gamepad{ buttons = pad.BTN.A, rt = 255, rx = 20000 }    -- 按键 + 扳机 + 右摇杆
pad.Gamepad(0, 32767)                                       -- 简写:仅左摇杆
pad.GamepadReset()                                          -- 全通道归零
pad.GamepadSlots()                                          -- 已连接手柄占用的 XInput 槽位
pad.GamepadClose()                                          -- 释放虚拟手柄
  • 表内未给出的字段保持原值,可「按住按键的同时推摇杆」;摇杆/扳机是状态不是事件,暂停或退出前务必 GamepadReset()
  • pad.BTN 常量(可直接相加):A B X Y / LB RBL1 R1)/ LS RSL3 R3)/ START BACK / UP DOWN LEFT RIGHT
  • buttons 支持三种写法:数字掩码 / 名称字符串 "A" / 名称数组 {"A","LB"}

虚拟手柄基于 ViGEmBus 总线驱动。系统未安装时 build.ps1 或程序会自动静默安装。 注意虚拟手柄会占用最小空闲槽位:若物理手柄已占槽位 0,虚拟手柄会落到槽位 1, 只读取槽位 0 的游戏会忽略它 —— 此时回放手柄脚本需暂时拔掉物理手柄(可用 GamepadSlots() 确认)。


4. 脚本与资源打包 (GoPacker)

单文件独立 EXE(默认)

# 打包 Lua 自动化脚本
bin\GoPacker.exe Projects\MyProject\MyProject.lua

# 打包录制回放脚本(.script)
bin\GoPacker.exe bin\script\VRView3D_2026-09-10_21-59-15.script

GoPacker 支持两类脚本,按扩展名自动分流:

脚本类型 打包后运行方式 说明
.lua Lua 引擎执行 自动化逻辑脚本;同目录有内容的 Asset\ 会自动压成 .pak 装入
.script 回放引擎重放 录制下来的键鼠/手柄动作数据;同样支持 -f(直接播完)与 -t(倍速)

打包后的单文件 EXE 对 .script 只认 -f-t <倍率>-vt <匹配度百分比>-vt 就是散装命令行里的 -vs),其余识图参数尚未透传。 调试期想随便调参数,用 GoPacker.exe -unpak 生成明文目录后直接跑 GoRunner.exe

  • Asset\ 为空目录时视为无资源,不生成 .pak
  • .script 的识图样本 <脚本名>.vision\ 会自动内嵌进单文件 EXE(负载 V3 格式), 运行时解包到 %LOCALAPPDATA%\GoRobotScript\vision_<哈希>\ 复用,产物始终只有一个 exe
  • 输出程序名默认取脚本文件名,可用脚本内 PackageInfo("MyBot.exe", "1.0.0") 指定。
  • 期望「独立文件夹模式」(EXE + .pak + DLL 同目录)时,在脚本里加 -- @pack_mode folder

调试模式(-unpak,不封装)

bin\GoPacker.exe Projects\gamepad_left_stick_loop\gamepad_left_stick_loop.lua -unpak
bin\GoPacker.exe bin\script\VRView3D_2026-09-10_21-59-15.script -unpak

产出 bin\run\<名称>\脚本与资源保持明文、不做任何封装

Lua 脚本:                              录制脚本:
bin\run\gamepad_left_stick_loop\       bin\run\VRView3D_2026-09-10_21-59-15\
├── gamepad_left_stick_loop.exe        ├── GoRunner.exe
│     ← 由 GoLua.apppak 复制改名        │     ← 由 GoRunner.exe 复制(回放引擎)
├── gamepad_left_stick_loop.lua        ├── VRView3D_..._21-59-15.script
├── Asset\                             ├── VRView3D_..._21-59-15.vision\
└── *.dll                              │     ← 识图样本,改完直接重跑
                                       └── *.dll
                                       (运行:GoRunner.exe <脚本>.script)
  • Lua 形态:双击 <名称>.exe 即可运行(无参数时自动执行同目录同名 .lua,其次 main.lua,再其次目录内唯一的 .lua)。
  • 改完脚本、资源或识图样本 直接重跑,无需重新打包 —— 这就是它存在的意义。

5. 录制脚本 .script 格式

录制产物是 JSON Lines(每行一个动作帧),纯文本、可直接手改:

{"dt":3,"op":"init","x":1122,"y":596}
{"dt":0,"op":"vision","vm":"image","vz":32,"von":true,"vsl":1}
{"dt":1455,"op":"gp","gp_ly":2258}
{"dt":15,"op":"rmv","dx":-3,"dy":1}
{"dt":16,"op":"md","btn":"left","x":1012,"y":256,"vi":"md-left-001","vx":16,"vy":16,"vm":"image","vz":32}
{"dt":0,"op":"vision","von":false}
字段 说明
dt 距上一帧的毫秒间隔(回放按此节流,受 -t 缩放;标记行恒为 0 且不占用时间轴)
op 动作类型:init 起点锚定 / mv 光标绝对移动 / rmv 3D视角相对位移 / kd·ku 键按下弹起 / md·mu 鼠标按下弹起 / mw 滚轮 / gp 手柄一帧 / vision·vslot 识图状态标记
vi vx vy vm vz 识图样本名 / 光标在样本图内的偏移 / 匹配方式 / 采样边长(md·mu 帧携带)。样本尺寸恒为 vz×vz
von vsl 识图开关 / 槽位号(visionvslot 标记行携带)

手柄帧 gp 的字段与 Lua 手柄接口一一对应gp_bbuttonsgp_lt/gp_rtlt/rtgp_lx/gp_lylx/lygp_rx/gp_ryrx/ry

完整字段表见 doc/LUA_API.md 第 9 节


编译与构建说明

本项目采用标准的 PowerShell / Bat 一键构建脚本,支持依赖库自动还原与编译:

# 在项目根目录下执行
.\build.ps1
# 或直接双击运行 build.bat

构建脚本会自动将 Core/libs 中的 16 个必要运行时动态库(OpenCV / MinGW / ViGEmClient)同步还原至 bin/ 目录,并编译生成全部核心模块产物。

build.ps1 只生成最新产物,不清理任何历史文件(清理由人工决定)。

运行回归测试(可选)

Core/ 下的关键逻辑(识图对齐变换、样本命名、打包负载)带有一组回归测试。 测试二进制同样依赖 bin/ 里的 OpenCV DLL,必须先把 bin 加进 PATH,否则会报 0xc0000135

$env:PATH = "$PWD\bin;$env:PATH"
go test ./Core/...

其中涉及真实抓屏的用例在没有屏幕的环境会自动跳过。

基础模块版本(base.version)

Core/libs/base.version 定义基础模块(DLL 集合)的版本号。打包产物运行时会把 DLL 解压到:

%LOCALAPPDATA%\GoRobotScript\base\b_<版本号>_<DLL内容哈希>\

同一基线的所有包共享同一份(约 56 MB,不再逐包重复占用);升级 DLL 后把版本号改成 1.0.1 再构建,新旧基线因哈希不同而并行共存、互不冲突,旧包继续使用它自己的基线正常运行。超过 7 天未使用的缓存目录会被自动回收(可用环境变量 GOROBOT_CACHE_KEEP_DAYS 调整)。


依赖说明


开源协议与作者

  • License:MIT
  • Author:Suceru