# 小地图系统 独立审查报告 Round 13 **审查范围**:`Assets/_Game/Scripts/World/Map/`(Runtime 17 文件)+ `Assets/_Game/Scripts/Editor/World/Map/`(Editor 4 文件) **对标标准**:成熟 2D Metroidvania 类型游戏的专业编辑器扩展级别, 面向开发人员和策划人员,要求架构解耦、高性能、高可扩展性。 --- ## 总评分 | 维度 | 满分 | 得分 | 说明 | |---|---|---|---| | 架构解耦 | 10 | 9.0 | 接口 + ServiceLocator 完整;唯一遗留:MapPin.cs 文件名与类名不符(历史问题) | | 性能 | 10 | 9.0 | 对象池完整、dirty check 完整;FormatException 风险影响稳定性 | | 编辑器 UX | 10 | 8.5 | 可视化布局编辑器完善;缺少快捷键说明与快速创建 | | 数据模型 | 10 | 8.5 | RoomType [Flags] + HasCustomExitPos 完善;但 DrawExits 未使用 HasCustomExitPos | | 输入系统 | 10 | 7.5 | InputReaderSO 对接完整;但 CycleZoom 无绑定、缺少"居中"快捷键 | | 功能完整性 | 10 | 7.5 | ITeleportService 接口已定义但无实现 | | 代码质量 | 10 | 9.0 | 注释质量高;MapProgressDisplay.Refresh() 缺少异常防护 | | 可扩展性 | 10 | 9.0 | SO 驱动、Event Channel 解耦、Region 机制完善 | | **总分** | **80** | **68.0** | **换算 100 分:85.0 / 100(B+)** | > **相比 R12(86.2/100)**:发现 2 个遗留 Bug(N1 DrawExits、N2 FormatException)和 3 个增强点,分数略有下调。修复后预计 91+。 --- ## Bug 发现 ### N1(严重):MapPanel.DrawExits() 忽略 HasCustomExitPos **文件**:`MapPanel.cs`,`DrawExits()` 方法 **问题**:出口连接线的位置直接使用 `exit.ExitGridPos * FullMapCellPixels`, 未检查 `HasCustomExitPos` 标志。当策划未配置自定义出口坐标时, `ExitGridPos` 默认为 `Vector2Int.zero`,所有连接线均渲染在 `_roomContainer` 原点 (0, 0) 处。 `MapLayoutEditorWindow.DrawExitLines()` 已在 R12 修复了同样问题,但 `MapPanel.DrawExits()` 被遗漏。 ```csharp // ❌ 当前代码:未检查 HasCustomExitPos conn.rectTransform.anchoredPosition = new Vector2( exit.ExitGridPos.x * MapGridConstants.FullMapCellPixels, exit.ExitGridPos.y * MapGridConstants.FullMapCellPixels); // ✅ 修复:回退到方向中点 Vector2Int gridPos = exit.HasCustomExitPos ? exit.ExitGridPos : GetExitFallbackGridPos(room, exit); // 按 ExitDirection 计算房间边缘中点 ``` --- ### N2(中等):MapProgressDisplay.Refresh() 无 FormatException 防护 **文件**:`MapProgressDisplay.cs`,`Refresh()` 方法 **问题**:`_globalFormat` / `_regionFormat` 是 Inspector 可编辑字段; 策划填写错误格式字符串(如 `{2}` 超出参数范围)时, `string.Format(...)` 抛出 `FormatException`,导致运行时异常。 ```csharp // ❌ 当前代码:无异常防护 _globalProgressText.text = string.Format(_globalFormat, progress); // ✅ 修复:try-catch + fallback try { _globalProgressText.text = string.Format(_globalFormat, progress); } catch (FormatException) { _globalProgressText.text = $"{progress:P0}"; Debug.LogWarning($"[MapProgressDisplay] 格式字符串错误:{_globalFormat}", this); } ``` --- ## 增强点 ### N3(高优先):MinimapHUD.CycleZoom() 无输入绑定 **文件**:`MinimapHUD.cs`,`CycleZoom()` 方法 `CycleZoom()` 是一个 `public` 方法,设计意图是绑定到按键。但: - `InputReaderSO` 没有 `CycleMinimapZoomEvent` 事件 - 没有 `MinimapInputHandler` 组件订阅该方法 **修复**: 1. 在 `InputReaderSO` 添加 `CycleMinimapZoomEvent` 2. 新建 `MinimapInputHandler.cs` 组件,绑定按键 → `CycleZoom()` --- ### N4(中等):MapInputHandler 缺少"居中到玩家"快捷键 **文件**:`MapInputHandler.cs`,`MapPanel.cs` 全屏地图打开后无"居中"快捷键(常见 UX 需求)。 `MapPanel.CenterOnCurrentRoom()` 为 `private`,外部无法调用。 **修复**: 1. 在 `InputReaderSO` 添加 `MapCenterEvent` 2. 将 `CenterOnCurrentRoom()` 改为 `public` 3. 在 `MapInputHandler.OnEnable/OnDisable` 中订阅 --- ### FA(缺失):ITeleportService 无具体实现 **文件**:`ITeleportService.cs`(接口已定义;无对应实现类) 定义了完整的传送服务接口,但无任何具体实现类。地图 UI 无法调用传送功能。 **修复**:新建 `TeleportService.cs`,实现 `ITeleportService`: - `CanTeleportTo`:检查解锁状态 + `IMapService.IsExplored` - `RequestTeleport`:触发 `OnTeleportRequested` 事件 + 经由 `StringEventChannelSO` 驱动场景加载 - `ISaveable` 持久化已解锁传送点列表 --- ## 已验证正常的项目 经本轮全量重读确认以下 R12 修复均完整到位: | 项目 | 状态 | |---|---| | MapPanel Cell/Exit 对象池(N1) | ✅ | | DrawExitLines HashSet 去重(N2) | ✅ | | MapPinConfigSO + O(1) dict cache(N3) | ✅ | | MapRoomAutoRegister null cleanup(N4) | ✅ | | HasCustomExitPos flag in MapLayoutEditorWindow(N5) | ✅ | | RegionNameDisplay O(1) dict lookup(N6) | ✅ | | _servicesReady 短路(N7) | ✅ | | 移除 OnMapUpdated 双重订阅(N8) | ✅ | | RoomFlags [Flags] 枚举兼容(N9) | ✅ | | PlayRevealAnim 协程(FC) | ✅ | | CycleZoom() 方法存在(FA — 但无绑定) | ⚠ 见 N3 | | TryGetRoomAtWorldPos(FB) | ✅ | | CreatePinAtWorldPos 扩展(FE) | ✅ | | MapProgressDisplay 组件存在(FF) | ⚠ 见 N2 | --- ## 实现计划 | 编号 | 改动 | 文件 | |---|---|---| | N1 | DrawExits 使用 HasCustomExitPos + 方向回退 | `MapPanel.cs` | | N2 | Refresh() FormatException 防护 | `MapProgressDisplay.cs` | | N3 | CycleMinimapZoomEvent + MinimapInputHandler | `InputReaderSO.cs`、新建 `MinimapInputHandler.cs` | | N4 | MapCenterEvent + 公开 CenterOnCurrentRoom | `InputReaderSO.cs`、`MapPanel.cs`、`MapInputHandler.cs` | | FA | TeleportService 具体实现 | 新建 `TeleportService.cs` |