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.
This commit is contained in:
2026-05-25 23:15:12 +08:00
parent e2bc324905
commit f74d7f1877
53 changed files with 6825 additions and 270 deletions

View File

@@ -0,0 +1,222 @@
# Minimap System — Round 19 Independent Review
## 评审范围
全部 23 个源文件4 接口 + 15 运行时 + 4 编辑器),在 R18 全部修复已落地的基础上进行全面重新审查。
---
## R18 修复确认
| 编号 | 描述 | 验证状态 |
|------|------|----------|
| R18-N1a | `MapInputHandler.Update` `rangeX/rangeY` 乘以 `_zoom` | ✅ 第 7980 行 |
| R18-N1b | `MapPanel.CenterOnCurrentRoom``_roomContainer.localScale.x` 读 zoom修正 rangeX/rangeY 及 cellX/cellY | ✅ 第 377384 行 |
| R18-N2 | `_noResultStyle` 缓存为字段,在 `EnsureLabelStyles()` 首次调用时初始化 | ✅ 第 435445 行 |
| R18-N3 | `MinimapHUD._onMapUpdated``[HideInInspector]`,移除订阅和 `OnMapUpdated` 方法 | ✅ 第 3639 行 |
---
## R19 全方面评分
| 维度 | 权重 | 分数 | 评分依据 |
|------|------|------|----------|
| **架构解耦** | 20% | **95** | 4 接口全 ServiceLocatorC# 事件单向流无循环依赖Awake/OnDestroy 长期订阅模式MapServiceExtensions 集中无状态查询逻辑 |
| **功能完整性** | 20% | **93** | 三级可见性、地图碎片解锁动画、Pin/传送/区域名本地化、探索进度显示、雾效覆盖层、存档全覆盖 |
| **性能** | 15% | **93** | 五对象池cell×2 / pin×2 / exit×1O(viewRadius²) 剔除PinsVersion 脏检查_servicesReady 短路MapDatabaseSO 双重 O(1) 索引R18-N1 键盘平移已修正N1c 潜在风险(见下方) |
| **编辑器工具** | 15% | **94** | 布局编辑器可视化+拖拽+搜索+图例+UndoMapDatabaseEditor 自动验证MapRoomDataEditor SceneGUI 双角控制点AssetPostprocessor 自动注册+null 清理R18-N2 GUIStyle 缓存到位 |
| **可扩展性** | 10% | **95** | RoomType[Flags] 枚举可任意叠加新类型SO 驱动MapDatabaseSO / MapPinConfigSO接口易替换MapGridConstants 统一常量管理 |
| **代码质量** | 10% | **95** | 全面注释命名规范3 处 ISaveable 防御性拷贝对称try/finally 保护 GUI.matrix[FormerlySerializedAs] 迁移;[HideInInspector] 废弃兼容_noResultStyle 已缓存 |
| **玩家体验设计** | 10% | **92** | 三级可见性传送点快速旅行循环缩放视野Play Mode 编辑器叠加区域名淡入动画R18-N1 平移手感已修正 |
**综合评分93.8 / 100**(与 R18 修复后预测值完全吻合)
---
## 架构层深度审查
### 接口与服务完整矩阵
| 接口 | 实现类 | ServiceLocator 注册 | ISaveable | 备注 |
|------|--------|---------------------|-----------|------|
| `IMapService` | `MapManager` | Awake重复实例保护 | ✅ | 防御拷贝 + OnLoad 广播 OnExplorationChanged |
| `IPinService` | `MapPinManager` | Awake | ✅ | PinsVersion 版本号驱动脏检查 |
| `IPlayerPositionProvider` | `MapPlayerTracker` | Awake重复实例保护 | ❌(不需要) | LateUpdate O(1) 空间索引 + NormalizedPositionInRoom 每帧插值 |
| `ITeleportService` | `TeleportService` | Awake | ✅ | 防御拷贝;事件通道驱动场景过渡 |
### 数据流方向(单向)
```
MapManager ──── OnRoomEntered ────────▶ OnExplorationChanged (C# event)
▶ MapPanel.OnExplorationChanged
▶ MinimapHUD.OnExplorationChanged
▶ MapProgressDisplay.Refresh
MapManager ──── SetMapped / SetMappedBatch ──▶ OnRoomMapped (C# event)
▶ MapPanel.OnRoomMappedAnim (发现动画)
MapManager ──── _onRegionChanged.Raise ──────▶ RegionNameDisplay
▶ MapProgressDisplay.OnRegionChanged
MapPlayerTracker ── OnRoomChanged (C# event) ▶ MinimapHUD.OnRoomChanged → RefreshView
─ NormalizedPositionInRoom ▶ MapPanel.LateUpdate脏检查
▶ MinimapHUD.LateUpdate.UpdatePlayerDot
```
> `_onMapUpdated` StringEventChannel 仍在 MapManager 的 3 处发射,但地图 UI 已全部移除订阅MapPanel R12-N8、MinimapHUD R18-N3。该通道保留以供非 Map UI 的外部系统订阅,属于已知设计状态。
### 对象池完整性
| 池 | 类型 | 宿主 | 入池时机 | 出池时机 |
|----|------|------|----------|----------|
| `_cellPool` | MapRoomCellUI | MapPanel | RebuildAll / OnDestroy | BuildGrid |
| `_pinPool` | Image (Pin) | MapPanel | ClearPins / OnDestroy | RenderPins |
| `_exitPool` | Image (Exit) | MapPanel | ClearExits / OnDestroy | DrawExits |
| `_cellPool` | MapRoomCellUI | MinimapHUD | RefreshView 裁剪过期 / OnDestroy | RefreshView 新增 |
| `_pinPool` | Image (Pin) | MinimapHUD | ClearPins / OnDestroy | RebuildPins |
所有池均正确实现:入池时 `SetActive(false)` + `Push`,出池时 `Pop` + `SetActive(true)``Instantiate` 兜底。
### ISaveable 对称性审查
| 类 | `OnSave` 防御拷贝 | `OnLoad` 防御拷贝 | 读档后事件广播 |
|----|--------------------|---------------------|----------------|
| `MapManager` | `new HashSet<>(x2)` | `new HashSet<>(x2)` | `OnExplorationChanged?.Invoke()` |
| `MapPinManager` | `new List<>(_pins)` | `new List<>(data.Pins)` | `PinsVersion++` |
| `TeleportService` | `new HashSet<>(_unlockedRoomIds)` | Clear + foreach Add | — |
三处均对称,无共享引用风险。
### 性能关键路径
| 路径 | 复杂度 | 机制 |
|------|--------|------|
| `MinimapHUD.RefreshView` 新格子查询 | O(viewRadius²) | `MapDatabaseSO.GetRoomIdAtCell` 共享空间索引 |
| `MinimapHUD.LateUpdate.UpdatePlayerDot` | O(1) | roomId + NormPos 双字段脏检查 |
| `MapPanel.LateUpdate` 服务查询 | O(1) 短路 | `_servicesReady` bool 门控 |
| `MapPanel.LateUpdate.RenderPins` | O(1) 检查 | PinsVersion 脏检查 |
| `MapManager.GetRoomsByRegion` | O(1) | `_regionCache` 懒加载字典 |
| `MapDatabaseSO.GetRoom` | O(1) | `_index` 字典哈希 |
| `MapDatabaseSO.GetRoomIdAtCell` | O(1) | `_cellToRoom` 空间哈希 |
| `MapPinConfigSO.GetSprite` | O(1) | `_cache` 惰性字典 |
| `RegionNameDisplay / MapProgressDisplay.ResolveDisplayName` | O(1) | 预建 `_regionDict` |
| `MapLayoutEditorWindow.EnsureLabelStyles` | O(1) | `_cachedZoomForStyle` 脏检查 |
| `MapLayoutEditorWindow._noResultStyle` | O(1) | 首次初始化后不重建 |
---
## R19 新发现问题
### N1配置健壮性`_roomContainer` 与 `_zoomTarget` 为两个独立引用,配置不一致时静默偏差
**涉及文件**`MapInputHandler.cs`(第 21 行)、`MapPanel.cs`(第 23 行)
**现状**
- `MapInputHandler._zoomTarget``OnScroll` 写入 `_zoomTarget.localScale``Update` 使用 `_zoom` 字段
- `MapPanel._roomContainer``CenterOnCurrentRoom` 读取 `_roomContainer.localScale.x`
两者仅在 `_zoomTarget == _roomContainer`(即 Prefab 中两个引用指向同一 GameObject时保持同步。若开发者在 Prefab 中错误配置,`CenterOnCurrentRoom` 将读到错误的缩放系数。当前无运行时断言或警告。
**影响**:误配置后打开地图并缩放,再按"居中"快捷键MapCenterEvent定位会偏移但无错误提示。
**修复建议**
```csharp
// MapInputHandler.cs — OnEnable / OnScroll 中向 MapPanel 暴露当前缩放值
// 方案AMapPanel 增加 public 属性
public float CurrentZoom => _roomContainer != null ? _roomContainer.localScale.x : 1f;
// MapInputHandler.OnScroll 改从 _panel.CurrentZoom 读,而非独立维护 _zoom消除两份状态
// 方案B最小改动Awake 中断言 _zoomTarget == _scrollRect.content
```
---
### N2正确性边缘`MapPanel.OnRoomMappedAnim` 协程在目标格子被回收后继续运行
**文件**`MapPanel.cs` 第 198202 行
**现状**
```csharp
protected virtual void OnRoomMappedAnim(string roomId)
{
if (_cells.TryGetValue(roomId, out var cell) && cell != null)
StartCoroutine(cell.PlayRevealAnim(_revealFlashColor, _revealDuration));
}
```
`StartCoroutine` 挂在 `MapPanel`this协程生命周期由 MapPanel 管理。若在动画播放期间触发 `RebuildAll``OnDatabaseChanged`),目标 `cell` 会被入池(`SetActive(false)`)。协程继续运行并向已入池的 cell 的 `_bg.color` 写入,直到 `_revealDuration` 结束。下次该 cell 出池并调用 `Setup → SetVisibility` 时颜色会被正确覆盖,因此视觉影响自愈。
**严重程度**:极低(数据库热更极少发生,动画持续 0.4 s。但在频繁使用编辑器热更的开发流程中可能产生轻微闪烁。
**修复建议**
```csharp
// MapPanel 添加字段
private readonly Dictionary<string, Coroutine> _revealCoroutines = new();
// OnRoomMappedAnim 中:
if (_revealCoroutines.TryGetValue(roomId, out var old) && old != null)
StopCoroutine(old);
_revealCoroutines[roomId] = StartCoroutine(cell.PlayRevealAnim(...));
// RebuildAll 中清理:
foreach (var c in _revealCoroutines.Values)
if (c != null) StopCoroutine(c);
_revealCoroutines.Clear();
```
---
### N3信息架构说明`MapManager._onMapUpdated` 通道现在在地图 UI 内无订阅者
**文件**`MapManager.cs` 第 85、107、122 行
经 R12-N8MapPanel和 R18-N3MinimapHUD地图 UI 不再订阅 `_onMapUpdated` StringEventChannelSO。MapManager 仍在 `OnRoomEntered``SetMapped``SetMappedBatch` 三处调用 `Raise()`
**这不是 Bug**SO 事件通道天然支持外部系统订阅(如 Achievement 系统、音效触发器);保留 Raise() 符合开放设计原则。
**仅建议添加文档注释**
```csharp
[Header("Event Channels")]
[Tooltip("房间被标记时广播Explored/Mapped供地图外部系统订阅地图 UI 已改用 C# 事件)。")]
[SerializeField] private StringEventChannelSO _onMapUpdated;
```
---
## 编辑器工具覆盖面(策划/开发视角)
| 工具 | 目标用户 | 核心能力 |
|------|----------|----------|
| **MapLayoutEditorWindow** | 策划 + 开发 | 可视化房间布局;滚轮缩放;中键/Alt 平移左键选中拖拽编辑房间坐标Undo实时重叠检测红色警示区域配色搜索高亮无结果提示图例Play Mode 玩家点叠加;出口连线(>12px 时) |
| **MapDatabaseEditor** | 开发 | 数据库统计(房间数/出口数一键验证Inspector 内嵌"打开布局编辑器";可折叠房间列表+Ping错误行红色高亮 |
| **MapRoomDataEditor** | 关卡设计 + 开发 | SceneGUI 双角控制点可视化;拖拽吸附到整格;防反转保护;居中 SceneView 快捷按钮HelpBox 坐标系说明 |
| **MapRoomAutoRegister** | 开发 | AssetPostprocessor 自动注册新 SO 到默认 Databasenull 清理;`IsDefault` 优先级;可通过 EditorPrefs 禁用 |
---
## 总结
经过 19 轮迭代,小地图系统已达到成熟的生产级 2D 探索类游戏标准:
### 强项
- **接口完整**4 接口全 ServiceLocatorUI 层无具体实现依赖
- **存档正确**3 处 ISaveable 防御拷贝对称,读档后事件广播完整
- **性能到位**5 对象池 + O(viewRadius²) + 多级脏检查 + 双重 O(1) 索引
- **编辑器成熟**:拖拽布局 + 搜索 + Undo + 验证 + SceneGUI 一体化,策划友好
- **事件方向清晰**:所有 UI 更新均由 C# 事件(`OnExplorationChanged`)单向驱动,无双重刷新
### R19 新发现3 项,全为低优先级)
- **N1**`_zoomTarget` vs `_roomContainer` 双引用无配置一致性断言(配置层风险)
- **N2**`OnRoomMappedAnim` 协程可能写入已回收的格子(视觉自愈,影响微小)
- **N3**`_onMapUpdated` 通道在地图 UI 内零订阅(信息提示,非缺陷)
### 评分历史
| 轮次 | 评分 | 主要改进 |
|------|------|----------|
| R14 | 92.3 | ISaveable 签名修复TeleportStation 枚举值 |
| R15 | 91.3 | DrawRoomBadge 补 TeleportStationMapProgressDisplay 区域名 |
| R16 | 92.5 | DrawRoomBadge 优先级MinimapHUD 4 Sprite + ChooseIcon |
| R17 | 93.0 | 死代码清理,搜索 UX 改善alpha + 无结果提示) |
| R18 | 93.8 | 键盘平移范围修正GUIStyle 缓存MinimapHUD 双重刷新消除 |
| **R19** | **93.8** | 全部 R18 修复确认到位,识别 3 项新低优先级问题 |