- 桌面应用
- RPA
- 计算机视觉
【免费下载链接】ZenlessZoneZero-OneDragon
绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄
导读:本文以
docs/develop/screen_scope_design.md方案为主体,完整讲解 ZenlessZoneZero-OneDragon(绝区零一条龙)中 screen 匹配开销过大、插件无法自定义画面、全局跳转缓慢三个问题的根因,以及通过app_id字段实现「全局 screen + 局部 screen」作用域管理的设计思路、API、迁移步骤与插件 screen 加载机制。读完你既能掌握如何给现有 screen YAML 添加app_id完成局部化迁移,也能理解 BFS 匹配优化与 Floyd 路由表为什么不受影响,还能复现插件通过screen_info/目录注入自建画面的完整链路。
1. 背景与问题:为什么 screen 需要「作用域」
在绝区零一条龙项目中,画面(screen)是自动化流程识别的基础单元:每个 screen 由若干区域(area)组成,区域可以是 OCR 文本区域或模板匹配区域,id_mark区域全部命中才算匹配到该画面。历史实现中所有 screen 都注册在全局唯一的ScreenContext中,由此引出三个核心问题(设计方案原文):
| 问题 | 原因 | 影响 |
|---|---|---|
| 插件无法管理 screen | screen 只从 assets/game_data/screen_info/ 加载,无注入点 | 第三方插件无法定义画面 |
| 全局污染 | 所有 screen 同一命名空间,BFS 搜索和路由计算混杂不相关 screen | 识别变慢 |
| 全局跳转慢 | round_by_goto_screen无范围限制,BFS 可能检查大量无关 screen | 每轮匹配耗时高 |
从源码看,匹配开销的真实来源在 screen_utils.py:get_match_screen_name_from_last从当前/上一个画面出发做 BFS 扩散,每个节点都要走is_target_screen→find_area_in_screen,而 find_area_in_screen 内部要么调用 OCR 服务做文本识别(lcs_percent阈值默认 0.5),要么调用模板匹配(template_match_threshold默认 0.7)。画面越多,每轮截图被 OCR/模板匹配的次数越多,最坏情况与 screen 总数成正比。
2. 设计方案:全局 screen 与局部 screen
方案的核心概念是用app_id把 screen 划分为两类(设计图):
┌─────────────────────────────────────────────┐ │ ScreenContext │ │ ┌────────────────────┐ ┌───────────────┐ │ │ │ 全局 screen │ │ 局部 screen │ │ │ │ (app_id 为空) │ │ (app_id 非空) │ │ │ │ 菜单 │ │ 迷失之地-入口 │ │ │ │ 大世界-普通 │ │ 迷失之地-大世界│ │ │ │ 快捷手册-训练 │ │ 零号空洞-入口 │ │ │ │ ... │ │ ... │ │ │ └────────────────────┘ └───────────────┘ │ │ 活跃范围 = 全局 ∪ 当前应用的局部 │ └─────────────────────────────────────────────┘- 全局 screen:YAML 中
app_id为空。始终参与匹配,如菜单、大世界、快捷手册等导航骨架。 - 局部 screen:YAML 中
app_id非空。仅在对应应用运行时参与匹配。
作用域由三个时机自动维护,无需在代码中手工维护字符串列表:
reload()后自动计算全局集合:_global_screen_names = {无 app_id 的 screen};- 应用启动时
enter_scope(app_id),自动收集app_id匹配的局部 screen; - 应用停止时
exit_scope(),恢复全量匹配。
这三步在 screen_loader.py 有对应实现:reload()末尾即重新计算_global_screen_names。
3. 数据模型变更:ScreenInfo 新增app_id字段
3.1 字段定义与解析
ScreenInfo 在构造时通过data.get('app_id', '')读取该字段,空字符串表示全局 screen,默认值保证完全向后兼容。to_dict()仅在app_id非空时才写入该键,因此旧配置文件的读取与写回不受影响。
3.2 YAML 配置示例
# 全局 screen(不设 app_id 或为空) - screen_id: menu screen_name: 菜单 pc_alt: false area_list: ... # 局部 screen(设置 app_id) - screen_id: lost_void_entry screen_name: 迷失之地-入口 app_id: lost_void # ← 与 Application.app_id 一致 pc_alt: false area_list: ...目前主工程已落地的合并配置 assets/game_data/screen_info/_od_merged.yml 中已有 47 处app_id声明,覆盖world_patrol、city_fund、coffee、lost_void、suibian_temple、shiyu_defense等应用,与 screen_scope_rollout.md 记录的迁移清单一致。
4. API 设计:ScreenContext 的 Scope 管理与 Application 生命周期
4.1 ScreenContext 新 API
方案文档给出的接口,在源码中全部有对应实现(screen_loader.py):
class ScreenContext: # ---- Screen Scope 管理 ---- def enter_scope(self, app_id: str) -> None: """进入应用 scope 活跃范围 = 全局 screen + 该 app_id 的局部 screen 仅当存在匹配的局部 screen 时才启用 scope """ def exit_scope(self) -> None: """退出应用 scope,恢复全量匹配""" @property def active_screen_names(self) -> set[str] | None: """当前活跃的 screen 名称集合。None 表示全部活跃""" @property def active_screen_info_list(self) -> list[ScreenInfo]: """当前活跃的 ScreenInfo 列表""" def is_screen_active(self, screen_name: str) -> bool: """判断某个 screen 是否在活跃范围内"""关键实现细节:
enter_scope内部先调用exit_scope()复位,然后做两次防御性判断:若_global_screen_names为空(所有 screen 都没设app_id)或该应用没有匹配的局部 screen,则直接返回、不启用 scope。这保证了「无局部 screen 的应用行为不变」。active_screen_names返回set[str] | None:None表示未启用 scope、全部活跃;启用时返回全局集合 ∪ 局部集合。active_screen_info_list在未启用 scope 时直接返回完整screen_info_list,启用时按名称过滤。
4.2 Application 生命周期自动集成
Application.execute 是实际落地的挂载点,且比设计文档更健壮——使用try/finally保证异常路径也会退出 scope:
def execute(self) -> OperationResult: self.ctx.screen_loader.enter_scope(self.app_id) try: return Operation.execute(self) finally: self.ctx.screen_loader.exit_scope()应用无需额外代码,scope 通过app_id+ YAMLapp_id字段自动匹配。
5. 匹配优化:BFS 搜索范围收窄
5.1 优化策略
get_match_screen_name_from_last中的 BFS 现在适配活跃范围(screen_utils.py):
- 非活跃 screen 跳过匹配:
active_names is not None and current_screen_name not in active_names时直接continue,不执行is_target_screen,从而省掉 OCR/模板匹配开销; - 非活跃 screen 仍展开邻居:跳过匹配的同时遍历其
goto_list继续入队,保持路由图连通性,确保 BFS 最终能到达活跃 screen; - fallback 遍历仅搜索活跃 screen:BFS 结束后兜底遍历
active_screen_info_list,而不是全量列表。
5.2 效果估算
| 场景 | 改动前 | 改动后 |
|---|---|---|
| 迷失之地运行时 BFS 匹配范围 | ~70 screen | ~30 screen(全局 ~16 + 局部 ~14) |
| 每轮 OCR/模板匹配次数(最坏) | ~70 次 | ~30 次 |
| Floyd 路由计算 | O(70³) ≈ 34万次 | 不变(全局路由表共用) |
注意表格第三行是设计文档的关键约束:路由表不随 scope 变化。init_screen_route()在 reload() 时对全量 screen 执行一次 Floyd 算法(O(n³)三层循环),scope 只影响识别阶段get_match_screen_name的搜索范围,round_by_goto_screen仍使用全局路由表查路径。这意味着局部化不会破坏任意两个 screen 之间的可达性计算。
6. 向后兼容性与迁移指南
6.1 兼容性矩阵
| 场景 | 行为 |
|---|---|
所有 YAML 未设app_id(当前现状) | 全部为全局 →enter_scope无匹配 → 不启用 scope → 行为不变 |
部分 YAML 设了app_id | 该应用启用 scope,其他应用不受影响 |
| 应用本身无匹配 screen | enter_scope跳过 → 行为不变 |
结论是零风险渐进迁移:全部不改 YAML 时与现在一模一样,改一个应用的 YAML 只影响该应用。
6.2 迁移步骤
步骤 1:给局部 screen 的 YAML 添加app_id。以迷失之地为例,给 14 个 screen 各加一行:
- screen_id: lost_void_entry screen_name: 迷失之地-入口 app_id: lost_void # ← 新增 pc_alt: false area_list: ...步骤 2:完成。无需修改任何 Python 代码,Application.execute自动调用enter_scope(self.app_id),匹配到 YAML 中app_id: lost_void的 screen,scope 自动生效。
6.3 分层迁移范围(来自 rollout 文档)
配套的 screen_scope_rollout.md 给出了主工程assets/game_data/screen_info/的落地清单:
- 保持全局:
打开游戏、菜单、菜单-更多功能、画面-通用、通用-出战、大世界系列、地图、HDD、战斗画面系列、快捷手册系列、区域巡防、实战模拟室、专业挑战室、恶名狩猎、恢复电量、电玩店、拉面店、家政券。这些是基础导航、共享路由或跨应用复用入口。 - 第一层(独立日常类):
city_fund→丽都城募、email→邮件、intel_board→情报板、random_play→影像店营业、ridu_weekly→丽都周纪、scratch_card→报刊亭、trigrams_collection→卦象集录。 - 第二层(路线链路/业务组):
world_patrol→3D地图+绳网、coffee→咖啡店、commission_assistant→委托助手+钓鱼、drive_disc_dismantle→三个仓库 screen、life_on_line→真拿命验收、suibian_temple→随便观 7 个 screen。 - 第三层(空洞/战斗域):
lost_void→迷失之地 14 个 screen、withered_domain→零号空洞 4 个 screen、shiyu_defense→两个防卫战 screen。
6.4 回归重点
- 加载默认
_od_merged.yml与分文件加载from_separated_files=True时,app_id数量应一致(reload()的两个分支都从ScreenInfo(data)解析,天然共享同一字段语义); HDD与菜单-更多功能必须仍保持全局;- 逐层验证
enter_scope(app_id)后的活跃范围为全局 screen + 当前 app 局部 screen; - 优先 smoke test:
email、drive_disc_dismantle、lost_void、withered_domain、suibian_temple、shiyu_defense。
7. 插件 Screen 加载:让第三方插件拥有自己的画面
7.1 加载机制
插件的 screen 通过OneDragonContext._load_plugin_screens()自动加载,实际调用链(one_dragon_context.py):
OneDragonContext.init() ├── register_application_factory() ← 扫描插件,填充 plugin_infos ├── screen_loader.reload() ← 加载主 screen YAML └── _load_plugin_screens() ← 遍历 plugin_infos → load_extra_screen_dir() └── 对每个插件的 screen_info/ 目录加载 YAML └── 未设 app_id 的 screen 自动使用插件的 app_id refresh_application_registration() ← 运行时刷新插件 ├── clear_applications + discover_factories ← 重新扫描 ├── screen_loader.reload() ← 重新加载主 YAML └── _load_plugin_screens() ← 重新加载插件 screen核心函数 load_extra_screen_dir 的行为要点:
- 仅遍历目录下
.yml文件;非 dict 顶层结构会跳过并告警; default_app_id仅在 YAML 未显式设置app_id时填充:if default_app_id and not data.get('app_id'): data['app_id'] = default_app_id;- screen_name 与 screen_id 冲突时跳过并输出警告日志;
- 只要有新增,就重新执行
init_screen_route()并重算_global_screen_names,保证插件 screen 立即参与路由与作用域计算。
7.2 插件目录结构
plugins/ my_plugin/ __init__.py my_plugin_const.py # APP_ID = 'my_plugin' my_plugin_factory.py my_plugin.py screen_info/ # ← 新增,放 screen YAML my_screen.yml7.3 插件 Screen YAML 示例
# plugins/my_plugin/screen_info/my_screen.yml screen_id: my_plugin_main screen_name: 我的插件-主界面 # app_id 可省略,自动使用插件的 APP_ID pc_alt: false area_list: - area_name: 返回按钮 id_mark: true pc_rect: [82, 13, 150, 90] template_id: back template_sub_dir: menu goto_list: - 大世界-普通这段配置展示了 area 的常用字段:id_mark: true使该区域参与画面精准判定(is_target_screen 要求所有id_mark区域全部命中才匹配成功)、pc_rect定义 PC 端矩形区域、template_id/template_sub_dir指向 assets/template/ 下的模板、goto_list声明跳转边用于路由图。
7.4 冲突处理与命名空间隔离
- 插件 screen_name 与主 YAML 或其他插件冲突时,跳过并输出警告日志;
default_app_id仅在 YAML 未显式设置app_id时使用;- 插件 screen 加载到
ScreenContext后,通过app_id自动归入局部命名空间,不污染其他应用。
插件 screen 同样支持save_screen/delete_screen管理:save_screen会通过_extra_screen_file_path_map判断来源,将修改写回插件自己的 YAML 文件而非合并文件(screen_loader.py),与主工程 screen 的管理路径完全隔离。
8. 文件变更清单与阅读索引
| 文件 | 变更 |
|---|---|
| src/one_dragon/base/screen/screen_info.py | ScreenInfo新增app_id字段 |
| src/one_dragon/base/screen/screen_loader.py | ScreenContext新增 scope 管理 API 和load_extra_screen_dir |
| src/one_dragon/base/screen/screen_utils.py | BFS 匹配适配活跃范围 |
| src/one_dragon/base/operation/application_base.py | Application生命周期自动 enter/exit scope |
| src/one_dragon/base/operation/one_dragon_context.py | 新增_load_plugin_screens(),init 和 refresh 时加载插件 screen |
建议的阅读路径:先读本设计文档与 分层迁移清单 把握全貌,再对照 ScreenContext 的 scope 三件套(enter_scope/exit_scope/active_screen_names)、BFS 匹配 的活跃范围跳过逻辑,最后通过 Application.execute 与 插件加载 确认整个闭环。实际 YAML 落地效果可直接查看 assets/game_data/screen_info/ 中已带app_id的 screen 文件。
- 桌面应用
- RPA
- 计算机视觉
【免费下载链接】ZenlessZoneZero-OneDragon
绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄
相关推荐
绝区零一条龙「实战模拟室」画面识别与自动化挑战全解——从 screen 建档到体力计划编排
绝区零一条龙「实战模拟室」画面识别与自动化挑战全解——从 screen 建档到体力计划编排 本文基于 ZenlessZoneZero OneDragon(绝区零
桌面应用RPA计算机视觉绝区零一条龙插件化应用开发指南:从内置应用到第三方插件
绝区零一条龙插件化应用开发指南:从内置应用到第三方插件 导读 本文以 ZenlessZoneZero OneDragon(绝区零一条龙)开源项目的应用插件系统为
桌面应用RPA计算机视觉RealSense 深度相机生成点云:从安装到 6 步出图完整指南
RealSense 深度相机生成点云:从安装到 6 步出图完整指南 用 librealsense 驱动 D455 等 RealSense 深度相机,把深度图变成
桌面应用RPA计算机视觉
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考