- 所有 70 个 screen 注册在全局
ScreenContext中 - 应用通过硬编码字符串引用 screen(如
'迷失之地-入口') round_by_goto_screen调用get_match_screen_name时,BFS 遍历可能扫描全部 70 个 screen- 每次 screen 匹配涉及 OCR 或模板匹配,开销大
| 问题 | 原因 | 影响 |
|---|---|---|
| 插件无法管理 screen | screen 只从 assets/game_data/screen_info/ 加载,无注入点 |
第三方插件无法定义画面 |
| 全局污染 | 所有 screen 同一命名空间,BFS 搜索和路由计算混杂不相关 screen | 识别变慢 |
| 全局跳转慢 | round_by_goto_screen 无范围限制,BFS 可能检查大量无关 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(不设 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: ...app_id 默认为空字符串,完全向后兼容。
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 是否在活跃范围内"""class Application(Operation):
def handle_init(self):
# 自动进入 scope(基于 self.app_id)
self.ctx.screen_loader.enter_scope(self.app_id)
...
def after_operation_done(self, result):
# 自动退出 scope
self.ctx.screen_loader.exit_scope()
...应用无需额外代码,scope 通过 app_id + YAML app_id 字段自动匹配。
get_match_screen_name_from_last 中:
- 非活跃 screen 跳过匹配(避免 OCR/模板匹配开销)
- 非活跃 screen 仍展开邻居(保持图连通性,确保能找到活跃 screen)
- fallback 遍历仅搜索活跃 screen
| 场景 | 改动前 | 改动后 |
|---|---|---|
| 迷失之地运行时 BFS 匹配范围 | ~70 screen | ~30 screen(全局 ~16 + 局部 ~14) |
| 每轮 OCR/模板匹配次数(最坏) | ~70 次 | ~30 次 |
| Floyd 路由计算 | O(70³) ≈ 34万次 | 不变(全局路由表共用) |
全局路由表(Floyd)仅在 reload() 时计算一次,scope 不影响。
round_by_goto_screen 使用全局路由表查找路径,scope 仅影响 screen 识别的搜索范围。
| 场景 | 行为 |
|---|---|
所有 YAML 未设 app_id(当前现状) |
全部为全局 → enter_scope 无匹配 → 不启用 scope → 行为不变 |
部分 YAML 设了 app_id |
该应用启用 scope,其他应用不受影响 |
| 应用本身无匹配 screen | enter_scope 跳过 → 行为不变 |
零风险渐进迁移:全部不改 YAML 时与现在一模一样,改一个应用的 YAML 只影响该应用。
以迷失之地为例,给 14 个 screen 各加一行:
- screen_id: lost_void_entry
screen_name: 迷失之地-入口
app_id: lost_void # ← 新增
pc_alt: false
area_list: ...无需修改任何 Python 代码。Application.handle_init 自动调用 enter_scope(self.app_id),
匹配到 YAML 中 app_id: lost_void 的 screen,scope 自动生效。
实际业务 screen 的 local 化范围与分层迁移顺序见 screen_scope_rollout.md。
插件的 screen 通过 OneDragonContext._load_plugin_screens() 自动加载:
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
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.yml
# 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:
- 大世界-普通- 插件 screen_name 与主 YAML 或其他插件冲突时,跳过并输出警告日志
default_app_id仅在 YAML 未显式设置app_id时使用 插件的 screen 加载到ScreenContext后,通过app_id自动归入局部命名空间,不污染其他应用。
| 文件 | 变更 |
|---|---|
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 |