Files
zeling_v2/Docs/Review/Minimap_Review_Round23_Independent.md
Joywayer f74d7f1877 Add independent review reports for Minimap system (Rounds 8, 9, and 26)
- Round 8 report highlights improvements in architecture, editor usability, and data robustness, with a total score of 80/100.
- Round 9 report focuses on editor extension capabilities, identifying issues with room data indexing and layout editing, resulting in a score of 76/100.
- Round 26 report evaluates the system against commercial standards, noting new issues and confirming previous fixes, with a score of 95.8/100.
2026-05-25 23:15:12 +08:00

199 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 小地图系统独立评审 Round 23
**评审时间**R23基于 R22 全部修复已落地的代码基线)
**评审范围**`Assets/_Game/Scripts/World/Map/` 全部 19 个运行时文件 + 4 个编辑器扩展文件
**评分基准**:专业 2D Metroidvania 编辑器扩展标准(架构解耦 / 高性能 / 可扩展 / 开发者友好)
---
## R22 修复确认
| 编号 | 内容 | 状态 |
|------|------|------|
| R22-N1 | `BuildRegionDict/ResolveRegionDisplayName` 迁移至 `MapServiceExtensions`;两个组件单行委托 | ✅ 确认(`MapServiceExtensions.cs:17,32`;两组件第 104-108 行) |
| R22-N2 | `DrawRoomBadge` 注释更新为 `MapRoomDataSO.ChooseDisplayIcon` | ✅ 确认(`MapLayoutEditorWindow.cs:480,488` |
| R22-N3 | `MapPinManager [DefaultExecutionOrder(-500)]` | ✅ 确认(`MapPin.cs:20` |
---
## 各维度评分
### 1. 架构设计Architecture19.5 / 20
**亮点(全面审查确认)**
- 4 接口IMapService / IPinService / IPlayerPositionProvider / ITeleportService+ ServiceLocator零硬依赖
- 事件驱动Action无帧轮询OnDatabaseChanged / OnExplorationChanged / OnRoomMapped 语义独立
- ISaveable 防御性拷贝三处对称MapManager`new HashSet<>` ×2MapPinManager`new List<>` 双向TeleportService`readonly` + Clear+Add
- `ChooseDisplayIcon` 单一入口MapRoomDataSOMapPanel / MinimapHUD / Editor 三处均委托
- `MapServiceExtensions`3 个无状态方法GetVisibility / CreatePinAtWorldPos / BuildRegionDict+Resolve消费方零重复逻辑
- `MapRoomCellUI` 双视图MapPanel 全屏 + MinimapHUD 角落)复用同一 Prefab
- `RoomType [Flags]` 枚举支持多类型组合,`OnValidate` 自动迁移旧 bool 字段
**信息级观察**`MapPanel.UnsubscribeServices()` 不 null 服务引用,而 `MinimapHUD.UnsubscribeServices()` 会 null 三个字段并重置 `_servicesReady`。这是有意的架构差异——MapPanel 仅在 OnDestroy 调用 UnsubscribeServices生命周期末尾引用已无意义而 MinimapHUD 作为持久 HUD 需支持跨场景重连。在目标持久服务架构下,两者均不会出现悬空引用。**无需修复,但值得注释说明设计意图。**
**扣分0.5**UnsubscribeServices 不对称缺少注释,可读性略低)
---
### 2. 性能Performance19.5 / 20
**亮点**
- `_servicesReady` 短路MapPanel R12-N7MinimapHUD R21-N1消除每帧 ServiceLocator 查询
- MapPanel 对象池 × 3`readonly` 集合:`_cellPool` / `_pinPool` / `_exitPool`MinimapHUD × 2
- O(viewRadius²) 空间索引(`MapDatabaseSO.GetRoomIdAtCell`MinimapHUD 无需扫描全量房间
- 4 个复用缓冲区(`_toRemove` / `_roomsInViewBuffer` / `_newlyAddedBuffer` + `_cells` 增量更新)
- `PinsVersion` 脏检查:两 UI 均跳过无变化重绘
- `_totalRoomCount` 懒加载缓存MapManager`_regionCache` 懒加载GetRoomsByRegion 零 LINQ
- 玩家图标双脏标记roomId + normPos消除无效 RectTransform 写入
**信息级**`MapProgressDisplay.Refresh()` 区域进度遍历 O(N/region),仅在 `OnExplorationChanged` 触发(非每帧),当前规模无性能风险。
**扣分0.5**`MapProgressDisplay` 区域遍历未缓存已探索计数;极大地图时有轻微冗余)
---
### 3. 代码质量Code Quality19.5 / 20
**亮点**
- 全部集合字段补齐 `readonly`MapPanel × 6MinimapHUD × 4`_revealCoroutines` 亦为 `readonly`
- `CurrentZoom` 属性消除双份缩放状态MapPanel L379
- `RunRevealAnim` 自清理协程:完成后自动 `_revealCoroutines.Remove(roomId)`R20-N1
- `HasCustomExitPos` 语义布尔替代 `== Vector2Int.zero` 哨兵R13-N1
- `[Obsolete]` + `[HideInInspector]` 废弃字段注解完整
- `RoomId.Trim()` 自动修剪防空格污染OnValidate
- `DrawLine` try/finally 保证 `GUI.matrix` 恢复R11-N12
- `MapPin.cs` 文件头注释明确历史遗留原因
**信息级**`MapPanel.UnsubscribeServices()` 相比 `MinimapHUD.UnsubscribeServices()` 缺少注释说明为何不 null 服务引用。新加入代码者可能误认为是疏漏。
**扣分0.5**UnsubscribeServices 不对称未有注释解释意图)
---
### 4. 编辑器扩展Editor Tools14.5 / 15
**亮点**
- `MapLayoutEditorWindow`:缩放 / 平移 / 房间拖拽 / 搜索 / 区域着色 / 验证 / Undo / 热改全功能
- `_cachedZoomForStyle` 脏检查 + `_noResultStyle` 首次初始化缓存R18-N2
- `DrawExitLines` 字段级 `_drawnExitPairs` 去重OnGUI 零分配
- `OnProjectChange` + `OnUndoRedo` 自动失效验证缓存并触发重绘
- `SetDatabase` 公共 API避免 MapDatabaseEditor 反射访问私有字段)
- `MapRoomAutoRegister` AssetPostprocessor 自动注册工作流
- `MapDatabaseSO.ValidateAll()` 四类校验null / 空 RoomId / 重复 / 格子重叠 / 出口悬空)
- `DrawRoomBadge` 注释已更新为 `MapRoomDataSO.ChooseDisplayIcon`R22-N2
**扣分0.5**`MapLayoutEditorWindow` 搜索框匹配范围仅 RoomId/RegionId 子串,无法按 RoomType 过滤;策划批量检查某类型房间时需逐个点击)
---
### 5. 功能完整性Feature Completeness15.0 / 15
**所有特性完整实现:**
- 三级可见性Unknown / Explored / Mapped+ 雾效覆盖层R12-FD
- 图标优先级唯一入口Override > SavePoint > Boss > Shop > Teleport
- Pin 系统(持久化 / 类型化 / 视野内渲染 / 64 字符 note 限制)
- 出口连接线 + Fallback 位置HasCustomExitPos 语义布尔)
- 传送系统TeleportService解锁 / 验证 / 请求 / 完成回调)
- 区域检测 + 区域名本地化显示RegionNameDisplay + MapProgressDisplay
- 存档/读档ISaveable 三处,防御性拷贝对称)
- 房间发现动画PlayRevealAnim + RunRevealAnim 自清理)
- 全屏地图 + 角落 HUD 双视图小地图视野档位循环切换CycleZoom
- `RoomType [Flags]` 枚举 + `OnValidate` 向后兼容迁移
**扣分0**
---
### 6. 输入系统Input System9.5 / 10
**亮点**
- 全部使用 InputReaderSOInputSystem无硬编码按键
- `MapInputHandler`Navigate / MapCenter / OnScroll 完整
- `MinimapInputHandler`CycleMinimapZoom 路由 MinimapHUD.CycleZoom()
- OnEnable/OnDisable 对称订阅/取消
**信息级**`MapInputHandler._zoom` 作为本地累加器,与 `_panel.CurrentZoom` 读取路径不同但结果一致OnScroll 写 `_roomContainer.localScale``CurrentZoom``_roomContainer.localScale.x` 读取,无环,无状态漂移)。
**扣分0.5**`MapInputHandler._zoom` 轻微职责重叠,正确但不够直观)
---
## 综合评分
| 维度 | 满分 | 得分 | 较 R22 |
|------|------|------|--------|
| 架构设计 | 20 | 19.5 | +0.5 |
| 性能 | 20 | 19.5 | ±0 |
| 代码质量 | 20 | 19.5 | +0.5 |
| 编辑器扩展 | 15 | 14.5 | ±0 |
| 功能完整性 | 15 | 15.0 | ±0 |
| 输入系统 | 10 | 9.5 | ±0 |
| **合计** | **100** | **97.5** | **+1.0** |
---
## 问题清单
### N1 — MapPanel.UnsubscribeServices 缺少意图注释(低优先级)
**现状**`MinimapHUD.UnsubscribeServices()` 会 null 三个服务引用并重置 `_servicesReady``MapPanel.UnsubscribeServices()` 不做此操作(也不需要,因为仅在 OnDestroy 调用)。两者不对称,可读性略差。
**修复**:在 `MapPanel.UnsubscribeServices()` 首行补注释:
```csharp
// OnDestroy 时调用,生命周期末尾不需要 null 服务引用(与 MinimapHUD.UnsubscribeServices 的设计差异:
// MinimapHUD 需支持 OnDestroy 后跨场景重连MapPanel 不需要)。
```
---
### N2 — MapLayoutEditorWindow 搜索框不支持 RoomType 过滤(低优先级)
**场景**:策划需要批量确认所有 SavePoint / BossRoom 房间的位置时,当前搜索只能匹配 RoomId/RegionId 子串,无法用 `SavePoint` 等类型关键字过滤。
**建议方案**:在搜索逻辑中加入 RoomType 枚举名匹配:
```csharp
// 在 DrawMapArea 的搜索匹配部分补充:
bool matchesType = !string.IsNullOrEmpty(_searchText) &&
System.Enum.GetNames(typeof(RoomType))
.Any(name => room.RoomFlags.HasFlag((RoomType)System.Enum.Parse(typeof(RoomType), name))
&& name.IndexOf(_searchText, System.StringComparison.OrdinalIgnoreCase) >= 0);
bool matches = matchesId || matchesRegion || matchesType;
```
---
### N3 — TeleportService 缺少 [DefaultExecutionOrder](信息级)
`TeleportService.Awake()` 注册 `ITeleportService`。当前 UI 组件MinimapHUD / MapPanel`SubscribeServices` 不查询 `ITeleportService`因此无执行顺序风险。但与其他三个服务MapManager -700 / MapPlayerTracker -600 / MapPinManager -500相比未标注顺序一致性略差。
```csharp
[DefaultExecutionOrder(-400)] // 晚于 MapPinManager(-500),早于默认 0ITeleportService 在 UI 初始化前可用
public class TeleportService : MonoBehaviour, ITeleportService, ISaveable
```
---
## 评分历史
| 轮次 | 评分 | 关键改动 |
|------|------|----------|
| R18 | 93.8 | MinimapHUD 废弃 _onMapUpdated 订阅 |
| R19 | 93.8 | CurrentZoom 属性_revealCoroutines 防泄漏 |
| R20 | 94.2 | RunRevealAnim 自清理ChooseDisplayIcon 集中 |
| R21 | 95.0 | MinimapHUD _servicesReadyMapPlayerTracker 执行顺序readonly 补全 |
| R22 | 96.5 | RegionNameEntry DRY 消除注释更新MapPinManager 执行顺序 |
| **R23** | **97.5** | R22 修复确认;识别 N1注释不对称N2搜索缺 RoomType 过滤N3TeleportService 执行顺序) |
---
## 总评
经过 23 轮迭代,小地图系统已达到**专业商业 2D Metroidvania 发布标准**
- **架构**:接口 + ServiceLocator + 事件驱动,各层职责清晰,扩展无需改动已有代码
- **性能**:全部热路径均有脏检查 / 对象池 / 短路机制保护,大地图下无性能风险
- **开发体验**:编辑器工具完整,策划可视化配置房间、验证错误、调试布局
- **代码健康**DRY 违反已全部消除readonly / 防御性拷贝 / 对象池三要素一致应用
剩余 2.5 分差距为信息级的可读性优化与编辑器小功能增强,不影响运行时正确性。