# StateMachineBase 状态机框架 ## 概述 `StateMachineBase` 是一个通用的有限状态机基类,专为游戏自动化场景设计。 ## 核心概念 | 概念 | 说明 | |------|------| | **State** | 游戏界面的抽象(如 MainWorld、EventMenu) | | **Transition** | 状态之间的有向边,定义从一个状态可能到达的下一个状态 | | **Handler** | 状态处理器,执行当前状态的操作(如点击按钮),返回 `StateHandlerResult` | | **Detector** | 状态检测器,根据截图判断当前是否处于某个状态 | ## StateHandlerResult Handler 必须返回 `StateHandlerResult`,状态机根据返回值决定下一步行为: | 返回值 | 语义 | 状态机行为 | 使用场景 | |--------|------|-----------|----------| | `Success` | 操作成功 | 等待邻接状态转换 | 点击按钮后等待界面切换 | | `Wait` | 可预期的等待 | 继续循环,重新检测 | 动画播放中、已到达目标状态 | | `Retry` | 意外失败 | 重试计数+1,超限后异常 | 找不到按钮、OCR 识别失败 | | `Fail` | 无法恢复 | 立即抛出异常 | 严重错误需要停止 | ## 使用步骤 ### 1. 定义状态枚举 ```csharp public enum MyState { Unknown, MainWorld, EventMenu, BattleArena } ``` ### 2. 继承 StateMachineBase ```csharp public class MyTask : StateMachineBase { protected override ILogger Logger => _logger; private readonly ILogger _logger; } ``` ### 3. 注册 Handlers ```csharp RegisterStateHandlers( (MyState.MainWorld, HandleMainWorld), (MyState.EventMenu, HandleEventMenu) ); RegisterUnknownStateHandler(HandleUnknown); ``` ### 4. 注册 Detectors ```csharp RegisterStateDetectors( (MyState.MainWorld, DetectMainWorld), (MyState.EventMenu, DetectEventMenu), (MyState.BattleArena, DetectBattleArena) ); ``` ### 5. 注册状态转换 ```csharp RegisterStateTransitions( (MyState.MainWorld, [MyState.EventMenu]), (MyState.EventMenu, [MyState.BattleArena]) ); ``` ### 6. 实现 Handler ```csharp private async Task HandleMainWorld(BvPage page) { Simulation.SendInput.SimulateAction(GIActions.OpenTheEventsMenu); await Delay(500, _ct); return StateHandlerResult.Success; // 等待转换到 EventMenu } private async Task HandleEventMenu(BvPage page) { var button = page.GetByText("开始").FindAll().FirstOrDefault(); if (button == null) { return StateHandlerResult.Retry; // 找不到按钮,重试 } button.Click(); await Delay(300, _ct); return StateHandlerResult.Success; } private Task HandleBattleArena(BvPage page) { // 已到达目标状态 return Task.FromResult(StateHandlerResult.Wait); } private async Task HandleUnknown(BvPage page) { await new ReturnMainUiTask().Start(_ct); return StateHandlerResult.Wait; // 尝试恢复后重新检测 } ``` ### 7. 实现 Detector ```csharp private bool DetectMainWorld(ImageRegion ra) { return ra.Find(MainWorldAssets.Minimap).IsExist(); } private bool DetectEventMenu(ImageRegion ra) { return ra.FindMulti(RecognitionObject.Ocr(100, 50, 200, 50)) .Any(t => t.Text.Contains("活动")); } ``` ### 8. 运行状态机 ```csharp public async Task Start(CancellationToken ct) { Initialize(ct, MyState.MainWorld); using var page = Bv.Page(); await RunStateMachineUntil(page, MyState.BattleArena); // 状态机退出后,已到达 BattleArena await ExecuteBattle(); } ``` ## API 参考 ### 核心方法 | 方法 | 说明 | |------|------| | `Initialize(ct, initialState)` | 初始化状态机 | | `RunStateMachineUntil(context, targetStates)` | 运行直到达到目标状态 | | `RunStateMachineUntil(context, targetState)` | 运行直到达到单个目标状态 | | `EnsureNextStateTransition(timeout)` | 等待邻接状态转换(供特殊场景使用) | ### 注册方法 | 方法 | 说明 | |------|------| | `RegisterStateHandler(state, handler)` | 注册单个状态处理器 | | `RegisterStateHandlers(...)` | 批量注册状态处理器 | | `RegisterUnknownStateHandler(handler)` | 注册未知状态处理器 | | `RegisterStateDetector(state, detector)` | 注册单个状态检测器 | | `RegisterStateDetectors(...)` | 批量注册状态检测器 | | `RegisterStateTransitions(...)` | 注册状态转换关系 | ### 可重写属性 | 属性 | 默认值 | 说明 | |------|--------|------| | `DefaultDetectionInterval` | 300ms | 状态检测间隔 | | `DefaultTransitionTimeout` | 10000ms | 状态转换超时 | | `DefaultMaxRetries` | 3 | 最大重试次数 | | `StateMachineLoopInterval` | 200ms | 状态机循环间隔 | ## 状态机循环流程 ``` ┌─────────────────────────────────────────────────────┐ │ RunStateMachineUntil │ └─────────────────────────────────────────────────────┘ │ ▼ ┌──────────────────────┐ │ RefreshCurrentState │ ◄─────────────┐ │ (检测当前状态) │ │ └──────────────────────┘ │ │ │ ▼ │ ┌──────────────────────┐ │ │ 是否到达目标状态? │ │ └──────────────────────┘ │ │ │ │ Yes No │ │ │ │ ▼ ▼ │ ┌────────┐ ┌──────────────────────┐ │ │ 退出 │ │ 执行 Handler │ │ └────────┘ └──────────────────────┘ │ │ │ ▼ │ ┌─────────────────────────────┐ │ │ Handler 返回值 │ │ └─────────────────────────────┘ │ │ │ │ │ │ ┌──────────────┤ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ ▼ │ │ ┌─────────┐ ┌─────┐ ┌───┐ ┌────┐│ │ │ Success │ │Wait │ │Rty│ │Fail││ │ └─────────┘ └─────┘ └───┘ └────┘│ │ │ │ │ │ │ │ ▼ │ │ ▼ │ │ ┌───────────────┐ │ │ 抛出 │ │ │EnsureNextState│ │ │ 异常 │ │ │ Transition │ │ │ │ │ └───────────────┘ │ │ │ │ │ │ ▼ │ │ │ │ ┌────────┐ │ │ │ │ │重试计数│ │ │ │ │ │ +1 │ │ │ │ │ └────────┘ │ │ │ │ │ │ │ └──────────────┴────┴────────┴────────────┘ ``` ## 性能优化 ### 检测器顺序 注册检测器时按速度排序(先快后慢): 1. **模板匹配**(~10ms)- 最快 2. **小范围 OCR**(~50ms) 3. **大范围 OCR**(~100-200ms)- 最慢 ### 邻接状态顺序 `RegisterStateTransitions` 中候选状态的顺序影响检测优先级,更具体的状态应放在前面: ```csharp // 正确:更具体的状态放前面 (TeleportMap, [DomainEntrance, MainWorld]) // 错误:通用状态会被优先检测 (TeleportMap, [MainWorld, DomainEntrance]) ``` ## 示例 完整示例见 `GameTask/AutoStygianOnslaught/AutoStygianOnslaughtTask.cs` 状态图: ``` MainWorld ─► EventMenu ─► StygianOnslaughtPage ─► TeleportMap │ ▼ DomainLobby ◄── DifficultySelect ◄── DomainEntrance ◄─┘ │ ▼ BossSelect ─► BattleArena ─► BattleResult ─► DomainLobby ```