前言
LuaTouch 是运行在 iOS 设备上的 Lua 自动化引擎。通过悬浮球菜单点「开始」后,引擎加载工作目录下的 scripts/main.lua 并执行;脚本中调用的 API 由引擎内置实现(应用控制、界面自动化、图色识别等)。
本文档覆盖 LuaTouch 的全部脚本 API,按模块划分:script / app / device / UI / 图色 …。所有 API 均为 全局函数(无模块前缀),在脚本任意位置直接调用即可,无需 require。
脚本约定
- 脚本入口固定为
scripts/main.lua,由悬浮球「开始」或 HTTP 远程启动触发;restartScript()可在脚本内重启自身 - 日志用
print()输出,写入引擎日志目录,可在 App 内或logs/目录查看 - 脚本运行在独立工作线程,涉及 UI 的操作引擎内部已自动回主线程执行,脚本层无需关心线程问题
- API 标注 已支持iOS 已支持 表示 iOS 端已实现;个别函数有平台限制的会在正文单独说明
- 脚本包(zip)内
res/、config/、libs/、scripts/四个目录的文件会被installScript()解压到工作目录对应位置
建议先通读 script 模块(脚本控制与启动模式),再按需查阅各功能模块。
用途声明:LuaTouch 仅供个人设备自动化学习研究使用。请遵守您所在地区的法律法规,勿将本工具用于任何违反法律法规或侵犯第三方权益的用途;因违规使用产生的一切责任由使用者自行承担。
下载项
官方安装包与配套工具,地址由管理端统一维护,点击链接即可下载。
加载中…
script 模块(脚本控制)
脚本自身的退出与重启控制(集合 scrcpy lua 全局变量实现:exitScript / restartScript)。
print(...) 已支持iOS 已支持
打印日志(输出到引擎/Lua 日志文件,用于调试)。
- 参数:任意多个值(字符串/数字/布尔等)
- 返回:无
print("hello", 123, true) -- 日志输出
wsLog(...) 已支持iOS 已支持
向中控推送日志({"type":"log"} 消息,中控 UI 实时可见);同时写入本地引擎日志。用法与 print 一致,多参数以 tab 分隔。需引擎 v1.10.1016+。
- 参数:任意多个值(字符串/数字/布尔等)
- 返回:
boolean—true=已推送到中控;false=中控未连接(不报错,仅写本地日志)
wsLog("开始找图", pageNum) -- 中控实时可见
local ok = wsLog("步骤1完成") -- ok=true 已推送 / false 中控未连接
sleep(ms) 已支持iOS 已支持
休眠指定毫秒数(暂停/停止脚本时立即响应,不等满时长)。
- 参数:
ms{int} 休眠毫秒数 - 返回:无
sleep(1000) -- 等 1 秒
LUA_CODE 已支持iOS 已支持
全局配置集合变量:主 App 启动引擎时把控制面板参数集合(pass 字段,key 用
Pass_ 前缀)作为 JSON 字符串传入,引擎开启脚本时写入全局 LUA_CODE。
- 类型:
string(JSON 字符串),全局只读,脚本内直接读取 - 内容:控制面板全部参数,key 为
Pass_前缀,与 NSUserDefaults 一致 - 来源:主 App 控制面板输入框(引擎模式/账号/密码/序号/设备编号/luacode)→ 重启引擎生效
- 取值:用
jsonToTable()解析成表后按 key 取字段
-- LUA_CODE 内容示例:
-- {"Pass_mode":"root","Pass_account":"test","Pass_password":"test",
-- "Pass_seq":"1","Pass_device":"cs1","Pass_luacode":""}
-- 解析成表取字段(用于账号/授权校验、设备绑定等)
local cfg = jsonToTable(LUA_CODE)
if cfg and cfg.Pass_account == "test" then
print("账号匹配: " .. cfg.Pass_account)
end
exitScript() 已支持iOS 已支持
终止当前脚本(不重启)。调用后脚本立即停止执行。
- 参数:无
- 返回:无
exitScript() -- 立即退出脚本
restartScript() 已支持iOS 已支持
终止当前脚本并重新执行同一脚本文件(从头开始)。
- 参数:无
- 返回:无
restartScript() -- 重启脚本
installScript(zipPath) 已支持iOS 已支持
解压脚本项目包到工作目录:zip 内的 res/、config/、libs/、scripts/ 四个目录
分别覆盖到工作目录对应位置(等同安装一个脚本包;zip-slip 防护)。
- 参数:
zipPath— 脚本包 zip 的完整路径(如/var/mobile/Media/com.luatouch.app/models/script.zip) - 返回:
boolean是否解压成功
local ok = installScript("/var/mobile/Media/com.luatouch.app/models/script.zip")
-- zip 内 res/ → 工作目录 res/
-- config/ → 工作目录 config/
-- libs/ → 工作目录 libs/
-- scripts/→ 工作目录 scripts/
if ok then print("install ok") end
loadEncryptedFile(path) 已支持iOS 已支持
读取并立即执行一个加密脚本文件(.luc):读文件 → 解密 → 以「文件路径」为名称加载该 chunk → 直接运行。脚本内容按全局作用域执行(不是 require 那种返回模块表的方式),调用方拿不到脚本的返回值。
path{string} 加密脚本(.luc)的完整路径- 返回:
boolean, errMsg— 执行成功返回true, nil;解密失败返回false, "decrypt failed (rc)";脚本内部运行时报错返回false, err(错误信息含脚本行号)
解密得到的明文字节在执行前会清零后释放(不在堆内存残留,防内存 dump 还原脚本)。
local ok, err = loadEncryptedFile("/var/mobile/Media/com.luatouch.app/scripts/main.luc")
if not ok then print("加载失败:", err) end
getVerifyUuid() 已支持iOS 已支持
读取客户端验证结果 uuid。引擎启动后后台线程按「上传 updateqfkjuuid → 校验 accessqfkjuuid」轮询(最多 5 轮),验证通过才写入该值。
- 参数:无
- 返回:
string验证 uuid;未验证通过时返回空字符串""(不是nil)
用法是「非空才放行业务」。注意:即使已成功过一次,引擎仍会周期性重新验证,重新验证期间该值可能短暂变空,脚本不要把它当成“一次通过就永久有效”。
-- 等验证通过再进入业务逻辑
while getVerifyUuid() == "" do
sleep(1000)
end
print("已授权:", getVerifyUuid())
executeShellCommand(cmd) iOS 不支持
执行系统 Shell 命令。iOS 未实现:函数保留(保证同一份脚本能在各平台直接调用,不会因函数不存在而报错),但内部是空实现——不执行任何命令,恒返回 nil,传入的参数被完全忽略。
cmd{string} 命令字符串(iOS 下被忽略)- 返回:
nil(恒为 nil)
local out = executeShellCommand("ls /") -- iOS 恒为 nil
getStartMode() 已支持iOS 已支持
获取当前启动模式(START_MODE,int),由各启动入口在启动时设置:悬浮球点「开始」= 0、HTTP 10010 远程启动 = 1(可扩展其他模式);restartScript 重跑不重置(保留当前值)。
- 参数:无
- 返回:
int当前 START_MODE 值(当前版本恒为0,预留字段)
local mode = getStartMode()
print("START_MODE =", mode)
Thread.newThread(callback) 已支持iOS 已支持
创建子线程并立即开始执行 callback(独立 pthread + 独立 lua_State,与主脚本
真并行)。返回 thread userdata 用于后续 stopThread() 控制。
- 参数:
callback{function} 子线程入口函数 - 返回:
threaduserdata;创建失败返回nil+ 错误消息
数据共享(globalSet / globalGet):每个线程(含主线程)的全局变量默认本线程私有 (标准 Lua 语义,线程之间互不可见)。跨线程共享走 C 层共享区,就两个公开 API:
globalSet("名", 值)—— 发布/覆盖:把值写进共享区。支持路径形式globalSet("cfg", "ip", "1.2.3.4")(= 共享区的cfg.ip,多层路径依次传分量); 值传nil= 删除该路径。表/函数都能发(表逐字段入库,函数以字节码跨线程加载,取回可直接调用)globalGet("名")/globalGet("cfg", "ip")—— 读取:返回共享区里的 深拷贝实体(表可正常 pairs / 索引 /#,每次读取是独立副本,改它不影响共享区); 不存在返回nil- 快照语义(最重要):发布是"拷一份进共享区"——发布之后本线程再改自己的变量,
共享区不会自动跟着变,要让别人看到必须重新
globalSet一次
重要约束:
- 回调不能依赖闭包 upvalue(子线程通过字节码加载回调,upvalue 不跨线程传递);
数据一律用
globalSet / globalGet传 - 子线程里先
globalGet取到本地再循环使用,要最新值时再取一次 (别在循环里反复取大表,每次都是深拷贝) - 子线程不响应主脚本的暂停/恢复;只有
stopThread()或主脚本结束/停止时强停
-- 主脚本:写全局变量,子线程直接读(无需传参)
config = { count = 0, stop = false }
-- 子线程:每 1 秒打印一次,读主脚本写的全局 config
function worker()
local i = 0
while true do
i = i + 1
print("worker tick " .. i .. " count=" .. tostring(config.count))
if config.stop then break end
sleep(1000)
end
print("worker exit")
end
local t = Thread.newThread(worker)
-- 主脚本继续干别的事...
sleep(5000)
config.stop = true -- 子线程能立即读到(C 层树形存储主/子线程互通)
thread:stopThread() 已支持iOS 已支持
强制终止该子线程(置独立停止标志,线程内 sleep 立即响应,hook 强停)。只影响该线程, 不影响主脚本与其他子线程。主脚本结束/停止时引擎会自动强停所有子线程,无需手动清理。
- 参数:无(
thread为Thread.newThread的返回值) - 返回:无
local t = Thread.newThread(worker)
sleep(3000)
t:stopThread() -- 单独停掉 worker,主脚本继续跑
app 模块
appCurrentPackage() 已支持iOS 已支持
获取当前前台应用的 Bundle Identifier。
- 参数:无
- 返回:
string前台应用的 Bundle ID;无前台应用时返回nil
-- 获取当前前台 App 的包名
local bid = appCurrentPackage()
print(bid) -- 例如 "com.apple.mobilesafari"
appLaunch(packageName) 已支持iOS 已支持
通过 Bundle Identifier 启动应用。
packageName{string} 应用 Bundle ID- 返回:
bool是否启动成功
local ok = appLaunch("com.apple.mobilesafari")
if ok then print("已启动") end
appForceStop(packageName) 已支持iOS 已支持
关闭指定应用(系统级终止进程)。
packageName{string} 应用 Bundle ID- 返回:
bool是否成功终止
appForceStop("com.example.app")
appGetList(includeSystemApps) 已支持iOS 已支持
获取已安装应用列表。
includeSystemApps{bool} 是否包含系统应用- 返回:
table应用信息数组,每个元素是下表结构
| 字段名 | 类型 | 说明 |
|---|---|---|
PackageName | string | 应用的 Bundle Identifier |
AppName | string | 应用显示名称 |
VersionName | string | 版本号(CFBundleShortVersionString) |
VersionCode | string | 构建号(CFBundleVersion) |
IsSystemApp | bool | 是否为系统应用 |
local list = appGetList(true)
for _, a in ipairs(list) do
print(a.PackageName, a.AppName, a.VersionName)
end
appGetBundlePath(packageName) 已支持iOS 已支持
获取应用安装包(.app)路径。
packageName{string} 应用 Bundle ID- 返回:
string路径;未找到返回nil
local path = appGetBundlePath("com.auto.go")
appGetDataPath(packageName) 已支持iOS 已支持
获取应用数据目录路径。
packageName{string} 应用 Bundle ID- 返回:
string数据目录路径;未找到返回nil
local data = appGetDataPath("com.auto.go")
appSelfPackage() 已支持iOS 已支持
获取当前脚本/宿主自身的包名。
- 参数:无
- 返回:
string当前 Bundle ID;获取失败返回nil
local pkg = appSelfPackage()
appGetVersion(packageName) 已支持iOS 已支持
通过 Bundle ID 获取应用版本号(CFBundleShortVersionString)。
packageName{string} 应用 Bundle ID- 返回:
string版本号;未找到返回nil
local ver = appGetVersion("com.tencent.xin")
appUninstall(packageName) 已支持iOS 已支持
卸载应用。
packageName{string} 应用 Bundle ID- 返回:
bool是否成功
appUninstall("com.example.app")
appInstall(path) 已支持iOS 已支持
后台静默安装 IPA 文件。
path{string} IPA 文件路径- 返回:
bool是否成功
appInstall("/var/mobile/Documents/app.ipa")
appClear(packageName) 已支持iOS 已支持
清除应用数据(删除数据容器内容)。
packageName{string} 应用 Bundle ID- 返回:
bool是否成功
appClear("com.example.app")
appOpenUrl(url) 已支持iOS 已支持
使用系统默认方式打开 URL。若未带 http:// 或 https:// 前缀,会自动补上 http://。
url{string} 网址或 URL Scheme- 返回:
bool是否成功
appOpenUrl("https://example.com")
appGetPackageName() 已支持iOS 已支持
获取宿主 App(LuaTouch)包名。与 appSelfPackage() 的区别:
appSelfPackage() 返回引擎宿主(com.luatouch.core),本函数返回主 App(com.luatouch.app)。
- 参数:无
- 返回:
string主 App 包名(com.luatouch.app)
print(appGetPackageName()) -- com.luatouch.app
print(appSelfPackage()) -- com.luatouch.core
device 模块
deviceGet() 已支持iOS 已支持
一次获取设备信息(本引擎便利封装)。只含「变量 + 下方函数拿不到」的信息;内存用 deviceGetTotalMem、屏幕用 deviceGetDisplayInfo。
- 参数:无
- 返回:
table,字段见下表(取不到时字段缺失)
| 字段 | 类型 | 说明 |
|---|---|---|
Model | string | 设备机型标识(如 iPhone12,1) |
Release | string | 系统版本(如 "14.6") |
Serial | string | 设备序列号 |
Build | string | 系统构建号(如 18F72) |
SystemName | string | 系统名称(如 "iOS") |
DeviceName | string | 用户设置的设备名 |
Arch | string | CPU 架构(如 arm64) |
Is64Bit | bool | 是否 64 位 |
CpuCount | int | CPU 核数 |
local d = deviceGet()
print(d.Model, d.Release, d.Serial)
print(d.Arch, d.Is64Bit, d.CpuCount)
deviceGetDisplayInfo() 已支持iOS 已支持
获取主屏幕的分辨率信息(多返回值)。
- 参数:无
- 返回:
width{int} 逻辑宽度,height{int} 逻辑高度,scale{number} 缩放因子,rotation{int} 旋转角度(0/90/180/270)
local w, h, scale, rot = deviceGetDisplayInfo()
print(w, h, scale, rot) -- 例如 390 844 2.0 0
deviceVpnStatus() 已支持iOS 已支持
获取 VPN 状态(存在 utun 网络接口即视为开启)。
- 参数:无
- 返回:
bool
if deviceVpnStatus() then
print("VPN 已开启")
end
deviceSetVPNEnabled(enabled) iOS 不支持
设置 VPN 开关状态。iOS 平台限制,恒返回 false。
enabled{bool} 是否打开- 返回:
false
deviceSetVPNEnabled(true) -- 恒 false
deviceSetWiFiEnabled(enabled) iOS 不支持
设置 WiFi 开关状态。iOS 平台限制,恒返回 false。
enabled{bool} 是否打开- 返回:
false
deviceSetWiFiEnabled(true) -- 恒 false
deviceGetBattery() 已支持iOS 已支持
获取当前电量百分比。
- 参数:无
- 返回:
int0~100;读取失败返回-1
local level = deviceGetBattery()
print("电量 " .. level .. "%")
deviceGetBatteryStatus() 已支持iOS 已支持
获取电池状态。
- 参数:无
- 返回:
int0未知,1未充电,2充电中,3已充满
local st = deviceGetBatteryStatus()
if st == 2 then print("充电中") end
deviceIsScreenOn() 已支持iOS 已支持
判断屏幕是否点亮(未锁屏即视为点亮)。
- 参数:无
- 返回:
bool
if deviceIsScreenOn() then print("屏幕亮着") end
deviceIsScreenUnlock() 已支持iOS 已支持
判断屏幕是否已解锁(未处于锁屏状态)。
- 参数:无
- 返回:
bool
if deviceIsScreenUnlock() then print("已解锁") end
deviceGetBrightness() 已支持iOS 已支持
获取当前屏幕亮度。
- 参数:无
- 返回:
int0~255
local b = deviceGetBrightness()
print("亮度 " .. b)
deviceGetTotalMem() 已支持iOS 已支持
获取设备总内存。
- 参数:无
- 返回:
int总内存,单位 KB;失败返回0
print(deviceGetTotalMem() .. " KB")
deviceGetAvailMem() 已支持iOS 已支持
获取设备当前可用内存(free + inactive,host_statistics64)。
- 参数:无
- 返回:
int单位 KB;失败返回0
print(deviceGetAvailMem() .. " KB")
deviceWakeUp() 已支持iOS 已支持
唤醒设备(点亮屏幕)。
- 参数:无
- 返回:
bool是否成功调用
deviceWakeUp()
deviceKeepScreenOn() 已支持iOS 已支持
保持屏幕常亮(关闭自动锁屏)。
- 参数:无
- 返回:
bool是否成功调用
deviceKeepScreenOn()
deviceGetIp() 已支持iOS 已支持
获取设备局域网 IPv4 地址(优先私有网段,跳过自分配地址)。
- 参数:无
- 返回:
string如"192.168.31.169";取不到返回nil
local ip = deviceGetIp()
print(ip)
deviceGetWifiMac() 已支持iOS 已支持
获取 Wi‑Fi 网卡 MAC 地址(en0,回退 en1)。
- 参数:无
- 返回:
string如"aa:bb:cc:dd:ee:ff";取不到返回nil
local mac = deviceGetWifiMac()
print(mac)
deviceReboot() 已支持iOS 已支持
重启设备(root 直接 reboot(RB_AUTOBOOT),调用后设备立即重启)。
- 参数:无
- 返回:
bool(正常不会返回)
-- 慎用:立即重启设备
deviceReboot()
getPlatform() 已支持iOS 已支持
获取运行平台标识。iOS 版恒返回 "ios"(跨平台脚本兼容用,安卓版返回 "android")。
- 参数:无
- 返回:
string平台标识,iOS 上恒为"ios"
if getPlatform() == "ios" then
print("当前运行在 iOS")
end
file 模块
deleteFile(path) 已支持iOS 已支持
删除文件或空目录(目录用 rmdir,文件用 remove)。
path{string} 文件或空目录路径- 返回:
bool是否删除成功
deleteFile("/var/mobile/Documents/tmp.txt")
moveFile(src, dst) 已支持iOS 已支持
移动或重命名文件(rename)。
src{string} 源路径dst{string} 目标路径- 返回:
bool是否成功
moveFile("/tmp/a.txt", "/var/mobile/Documents/a.txt")
unzipFile(zip, dest) 已支持iOS 已支持
解压 zip 文件到目标目录。
zip{string} zip 文件路径dest{string} 解压目标目录(不存在会自动创建)- 返回:
bool是否成功
unzipFile("/var/mobile/Documents/app.zip", "/var/mobile/Documents/extract")
readFileAsBase64(path) 已支持iOS 已支持
读取文件内容并编码为 Base64(双返回值)。
path{string} 文件路径- 返回:
stringBase64 内容;失败返回nil+错误信息
local b64, err = readFileAsBase64("/tmp/img.png")
if b64 then
print("base64 length", #b64)
end
base64Encode(str) 已支持iOS 已支持
将字符串编码为 Base64(标准字母表 A-Z a-z 0-9 + /,= 填充)。
str{string} 原始内容- 返回:
stringBase64 编码结果
local b64 = base64Encode("hello")
print(b64) -- aGVsbG8=
base64Decode(str) 已支持iOS 已支持
将 Base64 字符串解码为原始内容(自动忽略换行/空白/= 填充,容错宽松)。
str{string} Base64 内容- 返回:
string解码结果
local raw = base64Decode("aGVsbG8=")
print(raw) -- hello
http 模块
httpGet(url, timeout?) 已支持iOS 已支持
HTTP GET 请求。支持 HTTP/HTTPS,默认超时 30 秒。
url{string} 完整 URL(如http://192.168.1.1:8080/api)timeout{number} 可选,超时秒数,默认 30- 返回:
intHTTP 状态码(200/404…,负值为错误码见下表)+string响应体
local code, body = httpGet("http://example.com/api")
print(code, body)
httpPost(url, data, contentType?, timeout?) 已支持iOS 已支持
HTTP POST 请求。支持 HTTP/HTTPS,默认超时 30 秒。
url{string} 完整 URLdata{string} 请求体(POST 数据)contentType{string} 可选,Content-Type;默认application/x-www-form-urlencodedtimeout{number} 可选,超时秒数,默认 30- 返回:
int状态码 +string响应体
local code, body = httpPost("http://example.com/api",
"name=test&id=1", "application/x-www-form-urlencoded")
downloadFile(url, path, timeout?) 已支持iOS 已支持
HTTP 下载文件到本地(流式写盘,父目录自动创建)。支持 HTTP/HTTPS,默认超时 60 秒。
url{string} 完整 URLpath{string} 保存路径(绝对路径)timeout{number} 可选,超时秒数,默认 60- 返回:
boolean是否成功 +string错误信息(成功为空串)
local ok, err = downloadFile("http://example.com/file.zip",
"/var/mobile/Media/com.luatouch.app/downloads/file.zip")
tableToJson(table) 已支持iOS 已支持
Lua 表序列化为 JSON 字符串。数组(连续数字键)输出数组,否则输出对象。
table{table} 要序列化的表- 返回:
stringJSON 字符串;参数非表返回错误串
local json = tableToJson({ name = "test", id = 1 })
print(json) -- {"id":1,"name":"test"}
jsonToTable(str) 已支持iOS 已支持
JSON 字符串解析为 Lua 表。
str{string} JSON 字符串- 返回:
table解析结果;解析失败返回nil
local t = jsonToTable('{"name":"test","id":1}')
print(t.name, t.id)
HTTP 错误码 说明
| 码 | 含义 |
|---|---|
| -1 | 参数错误(url 为空 / POST 无数据) |
| -2 | URL 解析失败 |
| -4 | socket 创建失败 |
| -5 | DNS 解析失败 |
| -6 | 连接失败 |
| -7 | 发送失败 |
| -8 | 无响应(超过 timeout 秒) |
ime 模块
imeInputText(text) 已支持iOS 已支持
向当前焦点控件输入文本(支持中文、英文、数字)。
text{string} 要输入的文本(支持中文/英文/数字)- 返回:
bool是否注入成功
imeInputText("Hello, World!")
imeInputText("你好世界")
imePasteText() 已支持iOS 已支持
模拟 Command+V,把剪贴板内容粘贴到当前焦点。
- 参数:无
- 返回:
bool是否注入成功
imeSetClipText("要粘贴的内容")
imePasteText()
imeGetClipText() 已支持iOS 已支持
获取剪贴板文本内容。
- 参数:无
- 返回:
string剪贴板文本;失败返回nil
local text = imeGetClipText()
print(text)
imeSetClipText(text) 已支持iOS 已支持
设置剪贴板文本内容。
text{string} 要写入剪贴板的文本- 返回:
bool是否设置成功
imeSetClipText("要复制的内容")
touch 模块
点击、滑动与按键注入。坐标均为物理像素,与 getScreenSize() 一致(本机 1242×2208,全屏中心 621, 1104)。findColor 返回的坐标可直接用于 tap,无需换算。
tap / swipe / touchDown / touchMove 支持可选触摸参数:tip 指尖压力(0~1,默认 1)、barrel 笔身压力(0~1,默认 0)、radius 触摸半径(默认 0.04)。
这些函数都是阻塞调用:注入在引擎的串行队列上执行,函数返回时注入已经完成。
tap(x, y, tip?, barrel?, radius?) 已支持iOS 已支持
在指定位置点击一下。
x{number} 横坐标(物理像素,0~屏幕宽)y{number} 纵坐标(物理像素,0~屏幕高)tip{number} 可选,指尖压力 0~1,默认 1barrel{number} 可选,笔身压力 0~1,默认 0radius{number} 可选,触摸半径,默认 0.04- 返回:无
tap(621, 1104) -- 点击屏幕中心(1242x2208)
swipe(x1, y1, x2, y2, ms?, tip?, barrel?, radius?) 已支持iOS 已支持
从起点滑动到终点(20 步插值)。
x1, y1{number} 起点坐标x2, y2{number} 终点坐标ms{int} 可选,滑动时长毫秒,默认 300(最小 50)tip{number} 可选,指尖压力 0~1,默认 1barrel{number} 可选,笔身压力 0~1,默认 0radius{number} 可选,触摸半径,默认 0.04- 返回:无
swipe(621, 1600, 621, 400, 500) -- 从下往上滑
key(name) 已支持iOS 已支持
按命名按键(按下 → 保持约 40ms → 自动抬起)。
name{string} 键名,仅支持下列 6 个:home/home2/lock(锁屏)/volup/voldown/mute- 返回:
bool是否支持该键名(true=已注入;false=键名未知,不注入任何按键)
| 键名 | 对应 HID | 说明 |
|---|---|---|
home | page 0x0C, usage 0x40 | 回到主屏(Consumer Home/Menu) |
home2 | page 0x0C, usage 0x0223 | 回主屏的另一种实现(Consumer AC Home),部分场景比 home 更有效 |
lock | page 0x0C, usage 0x30 | 锁屏 / 电源键 |
volup | page 0x0C, usage 0xE9 | 音量 + |
voldown | page 0x0C, usage 0xEA | 音量 − |
mute | page 0x0C, usage 0xE2 | 静音开关 |
key("home") -- 回到主屏
key("volup") -- 音量+
key("lock") -- 锁屏
if not key("back") then print("key 不认识 back,需要用 keyRaw") end
keyRaw(page, usage) 已支持iOS 已支持
按 HID 原始键码(按下 → 保持约 40ms → 自动抬起),用于 key() 未内置的按键。
page{int} HID 用法页(如0x0C消费类、0x07键盘)usage{int} 用法码- 返回:无
keyRaw(0x0C, 0x40) -- Consumer Home
keyMeta(name, repeat?, meta?) 已支持iOS 已支持
带修饰键的按键(键名范围同 key();修饰键只支持 Shift)。
name{string} 键名(同key)repeat{int} 可选,重复次数,默认 1meta{int} 可选,修饰键位掩码:0x1= Shift,默认 0- 返回:无
keyMeta("home", 2) -- 按 home 两次
keyMeta("home", 1, 0x1) -- Shift + home
touchDown(idx, x, y, tip?, barrel?, radius?) 已支持iOS 已支持
多指触摸:第 idx 根手指按下。手指索引范围 0~9,按下 / 移动 / 抬起必须用同一个 idx 成对出现(自己保证配对,不做手势识别)。
idx{int} 手指索引(0~9)x, y{number} 按下位置tip{number} 可选,指尖压力 0~1,默认 1barrel{number} 可选,笔身压力 0~1,默认 0radius{number} 可选,触摸半径,默认 0.04- 返回:无
touchDown(0, 100, 300) -- 第一根手指按下
touchMove(idx, x, y, tip?, barrel?, radius?) 已支持iOS 已支持
多指触摸:移动第 idx 根手指到新位置。按规范应先 touchDown 同一 idx;idx 超出 0~9 会被忽略。
idx{int} 手指索引(0~9)x, y{number} 新位置tip{number} 可选,指尖压力 0~1,默认 1barrel{number} 可选,笔身压力 0~1,默认 0radius{number} 可选,触摸半径,默认 0.04- 返回:无
touchDown(0, 200, 400)
touchMove(0, 400, 400) -- 保持按住并拖动
touchUp(0)
touchUp(idx) 已支持iOS 已支持
多指触摸:抬起第 idx 根手指(必须与对应 touchDown 的 idx 一致,抬起位置用的是按下时记录的坐标)。若该 idx 当前并未按下,则不发送任何事件、静默返回;idx 超出 0~9 同样被忽略。
idx{int} 手指索引(0~9)- 返回:无
touchUp(0) -- 第一根手指抬起
color 模块
颜色格式 已支持iOS 已支持
颜色统一为 6 位十六进制 RGB,格式 目标-容差(分隔符 - 或 | 均可):
目标{string} 6 位目标颜色,如ffffff容差{string} 6 位容差值(R/G/B 各一字节),如323232表示每通道允许 ±0x32
findColor(0, 0, 1242, 2208, "ffffff-323232", 0, 0, 0)
-- 多点偏移组:x|y|目标-容差
detectsMultiColors("620|389|ffffff-323232,100|200|000000-101010")
getPixel(x, y) 已支持iOS 已支持
获取指定像素点的 RGB 分量(多返回值)。
x, y{int} 屏幕像素坐标- 返回:
r, g, b(0~255);越界返回nil
local r, g, b = getPixel(100, 200)
print(r, g, b)
getRGB(x, y) 已支持iOS 已支持
获取指定像素点的打包颜色值。
x, y{int} 屏幕像素坐标- 返回:
int颜色0xAARRGGBB;失败返回0
local c = getRGB(100, 200)
print(string.format("%08X", c))
方向与坐标系(横竖屏都支持) 已支持iOS 已支持
iOS 版横屏、竖屏都支持,图色与触摸类 API 的坐标统一使用所见坐标系——即你眼睛看到的画面方向:竖屏时 w < h,横屏时 w > h,不需要自己做任何旋转换算。
- 图色(findColor / findMultiColors / isColor / getRGB / getPixel / findPic…)坐标 = 所见坐标
- 触摸(tap / swipe / touchDown…)坐标 = 同一套所见坐标,引擎内部自动处理旋转
getScreenSize()返回所见方向的宽高(横屏打开返回2208, 1242这样的横版值)- 节点
bounds()例外:返回逻辑点坐标,tap 前需乘屏幕倍数 scale(见「点击节点」节)
getScreenSize() 已支持iOS 已支持
获取屏幕尺寸(多返回值)。返回所见方向的尺寸:竖屏 w < h、横屏 w > h,可直接用于图色/触摸坐标。
- 参数:无
- 返回:
w, h(像素,所见方向);无屏幕缓存返回nil
local w, h = getScreenSize()
print(w, h) -- 竖屏例: 1242 2208;横屏例: 2208 1242
getScreenOrientation() 已支持iOS 已支持
获取当前屏幕方向码(多返回值为 1 个 int)。
- 参数:无
- 返回:
1/2/3/4(1=竖屏 2=倒竖屏 3=横屏左 4=横屏右)
local o = getScreenOrientation()
if o == 3 or o == 4 then
print("当前横屏")
end
findColor(x1, y1, x2, y2, color, d1?, d2?, d3?) 已支持iOS 已支持
在矩形区域内查找第一个匹配颜色的点(8 方向扫描)。
x1, y1, x2, y2{int} 搜索区域(自动归一化)color{string} 目标-容差,如ffffff-323232d1, d2, d3{int} 可选,扫描方向(0/1),决定起扫边与主轴;全 0 = 左上→右下- 返回:
x, y命中坐标;未找到返回-1, -1
local x, y = findColor(0, 0, 1242, 2208, "ff0000-101010")
if x >= 0 then
print("found at", x, y)
end
isColor(x, y, color) 已支持iOS 已支持
判定指定像素点颜色是否匹配(单点快速判定:直读渲染源目标像素,不建目标面、不整帧拷贝;keepCapture 冻结期 / 50ms 去重窗口内直接读缓存帧零成本返回)。
x, y{int} 屏幕像素坐标color{string}RRGGBB或RRGGBB-容差;纯 6 位串自动按容差000000处理- 返回:
booleantrue命中 /false不命中
if isColor(102, 86, "D33422-101010") then
print("是目标颜色")
end
findMultiColors(x1, y1, x2, y2, firstColor, offsetColors, d1?, d2?, d3?) 已支持iOS 已支持
首点 + 相对偏移多点匹配(可精确识别图标/按钮)。
x1, y1, x2, y2{int} 搜索区域firstColor{string} 首点颜色(目标-容差)offsetColors{string} 偏移点组,x|y|目标-容差逗号分隔(偏移相对首点)d1, d2, d3{int} 可选,扫描方向- 返回:
x, y首点命中坐标;未找到返回-1, -1
local x, y = findMultiColors(0, 0, 1242, 2208,
"ffffff-323232",
"620|391|ffffff-323232,100|200|000000-101010")
keepCapture() / releaseCapture() 已支持iOS 已支持
图色渲染帧冻结控制:图色函数读取的是后台持续更新的缓存帧。
keepCapture():冻结当前画面,期间所有图色函数查的是同一帧releaseCapture():恢复实时画面- 参数:无;返回:无
- 用途:画面静止时连续多次图色查找(循环找点)保证同一画面
keepCapture() -- 冻结当前帧
for i = 1, 10 do
local x, y = findMultiColors(0, 0, 0, 0, "359bed-101010",
"45|76|ffffff-101010|93|114|ffffff-101010|33|146|ffffff-101010|-9|432|359bed-101010|58|447|359bed-101010",
0, 0.9)
if x ~= -1 and y ~= -1 then print(x, y) end
end
releaseCapture() -- 恢复实时画面
⚠️ 注意:每次开启脚本(悬浮球「开始」)会自动恢复,不会因上次脚本忘记释放而永久冻结;但脚本内 keep 后建议主动 release。
tableFindMultiColors(x1, y1, x2, y2, firstColor, offsetColors, d1?, d2?, d3?) 已支持iOS 已支持
与 findMultiColors 同参数,但返回区域内所有匹配点的集合(而非第一个点)。
x1, y1, x2, y2{int} 搜索区域firstColor{string} 首点颜色(目标-容差)offsetColors{string} 偏移点组,x|y|目标-容差逗号分隔(偏移相对首点)d1, d2, d3{int} 可选,扫描方向- 返回:
table所有匹配点集合{{x=,y=},...};无匹配返回空表{}
local pts = tableFindMultiColors(0, 0, 1242, 2208,
"ffffff-323232",
"620|391|ffffff-323232,100|200|000000-101010")
for i, p in ipairs(pts) do
print(i, p.x, p.y)
end
getColorCountInRegion(x1, y1, x2, y2, colorStr, sim?) 已支持iOS 已支持
统计区域内符合颜色条件的像素数量。
x1, y1{int} 区域左上角坐标x2, y2{int} 区域右下角坐标;为0时取屏幕最大宽/高colorStr{string} 颜色字符串,多颜色用|分隔,每组RRGGBB或RRGGBB-容差,如FFFFFF|CCCCCC-101010sim{number} 可选,相似度 0.1~1.0(默认 0.9);未带容差的颜色用它换算容差(1-sim)*255- 返回:
int匹配像素总数(任一颜色命中即计 1)
local count = getColorCountInRegion(0, 0, 0, 0, "FFFFFF", 0.9)
print(count)
detectsMultiColors(colors) 已支持iOS 已支持
多点同屏判定:给定一组绝对坐标点,判断是否全部匹配(常用于区域校验)。
colors{string}x|y|目标-容差逗号分隔;首点可带第 4 字段extra指定偏移搜索范围(默认 0)- 返回:
bool全部匹配返回 1,否则 0
local ok = detectsMultiColors("620|389|ffffff-323232,100|200|000000-101010")
if ok == 1 then
print("all colors match")
end
findPic(x1, y1, x2, y2, files, deltaColor?, sim?, dir?) 已支持 iOS 已支持
区域找图(模板匹配):在屏幕区域内查找模板图片,返回其左上角坐标。支持透明图/异形图(模板四角同色且占比 ≥30% 时该色判为背景,匹配时只比前景像素——兼容大漠涂抹异形图用法与 PNG 真透明)、每通道偏色、多模板(| 分隔,返回最先命中的)。
x1, y1, x2, y2{int} 搜索区域;x2/y2为0时取整屏,坐标写反自动交换files{string} 模板图片路径,多图用|分隔(如a.png|b.png);相对路径以脚本工作目录res/为基准;支持 png/jpgdeltaColor{string} 可选,偏色RRGGBB(如"101010"),匹配时每通道允许 ±该值;"000000"= 无偏色sim{number} 可选,相似度 0.1~1.0(默认 0.9),1.0 = 逐像素完全一致dir{int} 可选,扫描方向:0 从左上(默认)/ 1 从右上 / 2 从左下 / 3 从右下- 返回:
x, y命中模板的左上角坐标;未找到返回-1, -1
模板图可用「图色助手 → 裁剪画笔涂抹」制作:涂抹掉不要的部分(涂抹色需一致且占比 ≥30%、四角为涂抹色,工具会自动补涂)。
-- 全屏找 btn.png,相似度 0.9,无偏色
local x, y = findPic(0, 0, 0, 0, "btn.png", "000000", 0.9, 0)
if x ~= -1 then
tap(x + 20, y + 10) -- 点模板内相对位置
end
-- 多模板:返回最先命中的那个(透明图/异形图直接可用)
x, y = findPic(0, 0, 0, 0, "btn2.jpg|btn3.png", "000000", 0.9, 0)
print(x, y)
findPicEx(x1, y1, x2, y2, files, deltaColor?, sim?, dir?) 已支持 iOS 已支持
区域找图(多图全部命中):与 findPic 同参数,但返回区域内所有模板命中结果的集合(而非第一个),结果按扫描方向排序,上限 1800 个。
- 参数与
findPic完全一致 - 返回:
table每项{index=模板序号, name=文件名, x=, y=};无匹配返回空表{}
local t = findPicEx(0, 0, 0, 0, "a.png|b.png", "000000", 0.9, 0)
for i, v in ipairs(t) do
print(i, v.name, v.x, v.y)
end
-- 无匹配时 t 为空表
screenshot(path) 已支持iOS 已支持
截取全屏并保存为 PNG 文件。
path{string} 保存路径- 返回:
bool是否保存成功
screenshot("/var/mobile/Documents/screen.png")
cv 模块(对象式 OpenCV,206 个函数)
用法与 Mat 对象 已支持 iOS 已支持
cv 模块为对象式 API:cv.snapshot() 等函数返回 Mat 对象,处理函数一律「Mat 进、Mat 出」。旧的 12 个 cv_xxx 全局函数已废弃,由本模块取代。
- 双写法:每个成员同时注册全小写与首字母大写两个名字(
cv.snapshot=cv.Snapshot),任选其一。 - Mat 对象方法(6 个):
mat:release()主动释放 /mat:empty()是否为空 /mat:size()尺寸 /mat:type()类型 /mat:clone()深拷贝 /mat:origin()截屏原点偏移。空 Mat 一律以nil表示。 - 常量传参:OpenCV 常量可直接传名字字符串(如
"TM_SQDIFF_NORMED"、"CV_32F")或数字值。 - 点集参数:所有接受点集的函数(arclength / contourarea / approxpolydp / convexhull / perspectivetransform 等)同时支持字段式
{{x=10,y=20},...}与数组式{{10,20},...},也可混用 Point 指针;引擎版本 1.0.5 起两端一致。 - 字节序:屏幕帧是 RGBA;要
imwrite/ base64 输出图片前先cv.tobgr(mat),否则红蓝颠倒。 - 找纯色块:
matchtemplate/findimage默认TM_CCOEFF_NORMED,纯色无纹理模板必然失效,须显式传"TM_SQDIFF_NORMED"。 - 释放:Mat 走 GC 自动回收,高频循环建议手动
mat:release();OCR 实例(ocr/ppocr)按需创建且不自动释放,用完记得ocr_close/ppocr_release。 - 所有函数的坐标均为 Mat 像素坐标;带 origin 的 Mat(roi/crop 产物)findimage 等返回绝对坐标。
创建与生命周期 已支持 iOS 已支持
snapshot([x1,y1,x2,y2])— 截当前屏幕帧为 Mat(无参全屏,有参区域)new(w,h[,ch[,fill]])— 新建空 Mat(CV_8UC1~4,可指定填充值)eye(n)/ones(rows,cols)/zeros(rows,cols)— 单位阵 / 全 1 / 全 0clone(m)— 深拷贝(新内存)copyto(src[,dst[,mask]])— 拷贝(可带掩码)setto(m,value[,mask])— 区域赋值convertto(m,type[,alpha[,beta]])— 元素类型转换(可缩放)reshape(m,ch,rows)— 改变形状roi(m,x,y,w,h)/crop(m,x,y,w,h)— 截取子区域(新 Mat,origin 累计偏移)release(m)— 主动释放(裸指针的释放见下方「指针与辅助类型」)
属性与像素读写 已支持 iOS 已支持
empty(m)/getsize(m)/gettype(m)/getorigin(m)— 空 / 尺寸 / 类型 / 截屏原点getpixel(m,x,y)/setpixel(m,x,y,value)— 单像素读写(value 为 {b,g,r} 表)seqcount(m)— 序列元素个数(点集/结果序列长度)gettextsize(text,font,scale,thickness)— 计算文字绘制尺寸
指针与辅助类型 已支持 iOS 已支持
分配/读写原生标量与 Point 指针(供需要输出参数的接口使用)。指针必须用 cv.deletePtr 释放——忘记释放由 GC 兜底不泄漏,但释放后再读写会明确报错;类型严格校验(int 指针传给 getDouble 会报错)。拼写为全小写/首字母大写双名(cv.newpoint = cv.NewPoint)。
newInt(v)/getInt(p)/setInt(p,v)— 整型指针分配 / 读 / 写newDouble / getDouble / setDouble、newFloat / getFloat / setFloat、newLong / getLong / setLong、newByte / getByte / setByte— 与 Int 完全同构(双精度 / 单精度 / 长整 / 字节指针)newPoint(x,y)/getPoint(p)/setPoint(p,x,y)— Point 指针;getPoint返回{x=,y=}表newPoint2f(x,y)/getPoint2f(p)/setPoint2f(p,x,y)— 浮点坐标 Point(返回同样为 {x=,y=} 表)deletePtr(p)— 释放指针;ptrtype(p)— 返回类型名与是否已释放(调试用)
local p = cv.newInt(5)
print(cv.getInt(p)) -- 5
cv.setInt(p, 8)
print(cv.getInt(p)) -- 8
cv.deletePtr(p)
local pt = cv.newPoint(100, 200)
local pos = cv.getPoint(pt) -- {x=100, y=200}
cv.setPoint(pt, 500, 500)
print(cv.ptrtype(pt)) -- point false(未释放)
cv.deletePtr(pt)
算术与数学 已支持 iOS 已支持
add / subtract / multiply / divide— 四则(带饱和/缩放语义)addweighted(m1,a,m2,b,gamma)— 加权和;scaleadd(m1,a,m2)— 缩放加absdiff(m1,m2)— 绝对差;min/max(m1,m2)— 逐元素最值;compare(m1,m2,op)— 比较bitwiseand / bitwisenot / bitwiseor / bitwisexor— 位运算(可带掩码)sqrt / pow / exp / log— 逐元素数学convertscaleabs(m[,alpha[,beta]])— 缩放+偏移后取绝对值,输出 CV_8U(增强对比/边缘可视化的常用前处理)gemm(a,b,alpha,c,beta)— 广义矩阵乘(注意第 5 参才是 beta,第 4 参可省略矩阵)invert(m[,method])/determinant(m)/trace(m)— 求逆 / 行列式 / 迹solve(a,b[,method])/solvecubic / solvepoly / solvey— 线性方程组 / 多项式求根setidentity(m[,s])— 原地置单位阵sort(m[,flags])/sortidx(m[,flags])— 排序 / 排序索引magnitude(x,y)/carttopolar / polartocart— 幅值与极坐标互换;phasecorrelate— 相位相关
统计 已支持 iOS 已支持
mean(m[,mask])/meanstddev(m[,mask])— 均值 / 均值与标准差sumelems(m)— 元素求和;countnonzero(m)— 非零个数minmaxidx(m[,mask])— 最小最大值与位置(注意 idx[0]=行 y、idx[1]=列 x,不是线性下标)minmaxloc(m[,mask])— 最小最大值与位置findnonzero(m)— 非零元素坐标表(N×2 CV_32S)norm(m[,normType[,mask]])/normalize(m[,alpha,beta,normType])— 范数 / 归一化psnr(a,b)— 峰值信噪比;calchist/comparehist— 直方图统计与比对kmeans(data,K,...)— K 均值聚类;reduce / reduceargmax / reduceargmin— 按行/列压缩与最值位置integral(m)— 积分图;batchdistance(a,b[,dtype,normType,K])— 点集距离矩阵(注意硬断言:K=0 必须 noArray,CV_8U 只能配 CV_32S)checkrange(m[,min,max])— 值域检查(返回 bool + 首个越界位置)
滤波与增强 已支持 iOS 已支持
blur / gaussianblur / medianblur / bilateralfilter / boxfilter— 常规滤波filter2d / sepfilter2d(m,kernel,...)— 自定义(可分离)卷积sobel / scharr / laplacian / spatialgradient— 梯度与边缘算子sharpen(m[,strength])— USM 锐化;clahe(m[,clipLimit])— 限对比度自适应直方图均衡equalizehist(m)— 全局直方图均衡;lut(m,table)— 查表变换denoise([h,hColor,templateWindowSize,searchWindowSize])— 非局部均值去噪detailenhance([sigma_s,sigma_r])— 细节增强(只支持 8 位 3 通道)inpaint(m,mask,radius,method)— 修复(去水印/遮挡)colordeviation(m,"RRGGBB-RRGGBB")— 颜色偏差二值化(目标色-容差)getgaussiankernel / createhanningwindow— 生成核 / 窗函数
形态学 已支持 iOS 已支持
erode / dilate(m,kernel[,iter])— 腐蚀 / 膨胀morphologyex(m,op,kernel[,iter])— 开闭/梯度/顶帽/黑帽getstructuringelement(shape,ksize)— 生成结构元
几何变换 已支持 iOS 已支持
resize(m,w,h[,interp])— 缩放;rotate(m,code)/rot90(m,k)/flip(m,code)— 旋转 / 翻转transpose(m)— 转置warpaffine(m,M,dsize)/warpperspective(m,M,dsize)— 仿射 / 透视变换getaffinetransform / invertaffinetransform / getperspectivetransform— 变换矩阵求解perspectivetransform(pts, m[,ox,oy])— 点集透视/仿射变换(pts 点集支持字段式/数组式(见上方「点集参数」),m 为 3x3 矩阵,配合 getperspectivetransform 用;返回点集 Mat)clipline(w,h,p1,p2)— 线段裁剪到矩形内(p1/p2 可为 {x,y} 表或 Point 指针;返回 bool 与裁剪后坐标)getrotationmatrix2d(center,angle,scale)— 旋转矩阵;remap(m,map1,map2)— 重映射getrectsubpix / borderinterpolate / copymakeborder— 子像素取块 / 边界推算 / 加边pyrdown / pyrup(m)— 金字塔降/升采样;tile(m,nx,ny)/repeat_(m,nx,ny)— 平铺复制
阈值 / 边缘 / 轮廓 / 特征 已支持 iOS 已支持
threshold(m,thresh,maxval,type)/adaptivethreshold(...)— 二值化inrange(m,lower,upper)— 区间掩码;canny(m,t1,t2)— Canny 边缘findcontours(m[,mode,method])/drawcontours— 轮廓提取与绘制contourarea / arclength / moments(m[,binaryImage])— 面积 / 周长 / 矩boundingrect / boxpoints / minarearect / minenclosingcircle— 外接框与最小包围fitellipse / fitline / approxpolydp / convexhull / convexitydefects / iscontourconvex / pointpolygontest / matchshapes— 拟合与形状匹配(convexityDefects 需下标版凸包:convexhull(...,returnPoints=false))connectedcomponentswithstats(m[,conn,ltype])— 连通域(⚠️ 6 參重载第 6 参是 ltype=CV_32S,别传 CCL_DEFAULT)goodfeaturestrack / cornersubpix— 角点;houghlines / houghlinesp / houghcircles— 霍夫直线/圆distancetransform / watershed— 距离变换 / 分水岭
色彩与通道 已支持 iOS 已支持
cvtcolor(m,code)— 色彩空间转换;togray(m)— 转灰度;tobgr(m)— RGBA→BGR(存图前必做)split(m)/merge(mats)— 通道拆分 / 合并mixchannels / extractchannel / insertchannel— 通道混合 / 抽取 / 插入applycolormap(m,code)— 伪彩色映射
绘图 已支持 iOS 已支持
circle(m,center,radius,color[,thickness])— 圆;line / arrowedline— 直线 / 箭头线rectangle(m,pt1,pt2,color[,thickness])— 矩形;polylines / fillpoly— 折线 / 多边形填充ellipse(m,center,axes,angle,...)— 椭圆;puttext(m,text,org,font,scale,color)— 文字
图像 IO 已支持 iOS 已支持
imread(path[,flags])/imwrite(m,path)— 读 / 写图像文件imencode(m,ext)/imdecode(buf,flags)— 内存编解码;imdecodeb64(b64)— base64 解码为 Mattobase64(m[,ext])— Mat → base64(PNG/JPG);saveimage(m,path)— 保存
模板匹配 / 找图 已支持 iOS 已支持
matchtemplate(img,tmpl[,method])— 模板匹配(结果为相似度图)findimage(src,tmpl[,method,sim])— 找图,返回{x,y,w,h,sim}或 nil(绝对坐标)findimageall(src,tmpl[,method,sim,maxCount])— 找全部命中(按相似度降序)- 默认
TM_CCOEFF_NORMED;纯色/无纹理模板必须显式传"TM_SQDIFF_NORMED",否则必然找不到
完整示例(可直接跑):
-- ══ 模板匹配 / 找图 完整测试 ══
-- 1) 准备:截全屏当大图,区域帧直接当模板(也可 cv.imread 读本地图片文件)
local src = cv.snapshot()
local tmpl = cv.snapshot(100, 200, 259, 439) -- (l,t,r,b) 区域帧
-- 2) 找一个:默认 TM_CCOEFF_NORMED、sim≥0.8,返回 {x,y,w,h,sim}(绝对坐标)或 nil
local r = cv.findimage(src, tmpl)
if r then
print(string.format("命中 (%d,%d) %dx%d sim=%.3f", r.x, r.y, r.w, r.h, r.sim))
tap(math.floor(r.x + r.w/2), math.floor(r.y + r.h/2)) -- 点命中中心
else
print("未命中(相似度未达 0.8)")
end
-- 3) 显式传方法与阈值
local r2 = cv.findimage(src, tmpl, "TM_CCOEFF_NORMED", 0.9)
-- 4) 找全部:sim≥0.7 最多 10 个,结果按相似度降序
local all = cv.findimageall(src, tmpl, "TM_CCOEFF_NORMED", 0.7, 10)
for i, m in ipairs(all) do
print(string.format("第%d个 (%d,%d) %dx%d sim=%.3f", i, m.x, m.y, m.w, m.h, m.sim))
end
-- 5) 纯色/无纹理模板(纯色图标、色块)必须 TM_SQDIFF_NORMED,且 sim 阈值放低
local r3 = cv.findimage(src, tmpl, "TM_SQDIFF_NORMED", 0.1)
if r3 then print("SQDIFF 命中", r3.x, r3.y, string.format("%.3f", r3.sim)) end
-- 6) 只要相似度图自己分析:matchtemplate 出 CV_32F 单通道 Mat,配 newDouble/newPoint 指针取峰值
local res = cv.matchtemplate(src, tmpl, "TM_CCOEFF_NORMED")
local mnV, mxV = cv.newDouble(0), cv.newDouble(0)
local mnP, mxP = cv.newPoint(0, 0), cv.newPoint(0, 0)
cv.minmaxloc(res, mnV, mxV, mnP, mxP)
local pos = cv.getPoint(mxP)
print("最高相似度", cv.getDouble(mxV), "@", pos.x, pos.y)
cv.deletePtr(mnV); cv.deletePtr(mxV); cv.deletePtr(mnP); cv.deletePtr(mxP)
其他 已支持 iOS 已支持
randu / randn(m,low,high)— 均匀/正态随机填充;setrngseed(seed)— 随机种子getnumthreads() / setnumthreads(n)— 计算线程数hconcat / vconcat(mats)— 水平 / 垂直拼接
yolo 模块(YOLO11/v8 目标检测,ncnn)
yolo_init(param, bin, conf?, nms?, input_size?, names_txt?) 已支持 iOS 已支持
初始化 YOLO 检测器(ncnn 推理,4 线程 + fp16)。模型须用 yolo export format=ncnn 导出(输入名 in0、输出名 out0),如 yolo11n.ncnn.param + yolo11n.ncnn.bin。
param, bin{string} ncnn 模型两件套的绝对路径conf{number} 置信度阈值,默认 0.25(低于该分数的框丢弃;嫌误检多就调高,如 0.5)nms{number} NMS IoU 阈值,默认 0.45input_size{int} 推理输入边长,默认 640(32 的倍数,范围 32~1280)names_txt{string} 可选,类别名文件(每行一个,按序号对应);不传则 label 为 "cls<编号>"- 返回:
bool;失败返回 false + 错误串(code=1 路径空 / 2 param 加载失败 / 3 bin 加载失败) - 中途可重复调用 yolo_init 换阈值(会重载模型,几百毫秒)
local M = "/var/mobile/Documents/"
local ok = yolo_init(M .. "yolo11n.ncnn.param", M .. "yolo11n.ncnn.bin", 0.5)
yolo_detect_image(mat) 已支持 iOS 已支持
对一个 cv Mat(如 cv.snapshot() 的返回)做目标检测。
- 返回:
{width=, height=, count=, dets={...}} - 每个 det:
{x=, y=, w=, h=, rate=, cls=, label=}— 坐标为 Mat 像素坐标,rate 为置信度,cls 为类别编号,label 为类别名(无 names_txt 时为 "cls<编号>") - 未初始化返回 nil + 提示
local m = cv.snapshot()
local r = yolo_detect_image(m)
for i = 1, r.count do
local d = r.dets[i]
print(string.format("[cls%d] rate=%.2f x=%d y=%d w=%d h=%d", d.cls, d.rate, d.x, d.y, d.w, d.h))
end
yolo_detect_file(path) 已支持 iOS 已支持
直接读图像文件做检测,返回表结构与 yolo_detect_image 一致。
path{string} 图像文件路径- 返回:同
yolo_detect_image;读图失败返回 nil + 提示
yolo_close() 已支持 iOS 已支持
释放检测器(模型内存)。脚本结束不自动释放,长期驻留脚本建议不用时 close。
- 返回:无
tessocr 模块
文字识别(Tesseract):先 ocr_init 加载语言数据,再选择识别来源(文件 / 当前图像 / 像素数据),返回识别出的文字字符串。语言数据已内置在 /var/mobile/Media/com.luatouch.app/models/tessdata。
ocr_init(dataDir, language) 已支持iOS 已支持
初始化文字识别引擎,加载语言数据。
dataDir{string} 语言数据目录,如/var/mobile/Media/com.luatouch.app/models/tessdatalanguage{string} 识别语言,如eng(英文)- 返回:
bool是否初始化成功
local ok = ocr_init("/var/mobile/Media/com.luatouch.app/models/tessdata", "eng")
ocr_set_param(param, value) 已支持iOS 已支持
设置识别参数(需先 ocr_init)。底层引擎为 Tesseract 5.5.0(静态库、仅 LSTM 识别引擎),参数名沿用 Tesseract 官方变量名。
param{string} 参数名,见下方参数表,如tessedit_char_whitelist(字符白名单)value{string} 参数值,必须用字符串传入:布尔写"1"/"0"(也接受true/false/T/F/Y/N),数值写"7"、"0.7"这类字符串- 返回:
bool参数名存在即返回true;未ocr_init时返回false, "OCR not initialized"
返回值只代表“参数名存在”,不等于一定生效:
- 参数名写错、或该变量在当前 Tesseract 版本里不存在 → 返回
false - 表中标注「初始化前」的参数必须在
ocr_init之前设定,而本接口要求先ocr_init,所以这类参数会返回 true 但实际不生效 - 重新执行
ocr_init(例如换语言)会重置全部参数,需要重新设置
可直接使用的参数
| 参数名 | 类型 | 默认 | 说明 |
|---|---|---|---|
tessedit_char_whitelist | string | 空 | 只识别白名单内的字符,固定字符集场景可明显提高准确率 |
tessedit_char_blacklist | string | 空 | 禁止输出指定字符(与白名单互斥使用) |
page_seg_mode | int | 6 | 页面分割模式;本引擎的专用别名,等价于 tessedit_pageseg_mode |
tessedit_pageseg_mode | int | 6 | 同上:0=方向/脚本检测,3=全自动,6=整块文本,7=单行,8=单词,10=单字符,11=稀疏文本 |
preserve_interword_spaces | bool | 0 | 保留词间多个空格,适合列表、对齐文本 |
user_defined_dpi | int | 0 | 指定输入图像 DPI,0 表示自动判断 |
textord_tabfind_find_tables | bool | 1 | 表格检测;纯文字截图可设 0,略快 |
invert_threshold | double | 0.7 | 反色判定阈值(tessedit_do_invert 的替代参数) |
tessedit_do_invert | bool | 1 | 尝试反色图像;Tesseract 已标记废弃,6.0 版将移除 |
tessedit_write_images | bool | 0 | 保存预处理后的图像,排查识别不准时使用 |
初始化前才能设定的参数(本接口设置不会生效)
| 参数名 | 默认 | 说明 |
|---|---|---|
load_system_dawg | 1 | 加载系统词典(关闭后不再把字符纠正成词典词) |
load_freq_dawg | 1 | 加载高频词词典 |
load_punc_dawg | 1 | 加载标点词典 |
tessedit_ocr_engine_mode | 3 | 识别引擎选择;本构建仅含 LSTM 引擎 |
当前版本不存在的参数名(写了会返回 false)
tessedit_do_orientation:5.5.0 无此变量,方向检测由页面分割模式(page_seg_mode)控制tessedit_do_blur_detection:5.5.0 无此变量tessedit_use_opencl:Tesseract 5 已移除 OpenCL 支持
-- 只识别数字和字母
ocr_set_param("tessedit_char_whitelist", "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
-- 单行文本 + 保留词间空格
ocr_set_param("page_seg_mode", "7")
ocr_set_param("preserve_interword_spaces", "1")
-- 纯文字截图关掉表格检测
ocr_set_param("textord_tabfind_find_tables", "0")
-- 用返回值判断参数名是否有效
print(ocr_set_param("tessedit_do_orientation", "0")) -- false:本版本没有这个参数
ocr_from_file(path) 已支持iOS 已支持
识别指定图片文件中的文字。
path{string} 图片路径- 返回:
string识别出的文字;失败返回nil, errMsg
local text = ocr_from_file("/var/mobile/Documents/screen.png")
ocr_from_image(mat) 已支持iOS 已支持
识别 Mat 图像中的文字(★ 必传参数:旧版无参「当前图像」用法已随 cv_copyImageToMat 移除)。
mat{Mat}cv.snapshot()返回的 Mat 对象- 返回:
string识别出的文字;失败返回nil, errMsg
local cur = cv.snapshot() -- 截屏
local text, err = ocr_from_image(cur)
if text then print("识别:", text) end
ocr_from_pixels(data, w, h, bpp) 已支持iOS 已支持
识别原始像素数据中的文字。
data{string} 二进制像素数据(raw 字节串)w, h{int} 图像宽高bpp{int} 每像素字节数- 返回:
string识别出的文字;失败返回nil, errMsg
local text = ocr_from_pixels(rawdata, 1242, 2208, 4)
ocr_set_rectangle(left, top, w, h) 已支持iOS 已支持
限定识别区域(相对当前图像/像素数据的坐标),只识别该区域内的文字。
left, top{int} 区域左上角w, h{int} 区域宽高- 返回:
bool设置成功;失败返回false, errMsg
ocr_set_rectangle(100, 200, 200, 100)
ocr_clear() 已支持iOS 已支持
清除识别状态(区域限制、临时数据等)。
- 参数:无
- 返回:无
ocr_clear()
ocr_close() 已支持iOS 已支持
释放识别引擎资源。
- 参数:无
- 返回:无
ocr_close()
ocr 模块(点阵字库识别/找字)
点阵字库 OCR(移植自 OP 插件,大漠同语义):用字库文件里每个字的 0/1 点阵,与屏幕按颜色二值化后的点阵逐位比对。与 tessocr / ppocr 的 AI 识别不同—— 只认识字库里有的字,但速度极快(毫秒级)、结果精确可控,适合游戏内固定字体的文字 (金币数量、按钮文字、菜单项、伤害数字等)。
字库文件:用「AutoGo图色助手」的字库制作导出(OP 文本格式,每行
名字$颜色$h,w,bit$HEX),放 <数据目录>/res/ 下(与 findPic 模板同基准)。
GBK / UTF-8 编码都认(GBK 自动转 UTF-8,脚本里可直接用中文比对)。
⚠️ 字库必须与目标画面同源同尺寸:字库是在哪张截图上取的,识别就得用同一分辨率、 同一字体渲染的画面(换分辨率 / 换设备 / 换窗口大小 = 字的像素尺寸变了 → 必须重取字库, 否则简单字勉强能中、笔画复杂的字必挂)。
匹配规则:sim 0.1~1.0,默认 1.0(精确:一个像素都不许错);
sim<1 时按 有效相似度 = 0.5 + sim/2 放宽容差(如 sim=0.8 → 允许 10% 像素差异)。
建议优先用 1.0,识别不到再降(越低假阳性越多)。
区域必须完整包住文字:返回坐标是字形的左上角,区域边界把字窗口切掉一部分的
字会被直接跳过(下边界多留一个字高)。
ocrSetDict(idx, path) 已支持 iOS 已支持
载入字库文件到 idx 号槽位(0~99)。重复载入同槽 = 整槽覆盖。
idx{number} 槽位号 0~99path{string} 字库文件路径;相对路径以<数据目录>/res/为基准- 返回:
number载入的字数;失败返回-1 - 字库槽全局共用:主脚本与所有子线程看到的是同一份
local n = ocrSetDict(0, "opdict.txt") -- 实际读 <数据目录>/res/opdict.txt
print("载入 " .. n .. " 个字")
ocrUseDict(idx) 已支持 iOS 已支持
切换当前使用的字库槽位(后续 ocr/findStr 都用它)。
idx{number} 槽位号 0~99- 返回:
number该槽字数;槽空返回0
ocrSetDict(0, "dict_main.txt") -- 主字库(菜单/按钮)
ocrSetDict(1, "dict_num.txt") -- 数字字库(伤害/金币)
ocrUseDict(1) -- 切到数字字库做识别
ocrGetNowDict() 已支持 iOS 已支持
返回当前使用的槽位号(number)。
ocrGetDictCount(idx) 已支持 iOS 已支持
返回 idx 号槽的字数;该槽未载入返回 -1。
ocr(x1, y1, x2, y2, color?, sim?) 已支持 iOS 已支持
识别区域内所有字库字,按阅读顺序(|Δy|<9 视为同一行,行内按 x)拼接成字符串返回。
x1, y1, x2, y2{number} 识别区域;x2/y2 ≤ 0表示取到帧右下角color{string} 可选,颜色串(同 findColor 格式):"RRGGBB[-偏色]",'|'分隔多组;默认"FFFFFF-323232"。必须与做字库时用的颜色口径一致sim{number} 可选,相似度 0.1~1.0,默认1.0- 返回:
string拼接后的识别文本(无命中返回空串)
ocrSetDict(0, "opdict.txt")
local s = ocr(200, 310, 480, 360, "FFFFFF-323232", 1.0)
print(s) -- 例:雷电游戏中心
ocrEx(x1, y1, x2, y2, color?, sim?) 已支持 iOS 已支持
同 ocr,但返回逐字结果(含每个字的坐标)。
- 参数同
ocr - 返回:
table{{index=结果序号, name=字符, x=, y=}, ...},index从 0 起(阅读序),x/y= 字形左上角(屏幕绝对坐标)
local hits = ocrEx(0, 0, 0, 0, "FFFFFF-323232", 1.0)
for i, h in ipairs(hits) do
print(h.index, h.name, h.x, h.y)
end
findStr(x1, y1, x2, y2, strs, color?, sim?) 已支持 iOS 已支持
在区域内找字库里指定的词,返回第一个命中的词。
strs{string} 目标词,'|'分隔多词,如"确定|取消"; 词名必须与字库里的一致- 返回:3 个值 ——
idx命中的词序号(0 = 第一个词,无命中-1)、x/y该词首个命中字形的左上角(无命中 -1,-1)
local idx, x, y = findStr(0, 0, 0, 0, "确定|取消", "FFFFFF-323232", 1.0)
if idx == 0 then
print("找到 确定 @ " .. x .. "," .. y) -- 点它: tap(x + 10, y + 10)
elseif idx == 1 then
print("找到 取消 @ " .. x .. "," .. y)
end
findStrEx(x1, y1, x2, y2, strs, color?, sim?) 已支持 iOS 已支持
同 findStr,但返回全部命中。
- 参数同
findStr - 返回:
table{{index=词序号, name=词名, x=, y=}, ...}(无命中空表)
local all = findStrEx(0, 0, 0, 0, "雷|闪电|中心", "FFFFFF-323232", 1.0)
for i, h in ipairs(all) do
print("词" .. h.index .. " [" .. h.name .. "] @ " .. h.x .. "," .. h.y)
end
ppocr 模块
中文文字识别(本地模型):先 ppocr_create 创建识别器,ppocr_init 加载检测/识别模型,再识别当前图像或图片文件,返回逐行文字及位置。模型已内置在 /var/mobile/Media/com.luatouch.app/models/ch/。
ppocr_create() 已支持iOS 已支持
创建文字识别器实例。
- 参数:无
- 返回:
bool创建成功;失败返回false, errMsg
local ok = ppocr_create()
ppocr_init(det.param, det.bin, rec.param?, rec.bin?, cls.param?, cls.bin?) 已支持iOS 已支持
加载识别模型(检测模型必填,识别/分类模型可选)。
det.param, det.bin{string} 文本检测模型文件路径rec.param, rec.bin{string} 可选,文字识别模型文件路径cls.param, cls.bin{string} 可选,方向分类模型文件路径- 返回:
bool加载成功;失败返回false, errMsg
local M = "/var/mobile/Media/com.luatouch.app/models/ch/"
local ok = ppocr_init(
M .. "PP_OCRv5_mobile_det.ncnn.param", M .. "PP_OCRv5_mobile_det.ncnn.bin",
M .. "PP_OCRv5_mobile_rec.ncnn.param", M .. "PP_OCRv5_mobile_rec.ncnn.bin")
ppocr_set_param(name, value) 已支持iOS 已支持
设置识别参数。底层引擎为自研 PP-OCRv5(ncnn 推理),只支持下表列出的 8 个参数名,不沿用 PaddleOCR 官方配置文件里的字段名。
name{string} 参数名,见下方参数表value{string} 参数值,必须用字符串传入:数值写"0.45"、"960",布尔用"1"/"0"- 返回:
bool仅在未先调用ppocr_create时返回false, "PPOcr not created";其余情况一律返回true
返回值不能用来判断参数是否生效(与 ocr_set_param 不同):
- 参数名不在下表内(拼错、或用了 PaddleOCR 的字段名如
det_db_thresh、rec_img_h、num_threads)→ 静默忽略,仍然返回true - 本接口可重复调用、后设覆盖先设;参数在
ppocr_create之后即可设置,不必等ppocr_init,且ppocr_init不会重置已设参数 - 执行
ppocr_release后再ppocr_create会得到全新实例,全部参数恢复默认值 - 线程数不可调:检测固定用 ncnn 大核数、识别固定单线程
可用参数表
| 参数名 | 类型 | 默认 | 说明 |
|---|---|---|---|
target_size | int | 640 | 检测阶段输入图最长边的缩放上限(超过则等比缩小后再送模型)。调大:小字更清晰但变慢;调小:更快,小字可能漏检 |
box_thresh | float | 0.6 | 候选文本框的平均得分阈值(0~1),低于该值的框被丢弃。调低:框更多(含噪声);调高:框更少更干净 |
threshold | float | 0.3 | 检测概率图的二值化阈值。调低:文字区域更连片(易粘连);调高:更严格(易断裂) |
min_size_base | float | 3.0 | 最小框尺寸基准,实际最小边 ≈ min_size_base × 缩放比,用于过滤极小噪点框。调大:滤掉小字 |
max_candidates | int | 1000 | 单张图最多处理的候选轮廓数,超出直接截断。密集文本可调大 |
enlarge_ratio | float | 1.95 | 文本框向外扩张比例,保证文字边缘被完整裁入。过大易把相邻文字并进同一行 |
post_process_mode | int | 0 | 后处理方式:0=按比例扩张矩形(快);1=DBNet unclip(按轮廓面积/周长扩张,更贴合文字形状,稍慢) |
target_height | int | 48 | 识别阶段文字行统一缩放到的高度。调大:小字/笔画复杂字符更准但更慢;调小:更快 |
ppocr_create()
ppocr_init(M .. "PP_OCRv5_mobile_det.ncnn.param", M .. "PP_OCRv5_mobile_det.ncnn.bin",
M .. "PP_OCRv5_mobile_rec.ncnn.param", M .. "PP_OCRv5_mobile_rec.ncnn.bin")
-- 小字场景:加大检测分辨率、放宽候选框阈值
ppocr_set_param("target_size", "960")
ppocr_set_param("box_thresh", "0.4")
-- 密集文本:提高候选上限
ppocr_set_param("max_candidates", "3000")
-- 更贴合文字轮廓的后处理(略慢)
ppocr_set_param("post_process_mode", "1")
ppocr_set_param("enlarge_ratio", "1.8")
ppocr_detect_image(mat) 已支持iOS 已支持
识别 Mat 图像中的文字,返回逐行文字及位置(★ 必传参数:旧版无参「当前图像」用法已随 cv_copyImageToMat 移除)。
mat{Mat}cv.snapshot()返回的 Mat 对象- 返回:
table结果表{width, height, lines={{text=, rate=, left=, top=, right=, bottom=}, ...}};失败返回nil, errMsg width, height识别图像尺寸;lines每行文字,text文字内容,rate置信度 0~1,left/top/right/bottom位置坐标(0~1 归一化,乘屏幕宽高得像素)
local cur = cv.snapshot() -- 截屏
local res = ppocr_detect_image(cur)
if res then
for i, line in ipairs(res.lines) do
print(i, line.text, string.format("%.2f", line.rate))
end
end
ppocr_detect_file(path) 已支持iOS 已支持
识别指定图片文件中的文字,返回格式同 ppocr_detect_image。
path{string} 图片路径- 返回:
table结果表;失败返回nil, errMsg
local res = ppocr_detect_file("/var/mobile/Documents/screen.png")
ppocr_release() 已支持iOS 已支持
释放识别器资源。
- 参数:无
- 返回:
bool是否成功释放
ppocr_release()
hud 模块(悬浮显示)
hud 提供悬浮文本显示功能,支持多实例、彩色文本、独立位置/背景/显隐。与引擎「悬浮球」是两回事:hud 是纯显示浮层(不接管触摸,绝不遮挡点击),悬浮球是单实例可拖动的控制入口。
坐标系为物理像素,与 getScreenSize()、tap 等接口完全同一套单位,无需任何换算。setPosition 的 fx/fy 是 0~1 的屏幕百分比,width/height 和字号同样是像素。所有实例通过全局表 hud 创建。
hud 模块还包含HTML 面板系列(showUi / hideUi / isShowUi,均为全局函数):以全屏浮层加载本地 HTML 作为交互面板,可传入 JSON 配置回填表单,面板内 JS 操作结果以字符串带回 Lua。面板自带输入键盘(不调用系统键盘),随面板开关。
showUi(path [, countdown [, query [, hash [, w [, h]]]]]) 已支持iOS 已支持
显示 HTML 面板并阻塞等待用户操作。面板跟随屏幕横竖方向(横屏下面板旋转显示,宽高按所见方向自动换算;打开时实时按当前方向布局);默认尺寸为屏幕宽高的 90% 居中。返回两个值:操作结果 + JS 带回的表单数据。
path{string} 加载目标:绝对路径(/var/mobile/...)、file://绝对路径(可自带?query)、http(s)://远程 URL、或裸 HTML 字符串countdown{int} 可选,默认 0:开始按钮倒计时秒数;>0 时按钮显示「开始(N)」每秒减一,归零自动触发「开始」query{string} 可选,默认 0 / -1 / 空 = 不传:拼接在 URL?后,引擎自动 percent-encode(main.lua 直接传裸 JSON 即可)hash{string} 可选,默认 0 / -1 / 空 = 不传:拼接在 URL#后,引擎自动 percent-encode(query 的兜底通道)w{number} 可选,默认 0:面板宽(每维独立)——≤0 或不传 = 屏幕宽 × 90%;0~1 = 屏幕宽的比例(如 0.5 = 半屏宽);>1 = 像素值(与getScreenSize()同一坐标系)h{number} 可选,默认 0:面板高,规则同w(按屏幕高计算)- 返回:
result{int} 1=点「开始」/倒计时归零,0=点「关闭」,-1=叉号/其他方式关闭(含 hideUi 唤醒);data{string|nil} 面板 JS 在开始/关闭时带回的表单 JSON 字符串,无则 nil
-- 打开面板,把上次配置 JSON 作为 query 传入(裸 JSON,引擎 percent-encode 注入 ? 后)
local r, data = showUi("/var/mobile/Media/com.luatouch.app/config/ui/index.html", 0, cfgJson)
if r == 1 then
-- 用户点了「开始」,data 为表单 JSON,可直接落盘下次复用
elseif r == -1 then
-- 用户关闭了面板
end
参数传递机制:引擎把第三参 percent-encode 后拼到 URL ? 后、第四参拼到 # 后。JS 端读取:location.search 优先 → location.hash 兜底 → 去 ?/# 前缀 → decodeURIComponent → JSON.parse(search 为第一通道,hash 仅在 search 为空时兜底)。
hideUi() 已支持iOS 已支持
隐藏 HTML 面板;若有 showUi 正在阻塞,则唤醒并使其按「叉号关闭」返回 -1(无 data)。
- 返回:无
hideUi() -- 关闭面板(定时器等场景主动收起)
isShowUi() 已支持iOS 已支持
检查 HTML 面板是否正在显示。
- 返回:
booltrue 显示中 / false 未显示
if isShowUi() then
-- 面板还开着,先别重复打开
end
toast(message [, x [, y [, duration]]]) 已支持iOS 已支持
显示 Toast 提示信息(非阻塞,自动消失)。
message{string} 要显示的提示信息x{int} 可选,默认 -1:显示 X 坐标(物理像素,左上角原点;-1 = 水平居中)y{int} 可选,默认 -1:显示 Y 坐标(物理像素;-1 = 垂直居中)。横屏时按当前所见方向自动换算,直接填肉眼看到的像素位置即可duration{int} 可选,默认 -1:显示时长毫秒(-1 = 2000ms)- 返回:无
toast("Hello LuaTouch", -1, -1, -1) -- 默认居中 2 秒
toast("操作完成", 100, 300, 3000) -- 指定位置 3 秒
alert(title, content [, btn1Text [, btn2Text]]) 已支持iOS 已支持
弹出系统级对话框,阻塞直到用户点击按钮。
title{string} 弹窗标题content{string} 弹窗内容btn1Text{string} 可选,默认「确定」:第一个按钮文字btn2Text{string} 可选,默认空:第二个按钮文字;传空字符串则只显示一个按钮- 返回:
int0 = 点击第一个按钮,1 = 点击第二个按钮
local r = alert("确认操作", "是否继续?", "取消", "确定")
if r == 1 then
-- 用户点了确定
end
inputAlert(title, content [, placeholder [, defaultText [, btn1Text [, btn2Text]]]]) 已支持iOS 已支持
弹出带输入框的系统级对话框,阻塞等待用户操作。
title{string} 弹窗标题content{string} 说明文字placeholder{string} 可选,默认空:输入框占位提示defaultText{string} 可选,默认空:输入框默认文本btn1Text{string} 可选,默认「取消」:取消类按钮文字btn2Text{string} 可选,默认「确定」:确认类按钮文字;为空则只显示一个按钮- 返回:
text{string} 输入内容;ok{bool} true = 点击确认按钮,false = 点击取消
local text, ok = inputAlert("输入", "请输入备注", "备注", "", "取消", "确定")
if ok then
-- 使用 text
end
setBallPosition(fx, fy) 已支持iOS 已支持
移动悬浮球并保持停靠(横竖屏自动适配)。
fx{number} 停靠边:0=左,1=右fy{number} 边缘高度百分比(0~1,支持小数点后两位)- 返回:无
setBallPosition(0, 0.1) -- 靠左边缘 10% 高
setBallPosition(1, 0.5) -- 靠右边缘中间
hud.new() 已支持iOS 已支持
创建一个新的 HUD 实例。
- 参数:无
- 返回:
userdataHUD 实例(方法均返回自身,支持链式调用)
local h = hud.new()
h:setPosition(fx, fy, width, height) 已支持iOS 已支持
设置 HUD 的位置和大小(旋转自动适配:横屏自动按 90° 换算成视觉位置,内容不裁剪)。
fx, fy{number} 左上角百分比(0~1,支持小数点后两位);按竖屏语义计,横屏时自动换算width, height{int} 宽高,单位物理像素(与getScreenSize()同一套,不是点)- 返回:
userdata自身(可链式)
h:setPosition(0.1, 0.8, 900, 180) -- 左上角 10%、80% 处,宽 900 高 180 像素
-- 换算参考:@3x 设备上 1 个设计点 = 3 像素,300×60 点 = 900×180 像素
h:setBackgroundColor(color) 已支持iOS 已支持
设置 HUD 背景颜色(支持透明度)。
color{string} 十六进制,#RRGGBB或#RRGGBBAA(AA 为透明度)- 返回:
userdata自身
h:setBackgroundColor("#2D2D30")
h:setBackgroundColor("#00000080") -- 半透明黑色
h:setTextSize(size) 已支持iOS 已支持
设置 HUD 字体大小。
size{int} 字号,单位物理像素(推荐 90~180,约等于 30~60 设计点)- 返回:
userdata自身
h:setTextSize(135) -- ≈ 45 设计点
h:setText(items) 已支持iOS 已支持
设置 HUD 文本内容(支持多色文本)。
hud.new() 出来的实例默认是隐藏的:只调 setPosition / setTextSize / setText 不会显示,必须调一次 h:show()。之后循环里不断 setText 刷新内容即可,不用重复 show;调过 h:hide() 之后要重新 show() 才会再出现。
items{table} 文本项数组,每项含TextColor(颜色字符串)与Text(文本)- 返回:
userdata自身
h:setText({
{TextColor = "#00FF00", Text = "HP: "},
{TextColor = "#FFFFFF", Text = "100/100"},
})
h:show() 已支持iOS 已支持
显示 HUD。
- 返回:
userdata自身
h:show()
h:hide() 已支持iOS 已支持
隐藏 HUD。
- 返回:
userdata自身
h:hide()
h:isVisible() 已支持iOS 已支持
检查 HUD 是否可见。
- 返回:
booltrue 可见 / false 隐藏
if h:isVisible() then
-- HUD 当前可见
end
h:destroy() 已支持iOS 已支持
销毁 HUD 实例,释放窗口资源(也可等 Lua GC 自动回收)。
- 返回:无
h:destroy()
示例 已支持iOS 已支持
-- 创建并配置 HUD(宽高/字号都是物理像素)
local h = hud.new()
h:setPosition(0.1, 0.8, 900, 180) -- 屏幕下方,宽 900 高 180 像素
:setBackgroundColor("#3D000080")
:setTextSize(135)
:show() -- ← 必须调,否则不会显示
while true do
h:setText({
{TextColor = "#00FF00", Text = "当前时间: "},
{TextColor = "#FFFFFF", Text = os.date("%Y-%m-%d %H:%M:%S")},
})
sleep(1000)
end
draw 模块(屏幕绘制)
draw 提供屏幕叠加绘制功能:在屏幕上实时显示直线、矩形、圆形、多边形等图形标注(画在悬浮球窗口上的全屏透明覆盖层,不遮挡触摸)。常用于脚本调试时标注:找到的按钮画个框、点击位置画个圈、寻路路径画条线,肉眼直接看到。
坐标系为物理像素(与 findColor / tap / getScreenSize() 同一坐标系)。注意:坐标基于手机实际分辨率(如 1242×2208),脚本需按自己屏幕分辨率自行换算比例。
颜色参数统一为 ARGB 整数(0xAARRGGBB),如红色 0xffff0000;thickness 传 -1 表示填充。
句柄机制:每个绘制函数返回一个句柄(自增 id),图形不会自动消失,靠 clearDraw(句柄) 按句柄单独清除,或 clearDraw() 无参全部清除。
drawLine(x0, y0, x1, y1, argb, thickness) 已支持iOS 已支持
绘制一条直线(任意角度斜线均可,两端圆头)。
x0, y0{int} 起始点坐标x1, y1{int} 终点坐标argb{int} 颜色,如0xffff0000(红色)thickness{int} 线宽(物理像素,自动换算)- 返回:{int} 图形句柄(用于
clearDraw(句柄)清除)
local id = drawLine(100, 100, 1100, 2000, 0xffff0000, 20) -- 斜线
-- clearDraw(id) 清除这条线
drawRect(x0, y0, x1, y1, argb, thickness) 已支持iOS 已支持
绘制矩形框;thickness = -1 填充。
x0, y0{int} 左上角坐标x1, y1{int} 右下角坐标argb{int} 颜色thickness{int} 线宽,-1填充- 返回:{int} 句柄
local id = drawRect(300, 400, 900, 900, 0xff00ff00, 10) -- 绿框
fillRect(x0, y0, x1, y1, argb) 已支持iOS 已支持
填充一个矩形区域。
x0, y0{int} 左上角坐标x1, y1{int} 右下角坐标argb{int} 颜色(可用0x80前缀做半透明)- 返回:{int} 句柄
local id = fillRect(700, 300, 1000, 600, 0x80ff0000) -- 半透明红填充
drawCircle(x, y, radius, argb, thickness) 已支持iOS 已支持
绘制圆形;thickness = -1 填充。
x, y{int} 圆心坐标radius{int} 半径argb{int} 颜色thickness{int} 线宽,-1填充- 返回:{int} 句柄
local id = drawCircle(621, 1104, 200, 0xff0000ff, -1) -- 屏幕中心蓝色填充圆
drawPolygon(points, argb, thickness, isclosed) 已支持iOS 已支持
绘制多边形;thickness = -1 填充。
points{table} 顶点数组{{x1,y1},{x2,y2},...}argb{int} 颜色thickness{int} 线宽,-1填充isclosed{boolean} 是否闭合- 返回:{int} 句柄
local id = drawPolygon({{200,1600},{500,1500},{450,1850}}, 0xff00ff00, 8, true)
clearDraw(id?) 已支持iOS 已支持
清除屏幕绘制图形。
id{int|nil} 可选:传句柄只清除对应图形;不传清除全部- 返回:无
clearDraw(id1) -- 只清句柄 id1 对应的图形
clearDraw() -- 全部清除
示例 已支持iOS 已支持
-- 标注:找到按钮画框、点击位置画圈(坐标=物理像素,按自己屏幕分辨率换算)
local box = drawRect(300, 400, 900, 900, 0xffff0000, 10) -- 红框标注
local dot = drawCircle(621, 1104, 60, 0xff00ff00, -1) -- 绿点标注
sleep(1000)
clearDraw(box) -- 单独清除红框,绿点保留
sleep(1000)
clearDraw() -- 全部清除
imgui 模块(即时模式 GUI,与安卓同一套 API)
即时模式的悬浮面板,用来给脚本做交互控制台:按钮、滑块、输入框、下拉、页签、菜单、弹窗、图表。面板独立悬浮在最上层,不影响被控应用。脚本写法与安卓完全一致(同名同参),底层差异对脚本透明。
1. 即时模式:没有控件对象,返回值就是事件 已支持iOS 已支持
每帧把界面从头描述一遍,控件不保存在任何变量里。你需要的状态(开关、数值、文本)自己用 Lua 变量存,每帧回填旧值、再把返回值写回变量。
-- 固定帧循环(复制这段就能跑)
imgui.Init() -- ① 建悬浮层(异步,用 Ready() 探测)
while not imgui.Ready() do sleep(50) end -- ② 等图层就绪(一般 <1s)
while imgui.Running() do
if imgui.NewFrame() then -- ③ 帧开始(吸收触摸输入)
imgui.SetNextWindowSize(600, 400, imgui.Cond.FirstUseEver)
local visible = imgui.Begin("我的面板")
if visible then
imgui.Text("Hello")
end
imgui.End() -- ⚠️ 必须在 if 外面
imgui.Render() -- ④ 帧结束,渲染上屏
end
sleep(16) -- ≈60fps
end
imgui.Shutdown() -- ⑤ 收图层
⚠️ Begin() 返回 false 只表示「窗口被折叠」,里面的控件不画,但 End() 照样要调用——漏掉会每帧报 Missing End() 并在日志里刷屏。
⚠️ 控件的 label 是 ImGui 的隐藏 ID:不要写成 "次数: " .. n 这种每帧变化的名字,否则控件每帧都是「新控件」,点击会丢。要显示动态文字就 SameLine() 后跟一个 Text()。
2. 生命周期 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
Init() | 建悬浮图层,返回 true。每次调用都是真重建:内部先摘掉旧图层(Shutdown 同款)再建新的,上次被强杀留下的残留状态自动清掉。返回 true 只代表请求已发出,不代表图层能用,接着必须轮询 Ready() |
Ready() | 面板是不是真的能用(能否上屏)。Init() 只是发起创建,是异步的,所以 Init 之后必须先 while not imgui.Ready() do sleep(50) end 再进帧循环;Init() 与 Shutdown() 都会让 Ready() 立刻变回 false,直到面板重新建好 |
Running() | 恒返回 true,配合 while 用(语义占位) |
NewFrame() | 帧开始,吸收触摸;返回 false 时本帧跳过(图层未就绪/无焦点) |
Render() | 帧结束,渲染 + 上屏;必须与 NewFrame() 成对 |
Shutdown() | 收图层(脚本退出前调;不调也会在脚本停止时被引擎强制收掉) |
3. 窗口 已支持iOS 已支持
核心两个:
imgui.Begin(name [, open [, flags]])→ 打开窗口,返回是否可见;第二参传 boolean 时再返回是否还开着(点了 X 变 false)imgui.End()→ 关闭窗口,无条件调用
-- 三种写法
local v1 = imgui.Begin("无关闭按钮") -- 1 个返回值
local v2, open = imgui.Begin("带 X", true) -- 2 个返回值
local v3 = imgui.Begin("禁止拖动", imgui.Win.NoMove) -- 第二参数字=窗口标志
imgui.End()
| 函数 | 说明 |
|---|---|
SetNextWindowPos(x, y [, cond]) | 下一个窗口位置;cond 默认 imgui.Cond.FirstUseEver(只在首次生效,之后记住用户拖到哪) |
SetNextWindowSize(w, h [, cond]) | 下一个窗口尺寸,cond 同上;传 imgui.Cond.Always 则每帧强制、用户拖不动 |
SetNextWindowBgAlpha(a) | 下一个窗口背景透明度 0~1 |
SetNextWindowFocus() | 下一个窗口置顶 |
SetNextWindowScroll(x, y) | 下一个窗口的滚动位置;回顶用 (0, 0),要在 Begin 之前调 |
GetWindowPos() | 返回 x, y |
GetWindowSize() | 返回 w, h |
GetScrollY() / SetScrollY(y) | 读 / 设当前滚动量 |
SetScrollHereY([ratio]) | 把当前项滚到视口内,ratio 默认 0.5(居中) |
SetWindowFontScale(s) | 本窗口字号倍率(1.0 = 默认) |
窗口标志用 imgui.Win.*,常用:NoMove(禁止拖动)、NoResize(禁止拉伸)、NoCollapse(禁止折叠)、NoTitleBar、AlwaysAutoResize、NoScrollbar。多个标志用「加」组合:imgui.Win.NoMove + imgui.Win.NoResize。
4. 文本 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
Text(s) | 普通文本(不做 % 格式化,内容原样显示) |
TextDisabled(s) | 灰色弱化文本,适合放提示/状态 |
TextWrapped(s) | 超宽自动换行(长日志用这个) |
TextColored(r, g, b [, a], s) | 彩色文本,颜色 0~1;如 TextColored(1,0,0,"错误") |
LabelText(label, s) | 左标签 + 右值 |
Bullet() / BulletText(s) | 圆点 / 带圆点的文本 |
5. 布局 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
Separator() | 水平分隔线 |
SameLine([offset] [, spacing]) | 下一个控件摆在同一行(默认接着右侧) |
NewLine() / Spacing() | 换行 / 空一行 |
Dummy(w, h) | 占位空白 |
Indent([w]) / Unindent([w]) | 缩进 / 反缩进 |
BeginGroup() / EndGroup() | 逻辑分组:组内多个控件的包围盒合成一个整体参与布局 |
BeginChild(id [, w, h [, flags]]) / EndChild() | 子区域,可独立滚动,返回是否可见 |
6. 控件 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
Button(label [, w, h]) | 按钮,返回「本帧是否被点击」;w/h 传 0 表示按标签自适应 |
SmallButton(label) | 小号按钮(无边框内边距) |
ArrowButton(id, dir) | 箭头按钮,dir 用 imgui.Dir.Left/Right/Up/Down |
Checkbox(label, v) | 勾选框,传入旧值、返回新值(不会原地改你的变量) |
RadioButton(label, selected) | 单选框,返回是否被点击;组内互斥要自己写 if 点了 then idx = n end |
ProgressBar(frac [, text]) | 进度条,frac 0~1;第二参是覆盖显示的字符串 |
if imgui.Button("开始", 120, 60) then run() end
flag = imgui.Checkbox("启用", flag)
imgui.ProgressBar(done / total, string.format("%d/%d", done, total))
7. 数值 / 输入 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
SliderFloat(label, v, min, max) | 浮点滑块,返回拖动后的实时值 |
SliderInt(label, v, min, max) | 整数滑块 |
DragFloat(label, v [, speed, min, max]) | 拖动调值(无轨道),speed 默认 1 |
DragInt(label, v [, speed]) | 整数拖动 |
InputFloat(label, v) / InputInt(label, v) | 数值框;点击自动弹引擎软键盘,编辑中按输入原样显示(不截小数) |
InputText(label, s [, cap]) | 文本输入,返回当前文本;cap 是缓冲区长度,默认 256 |
Combo(label, index, {items}) | 下拉框,下标从 1 开始,返回新下标 |
ListBox(label, index, {items}) | 列表框,下标同样 1 起 |
ColorEdit3(label, r, g, b) / ColorEdit4(label, r, g, b, a) | 取色器,返回新分量(0~1) |
speed = imgui.SliderFloat("速度", speed, 0.1, 5.0)
name = imgui.InputText("名称", name)
mode = imgui.Combo("模式", mode, {"快速", "标准", "精细"}) -- mode 从 1 开始
r, g, b = imgui.ColorEdit3("颜色", r, g, b)
8. 树 / 页签 / 菜单 / 弹窗 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
TreeNode(label) | 树节点,返回是否展开;展开时必须调 TreePop() |
TreePop() | 结束树节点 |
SetNextItemOpen(open [, cond]) | 下一个树节点默认展开/收起 |
CollapsingHeader(label [, flags]) | 可折叠标题栏,返回是否展开(展开状态由 imgui 自动记忆,不用你存) |
Selectable(label [, selected [, flags]]) | 可选行,返回是否被点击 |
BeginTabBar(id [, flags]) / EndTabBar() | 页签容器,返回是否可见 |
BeginTabItem(label [, open]) / EndTabItem() | 单个页签,返回是否当前选中;传 open 时再返回 是否还开着 |
BeginMenuBar() / EndMenuBar() | 窗口内菜单栏(要在 Begin 之后) |
BeginMainMenuBar() / EndMainMenuBar() | 屏幕顶部主菜单栏(在窗口之外调用) |
BeginMenu(label [, enabled]) / EndMenu() | 一级菜单,返回是否展开 |
MenuItem(label [, shortcut [, selected]]) | 菜单项,返回是否被点击;selected 显示为勾选态 |
OpenPopup(id [, flags]) | 打开弹窗(配合按钮点击调用) |
BeginPopup(id [, flags]) / EndPopup() | 普通弹窗 |
BeginPopupModal(name [, open]) / EndPopup() | 模态框,传 open 时返回 可见, 还开着 |
CloseCurrentPopup() | 关闭当前弹窗(在弹窗内部调用) |
BeginTooltip() / EndTooltip() | 自定义 Tooltip 内容 |
SetTooltip(s) | 简易 Tooltip(一行文本) |
-- 页签
if imgui.BeginTabBar("tabs") then
if imgui.BeginTabItem("基本") then
imgui.Text("第一页")
imgui.EndTabItem()
end
if imgui.BeginTabItem("高级") then
imgui.Text("第二页")
imgui.EndTabItem()
end
imgui.EndTabBar()
end
-- 弹窗:按钮打开 → 模态框里点确定关闭
if imgui.Button("打开设置") then imgui.OpenPopup("设置") end
if imgui.BeginPopupModal("设置") then
imgui.Text("这里是弹窗内容")
if imgui.Button("确定") then imgui.CloseCurrentPopup() end
imgui.EndPopup()
end
9. 查询 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
IsItemHovered([flags]) | 上一个控件是否被悬停(手指在上面) |
IsItemActive() | 上一个控件是否正被按住/拖动 |
IsItemClicked([btn]) | 上一个控件本帧是否被点击 |
IsItemVisible() / IsItemEdited() | 上一个控件是否可见 / 本帧是否被编辑 |
IsMouseDown(btn) / IsMouseClicked(btn) / IsMouseReleased(btn) | 全局鼠标(触摸)状态,btn 用 imgui.MouseButton.Left 等 |
GetMousePos() | 返回 x, y(面板坐标系) |
GetDisplaySize() | 返回画布 w, h(横竖屏旋转时自动对调,用来重排窗口) |
SetItemDefaultFocus() | 弹窗里默认聚焦某个控件 |
PushID(id) / PopID() | 区分作用域:循环里生成同名控件时靠它避免 ID 冲突 |
10. 键盘注入(脚本软键盘) 已支持iOS 已支持
给「脚本自己画键盘」准备的:把字符/按键送进当前聚焦的输入框。
imgui.SendChars(s){string} 插入文本到当前聚焦的InputTextimgui.SendKey(name){string} 发一个按键;可选值:backspacedeleteenterescapetableftrightupdownhomeendspacepageuppagedowninsert;未知键名会直接报错
imgui.SendChars("abc")
imgui.SendKey("backspace")
11. 纹理 / 绘图 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
CreateTexture(w, h [, mode]) | 生成测试纹理,返回纹理句柄。mode:0=棋盘格(默认,用来看 UV/拉伸是否正常)1=渐变 2=四色块;w/h ≤ 2048 |
DeleteTexture(tex) | 释放纹理 |
Image(tex, w, h [, u0, v0, u1, v1 [, tr, tg, tb, ta]]) | 在面板里画纹理,可指定 UV 范围和着色 |
PlotLines(label, {values} [, w, h]) | 折线图,values 是数值数组 |
PlotHistogram(label, {values} [, w, h]) | 柱状图 |
local tex = imgui.CreateTexture(256, 256, 0) -- 棋盘格
imgui.Image(tex, 200, 200)
imgui.PlotLines("负载", {3, 7, 2, 9, 5, 6})
12. 样式 / 截图开关 已支持iOS 已支持
| 函数 | 说明 |
|---|---|
PushStyleColor(idx, "#RRGGBB" / "#RRGGBBAA") | 压入颜色覆盖,idx 用 imgui.Col.*(如 imgui.Col.Button) |
PushStyleColor(idx, r, g, b [, a]) | 同上,用 0~1 分量 |
PopStyleColor([n]) | 弹出 n 层(默认 1) |
PushStyleVar(idx, v) / PushStyleVar(idx, x, y) | 压入样式变量:vec2 型(ItemSpacing/FramePadding…)传两个数,其余传一个 |
PopStyleVar([n]) | 弹出 n 层 |
SetSecure([on]) | 截图开关,三态(与安卓同签名):true / false / 无参或传 nil = 只查询不修改,三种形态都返回当前态。⚠️ iOS 目前只记录这个状态,不会真的把面板从系统截图里排掉(安卓端才会跳过截图)。iOS 上要「截图里没有面板」,请在截图前 imgui.Shutdown() |
-- 单色按钮 + 自定义间距
imgui.PushStyleColor(imgui.Col.Button, "#3A7BD5")
imgui.PushStyleVar(imgui.StyleVar.FramePadding, 10, 8)
if imgui.Button("蓝色按钮") then end
imgui.PopStyleVar()
imgui.PopStyleColor()
-- 截图保护开关
if imgui.Button("切换截图保护") then
imgui.SetSecure(not imgui.SetSecure()) -- 无参那次只查询
end
imgui.SameLine()
imgui.TextDisabled(imgui.SetSecure() and "当前:截图跳过面板" or "当前:截图包含面板")
13. 常量表 已支持iOS 已支持
全部挂在 imgui 下的子表,不用记数字:
| 常量表 | 用途 / 常用值 |
|---|---|
imgui.Col(61) | 颜色槽位:Text Button ButtonHovered WindowBg FrameBg TitleBg CheckMark SliderGrab … |
imgui.Win(30) | 窗口标志:NoMove NoResize NoCollapse NoTitleBar NoScrollbar AlwaysAutoResize MenuBar … |
imgui.Cond(6) | None Always Once FirstUseEver Appearing |
imgui.StyleVar(35) | FramePadding ItemSpacing WindowPadding WindowRounding FrameRounding … |
imgui.Dir(7) | Left Right Up Down None |
imgui.MouseButton(5) | Left Right Middle |
imgui.Slider(11) | 滑块标志:Logarithmic NoInput WrapAround AlwaysClamp … |
imgui.TreeNodeFlags(21) | DefaultOpen Leaf NoIndent SpanAvailWidth … |
| 其它 | SelectableFlags(9) TabBar(13) TabItem(11) Popup(13) ComboFlags(11) Child(11) InputTextFlags(26) Hovered(21) ColorEdit(32) Focused(7) Table(38) TableColumn(29) |
⚠️ imgui.Table.* 等常量已齐备,但表格函数(BeginTable/TableNextRow…)本期未绑定,需要表格布局请用 SameLine() + 固定宽度控件自行排列。
14. 完整实例 已支持iOS 已支持
一个可直接跑的控制面板:状态栏 + 两个页签 + 按钮/勾选/滑块/输入框/下拉/进度条/折叠区/树节点/截图开关。注意状态都用 Lua 变量自己存。
-- ============================================================
-- imgui 完整实例:脚本控制面板
-- · 即时模式:每帧重画,控件返回值就是事件
-- · 所有状态自己用 Lua 变量存,每帧回填
-- ============================================================
-- ① 用户状态(脚本自己的变量,不是 imgui 的对象)
local cfg = {
enable = true, -- 勾选
speed = 1.5, -- 滑块
name = "默认配置", -- 输入框
mode = 1, -- 下拉(下标 1 起)
}
local modes = { "快速", "标准", "精细" } -- 下拉项(放外面,别每帧新建表)
local clickCount = 0
local logText = "就绪"
-- ② 建图层并等就绪(异步,最多等 5 秒)
imgui.Init()
local waited = 0
while not imgui.Ready() and waited < 5000 do
sleep(50)
waited = waited + 50
end
if not imgui.Ready() then
print("imgui 图层就绪超时")
return
end
logText = "图层就绪"
-- ③ 帧循环
while imgui.Running() do
if imgui.NewFrame() then
-- 仅首次生效:之后记住用户拖到的位置/尺寸
imgui.SetNextWindowPos(40, 300, imgui.Cond.FirstUseEver)
imgui.SetNextWindowSize(620, 800, imgui.Cond.FirstUseEver)
-- 第二参 true = 显示关闭按钮;返回 可见, 还开着
local visible, open = imgui.Begin("脚本控制面板", true)
if visible then
if imgui.BeginTabBar("mainTabs") then
-- ---------- 页签一:基本 ----------
if imgui.BeginTabItem("基本") then
imgui.Text("引擎版本 1.0.9")
imgui.TextDisabled("灰色文字适合放提示")
imgui.Separator()
-- 按钮:返回 true = 本帧被点击。label 固定,次数用 Text 显示
if imgui.Button("点我 +1", 140, 0) then
clickCount = clickCount + 1
logText = "点击次数: " .. clickCount
end
imgui.SameLine()
imgui.Text("次数: " .. clickCount)
-- 勾选:传入旧值,返回新值
cfg.enable = imgui.Checkbox("启用功能", cfg.enable)
-- 滑块:返回拖动后的实时值
cfg.speed = imgui.SliderFloat("速度", cfg.speed, 0.1, 5.0)
imgui.ProgressBar(cfg.speed / 5.0,
string.format("%.0f%%", cfg.speed / 5.0 * 100))
-- 输入框:点击自动弹引擎软键盘
cfg.name = imgui.InputText("名称", cfg.name)
-- 下拉:下标从 1 开始返回
cfg.mode = imgui.Combo("模式", cfg.mode, modes)
imgui.TextDisabled("当前模式: " .. modes[cfg.mode])
imgui.EndTabItem()
end
-- ---------- 页签二:高级 ----------
if imgui.BeginTabItem("高级") then
-- 折叠区(展开状态 imgui 自己记,不用存变量)
if imgui.CollapsingHeader("调试选项") then
if imgui.Button("清空日志") then logText = "" end
imgui.SameLine()
if imgui.Button("写一条日志") then logText = "手动写入一条" end
end
-- 树节点:展开时才需要 TreePop
if imgui.TreeNode("详细信息") then
imgui.BulletText("速度: " .. string.format("%.2f", cfg.speed))
imgui.BulletText("模式: " .. modes[cfg.mode])
imgui.BulletText("启用: " .. tostring(cfg.enable))
imgui.TreePop()
end
imgui.Separator()
-- 截图开关:SetSecure() 无参=只查询,不改变状态
if imgui.Button("切换截图保护") then
imgui.SetSecure(not imgui.SetSecure())
end
imgui.SameLine()
imgui.TextDisabled(imgui.SetSecure() and "截图跳过本面板" or "截图包含本面板")
-- 图表
imgui.PlotLines("示例曲线", { 3, 7, 2, 9, 5, 6, 8 })
imgui.EndTabItem()
end
imgui.EndTabBar()
end
imgui.Separator()
imgui.TextWrapped("日志: " .. logText)
end
imgui.End() -- ⚠️ 必须在 if 外面:折叠时 Begin 返回 false,但配对不能少
imgui.Render() -- 帧结束,渲染上屏
-- 点 X 关掉面板就退出循环
if not open then break end
end
sleep(16) -- ≈60fps
end
imgui.Shutdown() -- 收图层
可用参数速查:Begin 第二参 true 才有 X 关闭按钮;Combo/ListBox 下标 1 起;Checkbox/Slider*/Input* 都是「传旧值、返回新值」,别忘了写回变量。
uiacc 节点
1. 根据节点的id属性匹配 (id) 已支持
说明: 根据节点的id属性匹配
函数: id(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的具体控件的id |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = id("com.luatouch.app:id/detail_tv_title")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
2. 根据id包含的部分字符串匹配 (idContains) 已支持
说明: 根据id包含的部分字符串匹配(正则 :match)
函数: idContains(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的模糊的包含id某一段字符串的内容 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = idContains("detail_tv_title")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
3. 根据id的前缀去匹配 (idStartsWith) 已支持
说明: 根据id的前缀去匹配(正则 :match('^' .. str))
函数: idStartsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的id的前缀 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = idStartsWith("com.luatouch.app")
local node = sel:findOne(10000)
if node ~= nil then
print(node:id())
end
4. 根据id的后缀去匹配 (idEndsWith) 已支持
说明: 根据id的后缀去匹配(正则 :match(str .. '$'))
函数: idEndsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的id的后缀 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = idEndsWith("detail_tv_title")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
5. 正则匹配id (idMatches) 已支持
说明: 正则匹配id
函数: idMatches(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 正则表达式字符串 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = idMatches(".*detail_tv_title$")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
6. 根据节点的text属性匹配 (text) 已支持
说明: 根据节点的text属性匹配
函数: text(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的具体控件的text |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = text("LuaTouch高级版")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
7. 根据text包含的部分字符串匹配 (textContains) 已支持
说明: 根据text包含的部分字符串匹配(正则 :match)
函数: textContains(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的模糊的包含text某一段字符串的内容 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = textContains("LuaTouch")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
8. 根据text的前缀去匹配 (textStartsWith) 已支持
说明: 根据text的前缀去匹配(正则 :match('^' .. str))
函数: textStartsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的text的前缀 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = textStartsWith("LuaTouch")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
9. 根据text的后缀去匹配 (textEndsWith) 已支持
说明: 根据text的后缀去匹配(正则 :match(str .. '$'))
函数: textEndsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的text的后缀 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = textEndsWith("高级版")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
10. 正则匹配text (textMatches) 已支持
说明: 正则匹配text
函数: textMatches(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 正则表达式字符串 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = textMatches(".*高级版$")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
11. 根据节点的desc属性匹配 (desc) 已支持
说明: 根据节点的desc属性匹配
函数: desc(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的具体控件的desc |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = desc("LuaTouch高级版")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
12. 根据desc包含的部分字符串匹配 (descContains) 已支持
说明: 根据desc包含的部分字符串匹配(正则 :match)
函数: descContains(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的模糊的包含desc某一段字符串的内容 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = descContains("LuaTouch")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
13. 根据desc的前缀去匹配 (descStartsWith) 已支持
说明: 根据desc的前缀去匹配(正则 :match('^' .. str))
函数: descStartsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的desc的前缀 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = descStartsWith("LuaTouch")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
14. 根据desc的后缀去匹配 (descEndsWith) 已支持
说明: 根据desc的后缀去匹配(正则 :match(str .. '$'))
函数: descEndsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的desc的后缀 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = descEndsWith("高级版")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
15. 正则匹配desc (descMatches) 已支持
说明: 正则匹配desc
函数: descMatches(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 正则表达式字符串 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = descMatches(".*高级版$")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
16. 根据节点的className属性匹配 (className) 已支持
说明: 根据节点的className属性匹配
函数: className(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找的具体控件的类名 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = className("AXStaticText")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
17. 根据className属性所包含的字符串模糊匹配 (classNameContains) 已支持
说明: 根据className属性所包含的字符串模糊匹配(正则 :match)
函数: classNameContains(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找className包含该字符的节点 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = classNameContains("Text")
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
18. 根据className属性前缀匹配 (classNameStartsWith) 已支持
说明: 根据className属性前缀匹配(正则 :match('^' .. str))
函数: classNameStartsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找className前缀为该字符的节点 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = classNameStartsWith("AX")
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
19. 根据className属性后缀匹配 (classNameEndsWith) 已支持
说明: 根据className属性后缀匹配(正则 :match(str .. '$'))
函数: classNameEndsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要查找className后缀为该字符的节点 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = classNameEndsWith("StaticText")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
20. 根据className属性正则匹配 (classNameMatches) 已支持
说明: 根据className属性正则匹配
函数: classNameMatches(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要匹配className的正则表达式 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = classNameMatches("^AX.*Text$")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
21. 根据packageName属性全字段匹配 (packageName) 已支持
说明: 根据packageName属性全字段匹配
函数: packageName(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 指定要匹配节点所属的包名 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = packageName("com.apple.mobilesafari"):className("AXStaticText")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
22. 匹配包含指定字符串的节点 (packageNameContains) 已支持
说明: 匹配包含指定字符串的节点(正则 :match)
函数: packageNameContains(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 包含该字符串的包名的节点将被匹配到 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = packageNameContains("apple"):className("AXStaticText")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
23. 匹配包名前缀为指定字符串的节点 (packageNameStartsWith) 已支持
说明: 匹配包名前缀为指定字符串的节点(正则 :match('^' .. str))
函数: packageNameStartsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 包名为该字符的前缀的节点将被匹配到 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = packageNameStartsWith("com.apple"):className("AXStaticText")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
24. 匹配包名后缀为指定字符串的节点 (packageNameEndsWith) 已支持
说明: 匹配包名后缀为指定字符串的节点(正则 :match(str .. '$'))
函数: packageNameEndsWith(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 包名为该字符的后缀的节点将被匹配到 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = packageNameEndsWith("mobilesafari"):className("AXStaticText")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
25. 包名正则匹配 (packageNameMatches) 已支持
说明: 包名正则匹配
函数: packageNameMatches(str)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| str | string | 是 | 根据这个正则字符串匹配所有符合规则包名的节点 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = packageNameMatches("com.apple.*"):className("AXStaticText")
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
26. 根据节点的范围匹配 (bounds) 已支持
说明: 根据节点的范围匹配(精确匹配 bounds 坐标)
函数: bounds(l, t, r, b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| l | number | 是 | 左范围 |
| t | number | 是 | 上范围 |
| r | number | 是 | 右范围 |
| b | number | 是 | 下范围 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = bounds(30, 212, 236, 250)
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
27. 匹配该范围内的节点 (boundsInside) 已支持
说明: 匹配 bounds 完全包含在指定范围内的节点
函数: boundsInside(l, t, r, b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| l | number | 是 | 左范围 |
| t | number | 是 | 上范围 |
| r | number | 是 | 右范围 |
| b | number | 是 | 下范围 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = boundsInside(30, 212, 236, 250)
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
28. 根据绘制顺序匹配 (drawingOrder) 已支持
说明: 根据绘制顺序匹配
函数: drawingOrder(level)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| level | number | 是 | 绘制顺序级别 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = drawingOrder(1)
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
29. 根据深度索引匹配 (depth) 已支持
说明: 根据深度索引匹配
函数: depth(level)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| level | number | 是 | 深度索引级别 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = depth(1)
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
30. 根据在当前父节点里面的索引顺序查找 (index) 已支持
说明: 根据在当前父节点里面的索引顺序查找
函数: index(level)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| level | number | 是 | 索引级别 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = index(1)
local node = sel:findOne(10000)
if node ~= nil then
print(node:text())
end
31. 根据是否可见匹配 (visibleToUser) 已支持
说明: 根据是否可见匹配
函数: visibleToUser(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否可见 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = visibleToUser(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
32. 根据是否选中匹配 (selected) 已支持
说明: 根据是否选中匹配
函数: selected(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否选中 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = selected(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
33. 根据是否可点击匹配 (clickable) 已支持
说明: 根据是否可点击匹配
函数: clickable(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否可点击 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = clickable(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
34. 根据是否可长按点击匹配 (longClickable) 已支持
说明: 根据是否可长按点击匹配
函数: longClickable(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否可长按点击 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = longClickable(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
35. 根据是否可用匹配 (enabled) 已支持
说明: 根据是否可用匹配
函数: enabled(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否可用 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = enabled(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
36. 根据是否是密码框匹配 (password) 已支持
说明: 根据是否是密码框匹配
函数: password(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否是密码框 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = password(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
37. 根据是否可以滚动来匹配 (scrollable) 已支持
说明: 根据是否可以滚动来匹配
函数: scrollable(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否可以滚动 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = scrollable(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
38. 根据是否被勾选来匹配 (checked) 已支持
说明: 根据是否被勾选来匹配
函数: checked(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否被勾选 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = checked(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
39. 根据是否可以被勾选来匹配 (checkable) 已支持
说明: 根据是否可以被勾选来匹配
函数: checkable(b)
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| b | boolean | 是 | 是否可以被勾选 |
| 返回值类型 | 说明 |
|---|---|
| selector | 返回一个选择器对象,该对象支持级联选择 |
local sel = checkable(true)
local node = sel:findOne(10000)
if node ~= nil then
print(node:className())
end
findOne(timeoutMs) 已支持iOS 已支持
查找第一个匹配节点,未找到返回 nil。
一次调用只采集一次节点树(不是内部循环轮询):引擎会拉起一次节点采集并等待结果,拿到后在结果里做匹配。所以「等某个元素出现」需要自己写成循环(见下方示例),或用 exists()。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| timeoutMs | int | 否 | 等待本次采集完成的上限毫秒数,默认 10000;传 0 或负数会按 3000 处理(不会变成“立即返回”) |
| 返回值类型 | 说明 |
|---|---|
| node | 匹配的节点对象;未找到 / 采集超时 / 采集失败均返回 nil |
- 采集超时(
timeoutMs到点)会终止本次采集并返回nil - 同一时刻只允许一次采集:上一次还没结束时再调用,会直接返回
nil(不会排队等待) - 采集过程中子进程要先把「屏幕中心点采样」重试到成功(最坏约 50 次 × 50ms ≈ 2.5 秒),因此
timeoutMs给得过小(如 1000)可能在产出前就被截断;建议 ≥ 3000
local node = text("LuaTouch"):findOne(5000)
if node then
print("找到", node:text(), node:bounds())
end
-- findOne 不会自己重试,需要“等元素出现”时自己轮询:
local function waitText(t, ms)
local deadline = axTimeMs() + ms
while axTimeMs() < deadline do
local n = t:findOne(3000)
if n then return n end
axSleep(300)
end
return nil
end
local btn = waitText(textContains("确定"), 10000)
find(timeoutMs) 已支持iOS 已支持
查找所有匹配节点,返回数组(顺序 = 节点树中的先后顺序);未找到返回空表 {}。
与 findOne 一样是一次采集:采集一次节点树,然后在结果里收集全部匹配节点。
| 参数名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| timeoutMs | int | 否 | 等待本次采集完成的上限毫秒数,默认 10000;传 0 或负数按 3000 处理 |
| 返回值类型 | 说明 |
|---|---|
| node[] | 匹配的节点数组;未找到 / 采集超时 / 采集失败均返回空表 {} |
local ns = textContains("确定"):find(3000)
print("找到", #ns, "个")
for i, n in ipairs(ns) do
print(i, n:text(), n:bounds())
end
findOnce(timeoutMs) / exists(timeoutMs) 已支持iOS 已支持
选择器上的两个便捷方法(同样是一次采集):
| 方法 | 返回 | 说明 |
|---|---|---|
sel:findOnce(t) | node | nil | findOne 的别名,行为完全一致(默认 10000ms) |
sel:exists(t) | boolean | 是否存在匹配节点,等价于 findOne(t) ~= nil;只关心“有没有”时更直观 |
-- 等按钮出现:自己轮询(每次采集一次,间隔 300ms)
local function waitExists(sel, ms)
local deadline = axTimeMs() + ms
while axTimeMs() < deadline do
if sel:exists(3000) then return true end
axSleep(300)
end
return false
end
if waitExists(textContains("确定"), 10000) then
local n = textContains("确定"):findOnce(3000)
local l, t, r, b = n:bounds()
local scale = 3 -- ★ iOS 屏幕倍数(测试机均为 3)
tap((l + r) / 2 * scale, (t + b) / 2 * scale)
end
节点方法
通过 findOne / find 拿到的节点对象是属性读取器:每个方法读取该节点对应的属性并返回,不参与匹配。属性不存在时不会报错——字符串返回 ""、数值返回 0、布尔返回 false。
| 方法 | 返回类型 | 说明 |
|---|---|---|
n:text() | string | 节点显示文本 |
n:id() | string | 节点唯一标识(accessibilityIdentifier) |
n:desc() | string | 无障碍描述 |
n:className() | string | 节点类名 |
n:packageName() | string | 所属 App Bundle ID |
n:bounds() | l,t,r,b | 节点矩形坐标(4 个返回值,逻辑点——iOS 无障碍点坐标,与安卓不同) |
n:clickable() | boolean | 是否可点击 |
n:enabled() | boolean | 是否可用 |
n:focusable() | boolean | 是否可获得焦点 |
n:focused() | boolean | 当前是否已聚焦 |
n:visibleToUser() | boolean | 是否可见 |
n:selected() | boolean | 是否选中 |
n:checkable() | boolean | 是否可勾选 |
n:checked() | boolean | 是否已勾选 |
n:scrollable() | boolean | 是否可滚动 |
n:longClickable() | boolean | 是否可长按 |
n:password() | boolean | 是否是密码框 |
n:depth() | int | 节点深度 |
n:index() | int | 在父节点中的索引 |
n:drawingOrder() | int | 绘制顺序 |
local n = text("LuaTouch"):findOne(5000)
if n then
print("text =", n:text())
print("class =", n:className())
print("clickable =", n:clickable())
local l, t, r, b = n:bounds()
print("bounds =", l, t, r, b)
end
链式组合(多条件 AND)
用 : 链式串联多个 selector,节点必须同时满足所有条件才匹配。
-- 同时满足:文本包含"确定" 且 可见 且 可点击
local n = textContains("确定"):visibleToUser(true):clickable(true):findOne(5000)
-- 包名 + 类名组合
local n2 = packageName("com.apple.mobilesafari"):className("AXStaticText"):findOne(3000)
点击节点
节点对象没有 click() 方法。标准做法:取节点 bounds 的中心 → 调用引擎 tap(x, y)。
⚠ 坐标系差异(iOS 与安卓不同):bounds() 返回的是逻辑点(iOS 无障碍点坐标),而 tap / getScreenSize() 用的是物理像素——iOS 上必须乘屏幕倍数 scale 换算(安卓的 bounds 与 tap 同坐标系,不需要换算,安卓文档写法勿套用到 iOS):
local n = text("通讯录"):className("UITabBarButton"):findOne(5000)
if n then
local l, t, r, b = n:bounds()
local scale = 3 -- 屏幕倍数(测试机均为 3)
local cx, cy = (l + r) / 2 * scale, (t + b) / 2 * scale
tap(cx, cy)
end
底层 API
| 函数 | 返回 | 说明 |
|---|---|---|
axGetXml(timeout?) | string | 直接取一次节点树 XML 字符串。默认 3000ms;timeout ≤ 0 按 3000 处理。失败/超时返回空字符串 "",不是 nil。同一时刻只允许一次采集,重入时也返回 ""。findOne / find 内部就是调它,一般不用自己调 |
axSleep(ms?) | 无 | 阻塞等待指定毫秒,默认 100。属于无障碍模块的休眠,不响应脚本的暂停/停止——脚本里请优先用全局 sleep(),需要与节点采集节奏对齐时才用 axSleep |
axTimeMs() | int | 当前时间戳,单位毫秒(Unix epoch)。用于算耗时/超时截止时间 |
旧版 axGetTree / axDumpAttrs 已删除(不再整树转 Lua 表;属性改由节点对象的方法按需读取)。
-- 取原始节点树 XML(调试用)
local xml = axGetXml(3000)
if xml ~= "" then -- 注意:失败返回空字符串,不是 nil
print("XML 长度:", #xml)
else
print("采集失败/超时")
end
-- 用 axTimeMs 自己控制等待时长
local deadline = axTimeMs() + 5000
while axTimeMs() < deadline do
if textContains("确定"):exists(3000) then break end
axSleep(300)
end
注意事项
- 前台切换:节点树会随前台 App 变化,切换后建议先
findOne(5000)等新树稳定。 - 正则引擎:Contains / StartsWith / EndsWith / Matches 内部全用 Lua
string.match,支持 Lua 正则语法(非 PCRE)。 - 精确匹配:
text("A")是精确匹配;模糊匹配用textContains/textMatches。 - 性能:取树 12 个窗口 ~3000+ 节点,单次 ~50ms。建议
findOne+ 短超时。