简介
TM助手是一款专业的移动开发辅助工具,提供强大的Lua脚本引擎和完善的设备授权管理系统。
- Android 7.0 及以上
- 需要网络连接
- 建议2GB以上内存
安装配置
1. 下载安装
从下载中心下载最新版本的TM助手APK文件,安装到Android设备上。
2. 注册登录
首次使用需要注册账号:
- 输入手机号
- 获取验证码
- 完成注册
3. 设备激活
联系管理员获取授权码,在应用中激活设备。
快速上手
第一个Lua脚本
创建一个简单的Hello World脚本:
-- hello.lua
print("Hello, TM助手!")
-- 显示Toast消息
toast("欢迎使用TM助手")
-- 获取设备信息
local deviceId = getDeviceId()
print("设备ID: " .. deviceId)
运行脚本
- 打开TM助手应用
- 在IDE中编写Lua脚本
- 点击"运行"按钮
- 查看日志输出
初始化模块 (TmInit)
TmInit是所有图色功能的核心模块,必须在使用其他图色模块前调用。它负责初始化屏幕捕获缓冲区。
基本用法
-- 初始化(竖屏模式)
local result = TmInit(0)
-- 初始化(横屏模式)
local result = TmInit(1)
-- 带调试模式(保存截图到文件)
local result = TmInit(0, true)
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmInit(orientation, saveDebug) |
orientation: 0=竖屏, 1=横屏 saveDebug: 是否保存调试截图(可选,默认false) |
table {success, width, height, message} | 初始化屏幕捕获,返回屏幕尺寸信息 |
TmInit 必须在脚本开头调用!所有图色功能(TmCmpColor、TmFindColor、TmTemplateMatch等)都依赖于此初始化。
示例:完整脚本模板
-- 1. 首先初始化(0=竖屏, 1=横屏)
local initResult = TmInit(0)
if not initResult.success then
print("初始化失败: " .. initResult.message)
return
end
print("屏幕尺寸: " .. initResult.width .. "x" .. initResult.height)
-- 2. 现在可以使用图色功能了
local result = TmFindColor("100,200,500,800", "#FF0000")
if result.found then
TmClick(result.points[1].x, result.points[1].y)
end
点击模块 (TmClick)
TmClick模块提供屏幕坐标点击功能,支持ROOT和无障碍服务两种模式。
基本用法
-- 点击屏幕坐标 (500, 1000)
TmClick(500, 1000)
-- 延迟后点击
TmSleep(1000) -- 等待1秒
TmClick(300, 800)
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmClick(x, y) |
x: 横坐标 y: 纵坐标 |
table {success, x, y, message} | 点击指定坐标,返回结果表 |
工作模式
- ROOT模式:使用
input tap命令,速度快,精度高 - 无障碍模式:使用AccessibilityService,无需ROOT权限
应用会自动检测设备是否ROOT,并选择最佳模式。建议在设置中手动选择模式以获得更好的性能。
示例:连续点击
-- 连续点击5次
for i = 1, 5 do
TmClick(500, 1000)
TmSleep(500) -- 每次点击间隔0.5秒
end
print("点击完成")
颜色比对模块 (TmCmpColor)
TmCmpColor模块用于比较屏幕指定坐标的颜色,常用于判断界面状态。
基本用法
-- 检查坐标 (100, 200) 的颜色是否为红色
local result = TmCmpColor(100, 200, "#FF0000")
if result.match then
print("颜色匹配")
else
print("颜色不匹配,实际颜色: " .. result.actualColor)
end
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmCmpColor(x, y, color) |
x: 横坐标 y: 纵坐标 color: 颜色值"#RRGGBB" |
table {success, match, x, y, expectedColor, actualColor, message} | 比较单点颜色 |
TmCmpColorMulti(pointsStr, tolerance) |
pointsStr: "x1,y1,#color1|x2,y2,#color2|..." tolerance: 容差(可选,默认10) |
table {success, allMatch, matchCount, totalCount, results, message} | 批量多点比色(一次截图检查多个点) |
返回值字段说明
success- 函数是否执行成功match- 颜色是否匹配expectedColor- 期望的颜色值actualColor- 实际检测到的颜色值
使用持久化帧缓冲区,截图在内存中完成,性能优异。脚本运行时帧缓冲区会自动维护。
颜色格式
颜色值使用十六进制格式:#RRGGBB
#FF0000- 红色#00FF00- 绿色#0000FF- 蓝色#FFFFFF- 白色#000000- 黑色
颜色容差
系统默认颜色容差为10(RGB每个通道),即实际颜色与期望颜色的RGB值相差在10以内都会被认为匹配。
使用前必须先调用 TmInit() 初始化屏幕捕获。非ROOT模式需要屏幕捕获权限。
示例:等待界面加载
-- 等待按钮变为绿色(表示可点击)
local maxWait = 10 -- 最多等待10秒
local waited = 0
while waited < maxWait do
local result = TmCmpColor(500, 1000, "#00FF00")
if result.match then
print("按钮已就绪")
TmClick(500, 1000)
break
end
TmSleep(500)
waited = waited + 0.5
end
if waited >= maxWait then
print("等待超时")
end
示例:多点批量比色(推荐)
-- 一次截图检查多个坐标的颜色(性能更好)
local result = TmCmpColorMulti("100,200,#FF0000|300,400,#00FF00|500,600,#0000FF")
if result.allMatch then
print("所有颜色都匹配!")
else
print("匹配数量: " .. result.matchCount .. "/" .. result.totalCount)
-- 遍历每个点的结果
for i, point in ipairs(result.results) do
if not point.match then
print("不匹配: (" .. point.x .. "," .. point.y .. ") 期望:" .. point.expectedColor .. " 实际:" .. point.actualColor)
end
end
end
范围找色模块 (TmFindColor)
TmFindColor模块用于在指定的ROI(感兴趣区域)内查找指定颜色的像素点,支持多颜色匹配和自定义容差。
基本用法
-- 在区域 (100,200,500,800) 内查找红色
local result = TmFindColor("100,200,500,800", "#FF0000")
if result.found then
local point = result.points[1]
print("找到颜色,坐标: " .. point.x .. ", " .. point.y)
TmClick(point.x, point.y)
else
print("未找到颜色")
end
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmFindColor(roi, color, count, useCache, tolerance) |
roi: 区域"x1,y1,x2,y2" color: 颜色值,支持单个"#RRGGBB"或多个"{#c1,#c2}" count: 需要匹配的数量(可选,默认1) useCache: 是否使用缓存截图(可选,默认false) tolerance: 颜色容差(可选,默认10) |
table {success, found, matchCount, requiredCount, color, points, message} | 在ROI区域内查找颜色 |
返回值字段说明
success- 函数是否执行成功found- 是否找到足够数量的匹配点matchCount- 实际匹配到的数量requiredCount- 需要匹配的数量points- 匹配点数组 [{x, y}, ...]
ROI格式
ROI(Region of Interest)使用逗号分隔的四个坐标:"x1,y1,x2,y2"
x1, y1- 左上角坐标x2, y2- 右下角坐标- 示例:
"0,0,1080,2400"- 全屏搜索(1080×2400分辨率) - 示例:
"100,200,500,800"- 局部区域
颜色格式
支持两种颜色格式:
- 单个颜色:
"#FF0000" - 多个颜色:
{"#FF0000","#00FF00","#0000FF"}或"#FF0000,#00FF00"
- 缩小搜索区域可以提高查找速度
- 使用
useCache=true可复用上次截图(同一帧多次找色) - 使用 C++ OpenCV 优化,比纯 Kotlin 快数倍
颜色容差
默认容差为10,可通过第5个参数自定义。容差范围 0-255,即 RGB 每个通道允许的差值。
示例:查找并点击按钮
-- 在屏幕下半部分查找绿色按钮
local result = TmFindColor("0,1200,1080,2400", "#00FF00")
if result.found then
local p = result.points[1]
print("找到绿色按钮: (" .. p.x .. ", " .. p.y .. ")")
TmClick(p.x, p.y)
TmSleep(1000)
print("已点击按钮")
else
print("未找到绿色按钮")
end
示例:多颜色匹配
-- 查找红色或绿色或蓝色(任意一个即可)
local result = TmFindColor("100,200,500,800", {"#FF0000", "#00FF00", "#0000FF"})
if result.found then
print("找到颜色: " .. result.color)
TmClick(result.points[1].x, result.points[1].y)
end
示例:自定义容差和缓存
-- 第一次找色:实时截图
local result1 = TmFindColor("100,200,500,800", "#FF0000", 1, false, 15)
-- 第二次找色:使用缓存截图(同一帧,速度更快)
local result2 = TmFindColor("100,200,500,800", "#00FF00", 1, true, 15)
示例:循环查找
-- 在30秒内不断查找目标颜色
local maxWait = 30
local waited = 0
while waited < maxWait do
local result = TmFindColor("100,200,500,800", "#FF0000")
if result.found then
local p = result.points[1]
print("找到目标,坐标: (" .. p.x .. ", " .. p.y .. ")")
TmClick(p.x, p.y)
break
end
print("未找到,继续搜索...")
TmSleep(1000)
waited = waited + 1
end
if waited >= maxWait then
print("查找超时")
end
- 使用前必须先调用
TmInit()初始化 - 非ROOT模式需要屏幕捕获权限
- 搜索区域坐标不能超出屏幕范围
模板匹配模块 (TmTemplateMatch)
TmTemplateMatch 使用 OpenCV 进行高精度模板匹配,适用于按钮识别、图标定位等场景。
基本用法
-- 在 ROI 区域内匹配"按钮组"模板
local result = TmTemplateMatch("100,200,500,800", "按钮组", 0.8)
if result.success and result.results then
for i, match in ipairs(result.results) do
if match.found then
print("匹配: " .. match.matchedName .. " 坐标: " .. match.globalX .. "," .. match.globalY)
TmClick(match.globalX, match.globalY)
end
end
end
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmTemplateMatch(roi, groupName, threshold, useCache, debugMode) |
roi: 区域"x1,y1,x2,y2" groupName: 模板组名称 threshold: 匹配阈值 0.0-1.0(可选,默认0.8) useCache: 使用缓存截图(可选,默认false) debugMode: 调试模式,绘制匹配框(可选,默认false) |
table {success, matchTimeMs, groupName, results, message} | 在ROI区域内匹配模板组 |
返回值字段说明
success- 匹配是否执行成功matchTimeMs- 匹配耗时(毫秒)results- 匹配结果数组,每个元素包含:found- 是否匹配成功matchX, matchY- ROI 内坐标globalX, globalY- 全局屏幕坐标matchWidth, matchHeight- 模板尺寸confidence- 置信度 0.0-1.0matchedName- 匹配的模板名称
模板管理
模板图片通过 TMide 创建和管理:
- 在 TMide 中截图
- 框选要识别的区域
- 保存为模板,指定组名
- 模板自动存储在项目的
script/{分辨率}/目录
- C++ OpenCV 原生加速,匹配速度 < 10ms
- 模板加载后自动缓存,后续调用无需重新加载
- 支持多线程并行匹配多个模板
延时模块 (TmSleep)
TmSleep 是智能延时函数,延时期间会自动暂停帧获取以节省系统资源。
基本用法
-- 等待 1 秒
TmSleep(1000)
-- 等待 0.5 秒
TmSleep(500)
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmSleep(ms) |
ms: 延时毫秒数 | 无 | 智能延时,自动暂停帧获取 |
TmSleep 会在延时期间暂停屏幕帧获取,减少 CPU 和内存占用。推荐在图色脚本中使用 TmSleep 替代普通 sleep。
轮廓检测模块 (TmFindContours)
TmFindContours 使用 OpenCV 检测屏幕上的轮廓,适用于识别按钮边框、图标轮廓等。
基本用法
-- 在 ROI 区域内检测轮廓
local result = TmFindContours("100,200,500,800")
if result.success then
print("找到 " .. result.count .. " 个轮廓")
for i, contour in ipairs(result.contours) do
print("轮廓" .. i .. ": x=" .. contour.x .. " y=" .. contour.y ..
" w=" .. contour.width .. " h=" .. contour.height)
end
end
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmFindContours(roi, minArea, maxArea) |
roi: 区域"x1,y1,x2,y2" minArea: 最小面积(可选) maxArea: 最大面积(可选) |
table {success, count, contours, message} | 检测ROI区域内的轮廓 |
悬浮网格窗口 (TmFloatingGrid)
TmFloatingGrid 模块提供悬浮网格窗口功能,可在屏幕上显示自定义的网格信息,支持文字和图片内容,适用于调试信息展示、状态监控等场景。
基本用法
-- 创建 3行4列 的网格窗口 (宽400px, 高300px, 居中显示)
local windowId = TmFloatingGridCreate(400, 300, 3, 4, 0, 0)
-- 显示窗口
TmFloatingGridShow(windowId)
-- 设置单元格文字 (第1行第1列, 红色文字)
TmFloatingGridSetText(windowId, 1, 1, "状态", "#FF0000", 16, "")
-- 设置单元格图片
TmFloatingGridSetImage(windowId, 2, 1, "images/icon.png")
-- 脚本结束时销毁窗口
TmFloatingGridDestroy(windowId)
函数说明
| 函数 | 参数 | 返回值 | 说明 |
|---|---|---|---|
TmFloatingGridCreate(width, height, rows, cols, x, y) |
width: 窗口宽度(px) height: 窗口高度(px) rows: 网格行数 cols: 网格列数 x, y: 窗口位置 (0,0 表示居中) |
windowId (字符串) | 创建悬浮网格窗口 |
TmFloatingGridSetText(windowId, row, col, text, textColor, textSize, bgColor) |
windowId: 窗口ID row, col: 行列号 (从1开始) text: 文字内容 textColor: 文字颜色 "#RRGGBB" textSize: 文字大小 (0=默认) bgColor: 背景色 (""=透明) |
boolean | 设置单元格文字 |
TmFloatingGridSetTexts(windowId, data) |
windowId: 窗口ID data: 批量数据字符串 格式: "row,col,text,color,size,bg;..." |
boolean | 批量设置多个单元格文字 |
TmFloatingGridSetImage(windowId, row, col, imagePath) |
windowId: 窗口ID row, col: 行列号 imagePath: 图片路径 (相对于项目目录) |
boolean | 设置单元格图片 |
TmFloatingGridSetImageBase64(windowId, row, col, base64Data) |
windowId: 窗口ID row, col: 行列号 base64Data: Base64编码的图片 |
boolean | 设置单元格图片 (Base64) |
TmFloatingGridSetRowHeight(windowId, row, height) |
windowId: 窗口ID row: 行号 height: 高度 (0=自动) |
boolean | 设置指定行的高度 |
TmFloatingGridSetColWidth(windowId, col, width) |
windowId: 窗口ID col: 列号 width: 宽度 (0=自动) |
boolean | 设置指定列的宽度 |
TmFloatingGridClearCell(windowId, row, col) |
windowId, row, col | boolean | 清空指定单元格 |
TmFloatingGridClearAll(windowId) |
windowId: 窗口ID | boolean | 清空所有单元格 |
TmFloatingGridShow(windowId) |
windowId: 窗口ID | boolean | 显示窗口 |
TmFloatingGridHide(windowId) |
windowId: 窗口ID | boolean | 隐藏窗口 (不销毁) |
TmFloatingGridDestroy(windowId) |
windowId: 窗口ID | boolean | 销毁窗口 |
TmFloatingGridDestroyAll() |
无 | 无 | 销毁所有窗口 |
TmFloatingGridMove(windowId, x, y) |
windowId, x, y | boolean | 移动窗口位置 |
TmFloatingGridResize(windowId, width, height) |
windowId, width, height | boolean | 调整窗口大小 |
特性说明
- 多窗口支持:可同时创建多个悬浮窗口,通过 windowId 区分
- 自动布局:默认情况下,行高和列宽自动均分窗口尺寸
- 自定义尺寸:可通过 SetRowHeight/SetColWidth 指定特定行列的尺寸
- 拖拽移动:窗口支持手指拖拽移动位置
- 自动清理:脚本停止时自动销毁所有悬浮窗口
- 调试信息实时展示
- 脚本运行状态监控
- 游戏数据统计面板
- 识别结果可视化展示
示例:状态监控面板
-- 创建 2行3列 的状态监控面板
local windowId = TmFloatingGridCreate(300, 100, 2, 3, 50, 50)
TmFloatingGridShow(windowId)
-- 设置标题行 (第一行固定高度)
TmFloatingGridSetRowHeight(windowId, 1, 30)
-- 设置标题
TmFloatingGridSetText(windowId, 1, 1, "状态", "#FFFFFF", 12, "#333333")
TmFloatingGridSetText(windowId, 1, 2, "次数", "#FFFFFF", 12, "#333333")
TmFloatingGridSetText(windowId, 1, 3, "耗时", "#FFFFFF", 12, "#333333")
-- 主循环中更新数据
local count = 0
while true do
count = count + 1
local startTime = os.clock()
-- 执行任务...
TmSleep(1000)
local elapsed = string.format("%.1fs", os.clock() - startTime)
-- 更新显示
TmFloatingGridSetText(windowId, 2, 1, "运行中", "#00FF00", 14, "")
TmFloatingGridSetText(windowId, 2, 2, tostring(count), "#FFFF00", 14, "")
TmFloatingGridSetText(windowId, 2, 3, elapsed, "#00FFFF", 14, "")
end
示例:批量设置文字
-- 使用批量设置提高效率
local windowId = TmFloatingGridCreate(400, 200, 2, 4, 0, 0)
TmFloatingGridShow(windowId)
-- 批量设置多个单元格 (格式: row,col,text,color,size,bg;...)
TmFloatingGridSetTexts(windowId,
"1,1,A,#FF0000,16,;1,2,B,#00FF00,16,;1,3,C,#0000FF,16,;1,4,D,#FFFF00,16,;" ..
"2,1,E,#FF00FF,16,;2,2,F,#00FFFF,16,;2,3,G,#FFFFFF,16,;2,4,H,#888888,16,"
)
悬浮窗口需要"显示在其他应用上层"权限(SYSTEM_ALERT_WINDOW)。首次使用时会自动请求权限。
更多模块
TM助手还提供了更多实用模块,持续更新中...
已支持的高级功能
YOLO 目标检测
TmYolo 模块支持自定义模型
模板匹配
OpenCV 高精度模板匹配
轮廓检测
识别UI元素边界
多点比色
一次截图批量检测多点颜色
多颜色找色
一次搜索多个目标颜色
HTTP/WebSocket
与IDE实时通信
悬浮网格窗口
屏幕信息展示面板
如果您有新的模块需求,欢迎通过邮件或社区反馈给我们。我们会根据需求优先级进行开发。
Lua基础
变量和数据类型
-- 变量定义
local name = "TM助手"
local version = 1.0
local isActive = true
-- 表(数组/字典)
local arr = {1, 2, 3, 4, 5}
local dict = {
name = "张三",
age = 25,
city = "杭州"
}
函数定义
-- 函数定义
function greet(name)
return "Hello, " .. name
end
-- 调用函数
local message = greet("TM助手")
print(message)
条件语句
local score = 85
if score >= 90 then
print("优秀")
elseif score >= 60 then
print("及格")
else
print("不及格")
end
循环语句
-- for循环
for i = 1, 10 do
print(i)
end
-- while循环
local count = 0
while count < 5 do
print(count)
count = count + 1
end
Lua API
基础API
| 函数 | 说明 | 示例 |
|---|---|---|
toast(message) |
显示Toast消息 | toast("Hello") |
sleep(ms) |
延迟执行(毫秒) | sleep(1000) |
getDeviceId() |
获取设备ID | local id = getDeviceId() |
log(message) |
输出日志 | log("Debug info") |
HTTP请求
-- GET请求
local response = http.get("https://api.example.com/data")
print(response.body)
-- POST请求
local data = {
name = "张三",
age = 25
}
local response = http.post("https://api.example.com/user", data)
print(response.statusCode)
文件操作
-- 读取文件
local content = file.read("/sdcard/test.txt")
print(content)
-- 写入文件
file.write("/sdcard/test.txt", "Hello World")
-- 检查文件是否存在
if file.exists("/sdcard/test.txt") then
print("文件存在")
end
示例代码
示例1:自动签到
-- 自动签到脚本
function autoCheckIn()
toast("开始自动签到")
-- 模拟点击签到按钮
click(500, 1000)
sleep(2000)
-- 检查签到结果
local result = getText()
if string.find(result, "签到成功") then
toast("签到成功!")
return true
else
toast("签到失败")
return false
end
end
-- 执行签到
autoCheckIn()
示例2:数据采集
-- 数据采集脚本
function collectData()
local data = {}
-- 采集数据
for i = 1, 10 do
local item = {
id = i,
title = getText(100, 200 + i * 100),
time = os.time()
}
table.insert(data, item)
sleep(500)
end
-- 保存数据
local json = JSON.encode(data)
file.write("/sdcard/data.json", json)
toast("数据采集完成")
end
collectData()
示例3:定时任务
-- 定时任务脚本
function scheduleTask()
while true do
local hour = os.date("%H")
-- 每天早上8点执行
if hour == "08" then
toast("开始执行任务")
-- 执行任务逻辑
doTask()
-- 等待1小时
sleep(3600000)
end
-- 每分钟检查一次
sleep(60000)
end
end
function doTask()
-- 任务逻辑
print("执行任务...")
end
scheduleTask()
WebSocket
TM助手内置WebSocket服务,用于IDE与设备之间的实时通信。
连接WebSocket
WebSocket服务地址:ws://设备IP:8081
消息格式
{
"type": "execute",
"code": "print('Hello')"
}
WebSocket主要用于IDE与设备的通信,一般情况下不需要手动处理。
HTTP服务
TM助手内置HTTP服务,用于接收IDE的脚本执行请求。
服务地址
HTTP服务地址:http://设备IP:8080
执行脚本
{
"code": "print('Hello World')"
}
调试技巧
日志输出
-- 使用print输出日志
print("调试信息")
-- 使用log函数
log("Debug: " .. tostring(value))
-- 输出表内容
function printTable(t)
for k, v in pairs(t) do
print(k .. " = " .. tostring(v))
end
end
错误处理
-- 使用pcall捕获错误
local success, error = pcall(function()
-- 可能出错的代码
local result = riskyOperation()
return result
end)
if not success then
print("错误: " .. error)
toast("操作失败")
end
性能优化
- 避免在循环中进行耗时操作
- 合理使用sleep()控制执行频率
- 及时释放不需要的资源
- 使用局部变量提高性能