MaaAssistantArknights 任务流程协议详解:resource/tasks 字段、虚任务表达式与 Schema 校验
【免费下载链接】MaaAssistantArknights《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients.项目地址: https://gitcode.com/GitHub_Trending/ma/MaaAssistantArknights
本文详解 MaaAssistantArknights(Maa)中resource/tasks任务配置的完整协议:全部字段含义与默认值、四类识别算法的专用参数、任务列表表达式(@#*+^)的运算规则、模板任务与虚任务的继承机制,以及如何结合 JSON Schema 在编辑器中做配置校验。读完后你将能够读懂乃至独立编写 Maa 的tasks.json任务流,理解next/sub/exceededNext等控制流字段的展开逻辑,并在执行中通过lazy_parse与set_task_base动态改写任务行为。
一、任务配置的基本模型
Maa 的自动任务由resource/tasks目录下的 JSON 文件定义:每个任务是一个以任务名(如"TaskName")为键的对象,运行时按“识别(algorithm)→ 动作(action)→ 流转(sub / next / exceededNext / onErrorNext)”的顺序推进。整份任务图的骨架可以概括为:
{ "TaskName": { // 任务名称,带 @ 时可能为特殊任务,字段默认值会有不同 "baseTask": "xxx", // 以 xxx 任务为模板产生任务 "algorithm": "MatchTemplate", // 选填,辨识算法类型,不填写时默认为 MatchTemplate // - JustReturn: 不进行辨识,直接执行 action // - MatchTemplate: 比对图片 // - OcrDetect: 文字辨识 // - FeatureMatch: 特征比对 "action": "ClickSelf", // 选填,辨识到后的动作,不填写时默认为 DoNothing // - ClickSelf: 点击辨识到的位置(目标范围内随机一点) // - ClickRect: 点击 specificRect 指定区域(不建议使用) // - DoNothing: 什么都不做 // - Stop: 停止目前任务 // - Swipe: 滑动,对应 specificRect 与 rectMove 字段 // - Input: 输入文字,要求 algorithm 为 JustReturn,对应 inputText "sub": ["SubTaskName1", "SubTaskName2"], // 选填,子任务,不推荐使用。会在执行完目前任务后,依序执行每一个子任务 // 可以套娃(子任务再套子任务),但要注意不要写出无穷循环 "subErrorIgnored": true, // 选填,是否忽略子任务的错误,不填写默认 false // false 时子任务出错则不继续执行后续任务;true 时子任务出错没有影响 "next": ["OtherTaskName1", "OtherTaskName2"], // 选填,执行完目前任务和 sub 任务后,下一个要执行的任务 // 会从前往后依序辨识,执行第一个比对成功的 // 不填写默认执行完目前任务直接停止 // 对相同任务,第一次辨识后第二次就不再辨识: // "next": [ "A", "B", "A", "A" ] -> "next": [ "A", "B" ] // 不允许 JustReturn 型任务位于非最后一项 "maxTimes": 10, // 选填,该任务最大执行次数,不填写时默认为无穷大 // 达到最大次数后,若存在 exceededNext 字段则执行 exceededNext;否则直接任务停止 "exceededNext": ["OtherTaskName1", "OtherTaskName2"], // 选填,达到最大执行次数后要执行的任务 // 不填写时达到上限则停止;填写后就执行这里的,而不是 next 里的 "onErrorNext": ["OtherTaskName1", "OtherTaskName2"], // 选填,执行出错时,后续要执行的任务 "preDelay": 1000, // 选填,辨识到后延迟多久才执行 action,单位毫秒;默认 0 "postDelay": 1000, // 选填,action 执行完后延迟多久才去辨识 next,单位毫秒;默认 0 "roi": [0, 0, 1280, 720], // 选填,辨识范围,格式 [ x, y, width, height ] // 以 1280 * 720 为基准自动缩放;不填写时默认 [ 0, 0, 1280, 720 ] // 尽量填写,减小辨识范围可以减少效能消耗,加快辨识速度 "cache": false, // 选填,是否使用快取,默认 false // 开启后第一次辨识到目标时,以后永远只在第一次辨识到的位置进行辨识,可大幅节省效能 // 仅适用于待辨识目标位置完全不会变的任务 "rectMove": [0, 0, 0, 0], // 选填,辨识后的目标移动,不建议使用。以 1280 * 720 为基准自动缩放 // 例如辨识到 A,但实际要点 A 下方 10 像素 5*2 区域内的某位置, // 可填 [ 0, 10, 5, 2 ];可以的话尽量直接辨识要点击的位置 // 当 action 为 Swipe 时有效且必填,表示滑动终点 "reduceOtherTimes": ["OtherTaskName1", "OtherTaskName2"], // 选填,执行后减少其他任务的执行计数 // 例如执行了使用理智药,说明上一次点蓝色开始按钮没生效,所以蓝色开始要 -1 "specificRect": [100, 100, 50, 50], // action 为 ClickRect 时有效且必填,指定点击位置(范围内随机一点) // action 为 Swipe 时有效且必填,表示滑动起点。以 1280 * 720 为基准自动缩放 "specialParams": [int, ...], // 某些特殊辨识器需要的参数 // action 为 Swipe 时选填:[0] 为 duration,[1] 为额外滑动方向(0 不启用,1/2/3/4 为上/下/左/右), // [2]、[3] 为滑动轨迹缓入、缓出斜率,需乘 10 输入,默认均为 10 // 如需正常进入并缓出,建议 [2]、[3] 分别填 37, 1 "highResolutionSwipeFix": false, // 选填,是否启用高解析度滑动修复,默认 false // 现阶段应只有关卡导航未使用 unity 滑动方式时需要开启 } }注意:JSON 文件本身不支持注释,上述行内注释仅供理解参考,实际编写
tasks.json时请勿保留。
从源码看任务的加载与展开
上述配置最终由 TaskData 单例解析。从源码结构看,TaskData内部维护了三层映射(见 TaskData.h):
m_json_all_tasks_info:原始的 JSON 任务定义;m_raw_all_tasks_info:未展开虚任务的任务信息;m_all_tasks_info:已展开虚任务、可直接执行的任务信息。
对外提供get()(取已展开任务)、load()/lazy_parse()(加载配置)、set_task_base()(修改baseTask)等接口,并以inline static auto& Task = TaskData::get_instance();暴露为全局Task句柄——这正是后文“执行中更改任务”一节中示例代码里Task.get(...)的来源。
二、四类识别算法的专用字段
任务中algorithm之外的字段按算法分属不同“衍生类别参数”。这与 docs/maa_tasks_schema.json 中的oneOf结构(JustReturnTask/MatchTemplateTask/OcrDetectTask/FeatureMatchTask四种定义均allOf引用公共的BaseTask)一一对应。
MatchTemplate:模板比对
以下字段仅当algorithm为MatchTemplate(缺省值)时有效:
"template": "xxx.png", // 要比对的文件名,可为字串或字串列表,默认 "任务名称.png" // 範本圖可放在 template 及其子資料夾下,載入時遞迴搜尋 "templThreshold": 0.8, // 比對得分門檻,超過才認為辨識到;可為數字或數字列表,預設 0.8 "maskRange": [1, 255], // 灰階遮罩範圍,如將圖片不需辨識部分塗黑(灰階 0)並設 [1, 255] 即可忽略塗黑處 "method": "Ccoeff", // 範本比對演算法,可為列表,預設 Ccoeff // - Ccoeff: 對顏色不敏感,對應 cv::TM_CCOEFF_NORMED // - RGBCount: 依 colorScales 二值化後以 F1-score 計算 RGB 空間相似度,再與 Ccoeff 結果內積 // - HSVCount: 類似 RGBCount,顏色空間換為 HSV "nmsDistance": 0, // 多結果去重(NMS)半徑,單位像素;兩個命中位置橫縱座標差都小於該值時只留最高分, // 不填或 0 時按範本短邊的一半取值method為HSVCount或RGBCount时还需注意:
"colorScales": [ // 数色遮罩范围,此两种 method 下必填 [ [23, 150, 40], // 结构 [[lower1, upper1], [lower2, upper2], ...] [25, 230, 150] ] // 内层为 int 时是灰階,为 array<int,3> 时是三通道颜色; ... // 最外层代表不同颜色范围,待辨识区域为它们对应遮罩的联集 ], "colorWithClose": true, // 数色时是否先做闭运算处理遮罩(默认 true) // 闭运算可填补小黑点提高效果,但图中包含文字时建议 false "pureColor": false, // 为 true 时忽略範本比對得分,完全依赖颜色比對结果(默认 false) // 适用于颜色特征明显但模板比对效果不佳的场景,建议相应提高 templThresholdOcrDetect:文字辨识
以下字段仅当algorithm为OcrDetect时有效:
"text": [ "接管作戰", "代理指揮" ], // 必填项,要辨识的文字,任一匹配成功即认为辨识到 "ocrReplace": [ // 选填,针对常见辨识错误进行替换(支援正規表示式) [ "千員", "幹員" ], [ ".+擊幹員", "狙擊幹員" ] ], "fullMatch": false, // 是否全字比對(不能多字),預設 false // false 时子串即可:text 为 "开始",实际辨识到 "开始行动" 也算成功 "replaceFull": false, // ocrReplace 命中时是否替换整段文字,預設 false "withoutDet": false, // 是否不使用检测模型,預設 false "isAscii": false, // 要辨识的文字是否为 ASCII 字符,預設 falsewithoutDet为true时额外可用:
"useRaw": true, // 是否使用原圖比對,預設 true;false 時為灰階比對 "binThreshold": [140, 255], // 二值化灰階門檻值,預設 [140, 255] // 灰階值不在範圍內的像素視為背景,最終保留 [lower, upper] 區間像素作為文字前景注:docs/maa_tasks_schema.json 中
OcrDetectTask的required为["algorithm", "text"],并在useRaw/binThreshold的描述中标注了生效条件,可作为编辑器校验的依据。
JustReturn + Input:纯文字输入
algorithm为JustReturn且action为Input时:
"inputText": "A string text." // 必填项,要输入的文字內容Schema 中该组合通过条件校验强制inputText必填(BaseTask的allOf分支之一)。
FeatureMatch:特征点比对
以下字段仅当algorithm为FeatureMatch时有效:
"template": "xxx.png", // 要比對的圖片檔案名稱,預設 "任務名稱.png" "count": 4, // 比對特徵點的數量要求(門檻值),預設 4 "ratio": 0.6, // KNN 比對演算法的距離比值 [0 - 1.0],越大越寬鬆,預設 0.6 "detector": "SIFT", // 特徵點檢測器,預設 SIFT // 可選:SIFT / ORB / BRISK / KAZE / AKAZE / SURF // SIFT:複雜度高,具尺度、旋轉不變性,效果最好 // ORB:速度極快,具旋轉不變性,無尺度不變性 // BRISK:速度快,具尺度、旋轉不變性 // KAZE:適用於 2D/3D 圖像,具尺度、旋轉不變性 // AKAZE:速度較快,具尺度、旋轉不變性三、任务列表表达式
任务列表类型字段(sub、next、onErrorNext、exceededNext、reduceOtherTimes)的值支持表达式计算:
| 符号 | 含义 | 实例 |
|---|---|---|
@ | @型任务 | Fight@ReturnTo |
#(一元) | 虚任务 | #self |
#(二元) | 虚任务 | StartUpThemes#next |
* | 重复多个任务 | (ClickCornerAfterPRTS+ClickCorner)*10 |
+ | 任务列表合并(在 next 系列字段中同名任务只保留最靠前者) | A+B |
^ | 任务列表差(在前者但不在后者,顺序不变) | (A+A+B+C)^(A+B+D)(结果为C) |
运算符优先级为:#(一元) >@=#(二元) >*>+=^。
这一套词法在源码中由 TaskDataSymbol 实现:其Type枚举(At、Sharp、Mul、Add、Sub(即^)、LParen/RParen等)与symbol_repr_to_type映射表完整对应上表中的各符号;虚任务关键字则以SharpSub/SharpNext/SharpSelf/SharpBack/SharpNone等独立词元出现(src/MaaCore/Config/TaskData/TaskDataSymbol.h#L44-L65),说明表达式在解析阶段就被切分为带类型的符号流,再由TaskData的compile_raw_tasklist/compile_tasklist两阶段编译为最终任务列表。
四、特殊任务类型
4.1 模板任务
模板任务包括衍生任务与@型任务,其核心可理解为根据父任务修改字段的默认值。
衍生任务(baseTask)
存在字段baseTask的任务即衍生任务,baseTask对应的任务称为其父任务。规则:
- 若是模板比对任务,字段
template的默认值仍为"任务名称.png"; - 若字段
algorithm与父任务不同,则衍生类别参数不继承(只继承TaskInfo定义的参数); - 其余字段的默认值均为父任务对应字段。
隐式@型任务
存在任务"A"且所有任务文件中均未直接定义的形如"B@A"的任务即隐式@型任务,"A"称其父任务。规则:
- 任务列表类型字段(
sub、next、onErrorNext、exceededNext、reduceOtherTimes)的默认值为父任务对应字段直接增加B@前缀(如遇任务名称开头为#则增加B前缀); - 其余字段的默认值均为父任务对应字段(包括
template)。
显式@型任务
存在任务"A"且任务文件中直接定义了"B@A"时即显式@型任务。规则:
- 任务列表类型字段默认值为父任务对应字段增加
B@前缀(遇#开头增加B前缀); - 若是模板比对任务,
template默认值仍为"任务名称.png"; - 若
algorithm与父任务不同,衍生类别参数不继承; - 其余字段默认值为父任务对应字段。
4.2 虚任务(#型任务)
虚任务形如"#{sharp_type}"或"B#{sharp_type}",其中{sharp_type}可为none、self、back、next、sub、on_error_next、exceeded_next、reduce_other_times,可分指令虚任务(#none/#self/#back)与字段虚任务(#next等):
| 虚任务类型 | 含义 | 简单范例 |
|---|---|---|
none | 空任务,直接跳过 | "A": {"next": ["#none", "T1"]}被视为"A": {"next": ["T1"]};"A#none + T1"被视为"T1" |
self | 目前任务名称 | "A": {"next": ["#self"]}中的"#self"被视为"A";"B": {"next": ["A@B@C#self"]}中的"A@B@C#self"被视为"B" |
back | #前面的任务名称 | "A@B#back"被视为"A@B";"#back"直接出现则被跳过 |
next、sub等 | #前任务名称对应字段 | 以next为例:"A#next"被视为Task.get("A")->next;"#next"直接出现则被跳过 |
三个值得记住的备注:
"#none"一般配合模板任务增加前缀的特性使用,或用在字段baseTask中避免多文件继承不必要的字段;"XXX#self"与"#self"含义相同;- 当几个任务都有
"next": [ "#back" ]时,"T1@T2@T3"代表依序执行T3、T2、T1(层层回退到父任务)。
4.3 多文件任务
若后载入的任务文件(例如外服tasks.json,下称文件二)中定义了先载入文件(例如国服tasks.json,下称文件一)中已存在的同名任务:
- 文件二的任务没有
baseTask字段:直接继承文件一中同名任务的字段; - 文件二的任务有
baseTask字段:不继承文件一,而是直接覆盖。特别地,在没有模板任务时可用"baseTask": "#none"避免继承不必要的字段。
4.4 使用示例
衍生任务(baseTask)。假设:
"Return": { "action": "ClickSelf", "next": [ "Stop" ] }, "Return2": { "baseTask": "Return" }则"Return2"实际展开为:
"Return2": { "algorithm": "MatchTemplate", // 直接继承 "template": "Return2.png", // "任务名称.png" "action": "ClickSelf", // 直接继承 "next": [ "Stop" ] // 直接继承,与模板任务相比这里没有前缀 }@型任务。假设任务"A"含:
"A": { "template": "A.png", "next": [ "N1", "#back" ] }若"B@A"未被直接定义,其实际参数为:
"B@A": { "template": "A.png", "next": [ "B@N1", "B#back" ] }若"B@A"有定义"B@A": {},则实际参数为:
"B@A": { "template": "B@A.png", "next": [ "B@N1", "B#back" ] }虚任务。给定:
{ "A": { "next": ["N1", "N2"] }, "C": { "next": ["B@A#next"] }, "Loading": { "next": ["#self", "#next", "#back"] }, "B": { "next": ["Other", "B@Loading"] } }可得到:
Task.get("C")->next = { "B@N1", "B@N2" }; Task.get("B@Loading")->next = { "B@Loading", "Other", "B" }; Task.get("Loading")->next = { "Loading" }; Task.get_raw("B@Loading")->next = { "B#self", "B#next", "B#back" };对应源码中Task.get()返回已展开任务(m_all_tasks_info),而展开前的原始列表仍保留在m_raw_all_tasks_info中——这正是示例里get_raw与get结果不同的原因。
4.5 注意事项(表达式优先级陷阱)
若任务列表字段中定义了包含低优先级运算的任务,实际结果可能不符预期:
@与二元#的运算顺序特例{ "A": { "next": ["N0"] }, "B": { "next": ["A#next"] }, "C@A": { "next": ["N1"] } }此时
"C@B" -> next(即C@A#next)为[ "N1" ]而不是[ "C@N0" ]。@与+的运算顺序特例{ "A": { "next": ["#back + N0"] }, "B@A": {} }此时:
Task.get("A")->next = { "N0" }; Task.get_raw("B@A")->next = { "B#back + N0" }; Task.get("B@A")->next = { "B", "N0" }; // 注意不是 [ "B", "B@N0" ]事实上可反向利用该特性避免增加不必要的前缀,只需定义:
{ "A": { "next": ["#none + N0"] } }
五、执行中更改任务
Task.lazy_parse()可在执行中载入 JSON 任务配置文件,规则与上文多文件任务一节相同;Task.set_task_base()可修改任务的baseTask字段。
两者的 C++ 声明见 TaskData.h 中的bool lazy_parse(const json::value& json)与void set_task_base(const std::string& task_name, std::string base_task_name)。
示例:按模式切换任务基类
假设有任务配置文件:
{ "A": { "baseTask": "A_default" }, "A_default": { "next": ["xxx"] }, "A_mode1": { "next": ["yyy"] }, "A_mode2": { "next": ["zzz"] } }以下代码可根据mode的值改变任务"A",同时会改变其他依赖"A"的任务(例如"B@A"):
switch (mode) { case 1: Task.set_task_base("A", "A_mode1"); // 基本上相当于用 A_mode1 的内容直接替换 A,下同 break; case 2: Task.set_task_base("A", "A_mode2"); break; default: Task.set_task_base("A", "A_default"); break; }该机制在仓库中有大量真实用例:例如src/MaaCore/Task/Roguelike/RoguelikeConfig.cpp中通过Task.set_task_base(m_theme + "@Roguelike@Stages", m_theme + "@Roguelike@Stages_default")切换主题默认阶段,src/MaaCore/Task/Roguelike/BlackFlow/BlackFlowRoutingTaskPlugin.cpp中则按路线状态在多个BlackFlow@Roguelike@...基类之间来回切换,实现“同一入口任务名、运行时换行为”的效果。
六、Schema 校验
本仓库为tasks.json配置了 JSON Schema 校验,Schema 文件为 docs/maa_tasks_schema.json。其要点:
- 顶层
patternProperties以^(?!\$)匹配所有任务键,每个任务对象通过oneOf匹配四种算法任务定义之一; BaseTask中用if/then条件校验实现了字段联动约束:action为ClickRect时specificRect必填;action为Swipe时specificRect与rectMove均必填;algorithm为JustReturn且action为Input时inputText必填;MatchTemplateTask中templThreshold支持数组且注明“多模板时阈值数量需与模板数量一致”;OcrDetectTask将algorithm与text列为必填。
Visual Studio
在MaaCore.vcxproj中已对其完成设置,内建功能开箱即用。提示效果较为晦涩,且有部分信息缺失。
Visual Studio Code
在.vscode/settings.json中已对其完成设置,使用 Visual Studio Code 打开该项目文件夹即可使用,提示效果较好。
推荐配合 Maa Pipeline Support 扩展(提供模板预览、next跳转、任务引用查询、任务表达式展开计算等功能)实现高效编辑,扩展教程见 docs/zh-tw/develop/vsc-ext-tutorial.md。
七、编写任务时的实践建议
结合本文各节,日常编写tasks.json时可遵循:
- 控制流优先用
next表达:sub不推荐(仅用于必须串行且与主流程解耦的场合),next支持“取第一个识别成功者”的分支语义; - 缩小
roi:所有坐标字段以 1280×720 为基准自动缩放,填写更小的roi能减少性能消耗并加快识别; cache只给固定位置目标用:目标位置会变的任务保持默认false;- 善用
@型任务与虚任务替代重复配置,但注意第四节列出的优先级特例; - 多文件(国服/外服)维护时:外服覆盖用
baseTask显式声明,无父类时用"baseTask": "#none"切断继承; - 运行时动态行为用
set_task_base/lazy_parse实现,避免为每个分支复制整份任务图。
【免费下载链接】MaaAssistantArknights《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients.项目地址: https://gitcode.com/GitHub_Trending/ma/MaaAssistantArknights
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考