Files
zeling_v2/Docs/Review/Minimap_Review_Round18_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

197 lines
9.1 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.
# Minimap System — Round 18 Independent Review
## 评审范围
全部 23 个源文件4 接口 + 15 运行时 + 4 编辑器),在 R17 全部修复已落地的基础上进行全面重新审查。
---
## R17 修复确认
| 编号 | 描述 | 验证状态 |
|------|------|----------|
| R17-N1 | 删除 `MapLayoutEditorWindow._cachedErrorRoomIds` 死代码字段 | ✅ 已确认,字段已不存在 |
| R17-N2 | 搜索活跃时非匹配房间 alpha 0.28 → 0.08 | ✅ 已确认,第 335 行 |
| R17-N3 | 搜索无结果时居中显示提示文本 | ✅ 已确认,第 363373 行 |
---
## R18 全方面评分(修复前)
| 维度 | 权重 | 分数 | 说明 |
|------|------|------|------|
| 架构解耦 | 20% | 95 | 四接口全 ServiceLocator事件单向流无循环依赖服务订阅 Awake 长期持有不随 OnEnable/Disable 失效 |
| 功能完整性 | 20% | 92 | 三级可见性、Pin、传送、本地化区域名、地图碎片解锁动画、存档一致全部覆盖 |
| 性能 | 15% | 91 | 三池cell/pin/exit+ O(viewRadius²) 剔除 + PinsVersion 脏检查 + _servicesReady 短路N1 键盘平移未乘缩放N3 MinimapHUD 双重刷新 |
| 编辑器工具 | 15% | 93 | 布局编辑器:拖拽/搜索/图例/Play Mode 叠加/Undo-Redo/AssetPostprocessor 自动注册R17 UX 修复有效N2 每帧重建 noResultStyle |
| 可扩展性 | 10% | 95 | RoomType[Flags] 可任意叠加新类型SO 驱动接口易替换MapPinConfigSO 集中 Pin 配置 |
| 代码质量 | 10% | 93 | 命名规范、注释充实、防御拷贝三处对称N2 GUIStyle 每 OnGUI 帧重建 |
| 玩家体验设计 | 10% | 88 | 键盘平移范围未乘 `_zoom`,缩放后平移速度感知偏差 |
**综合修复前92.4 / 100**
---
## R18 新发现问题
### N1正确性`MapInputHandler` 键盘平移范围未乘缩放系数
**文件**`Assets/_Game/Scripts/World/Map/MapInputHandler.cs` 第 7687 行
**现状**
```csharp
Vector2 contentSize = content.rect.size;
Vector2 viewportSize = viewport.rect.size;
float rangeX = contentSize.x - viewportSize.x;
float rangeY = contentSize.y - viewportSize.y;
```
`RectTransform.rect.size` 是本地空间(未缩放)的尺寸。当 `_zoomTarget.localScale = (_zoom, _zoom, 1)` 改变后,内容在屏幕上的视觉尺寸为 `contentSize × _zoom`ScrollRect 的实际可滚动范围也随之放大。
当前代码中 `rangeX = contentSize.x - viewportSize.x` 不含缩放因子,导致:
- 放大时(`_zoom > 1`):每帧移动距离 `delta.x / rangeX` 被高估,平移速度感知快于预期;
- 缩小时(`_zoom < 1`):平移速度感知慢于预期;
- 同样问题影响 `MapPanel.CenterOnCurrentRoom` 中的 `rangeX / rangeY` 计算。
**修复**
```csharp
// MapInputHandler.Update
float rangeX = contentSize.x * _zoom - viewportSize.x;
float rangeY = contentSize.y * _zoom - viewportSize.y;
// MapPanel.CenterOnCurrentRoom — 同步修复 rangeX/rangeY
float rangeX = contentSize.x * /* zoom */ - viewSize.x; // 需从 MapInputHandler 传入或独立持有
```
> **注意**`MapPanel.CenterOnCurrentRoom` 中无法直接读取 `MapInputHandler._zoom`;最简方案是从 `_roomContainer.localScale.x` 读取当前缩放值。
---
### N2编辑器性能`noResultStyle` 每次 `OnGUI` 重新分配
**文件**`Assets/_Game/Scripts/Editor/World/Map/MapLayoutEditorWindow.cs` 第 365371 行
**现状**
```csharp
var noResultStyle = new GUIStyle(EditorStyles.boldLabel)
{
alignment = TextAnchor.MiddleCenter,
normal = { textColor = new Color(1f, 0.8f, 0.2f, 0.8f) },
fontSize = 13,
};
```
此段仅在"搜索有内容 && 无匹配"时执行,但每次 `OnGUI`(编辑器 60 fps 或更高频率)均分配一个新 `GUIStyle` 对象。与已有的 `_roomLabelStyle``_badgeBossStyle``_badgeNormalStyle` 的缓存模式不一致。
**修复**:添加 `_noResultStyle` 字段,在 `EnsureLabelStyles()` 中一并初始化(`fontSize = 13` 固定,无需随 `_zoom` 变化)。
---
### N3架构一致性`MinimapHUD` 保留 `_onMapUpdated` 订阅导致双重刷新
**文件**`Assets/_Game/Scripts/World/Map/MinimapHUD.cs` 第 90 行 + 第 205209 行
**现状**
```csharp
// OnEnable:
_onMapUpdated?.Subscribe(OnMapUpdated).AddTo(_subs);
// OnMapUpdated:
private void OnMapUpdated(string roomId)
{
if (_cells.TryGetValue(roomId, out var cell))
cell.SetVisibility(_mapSvc.GetVisibility(roomId));
}
```
`MapPanel` 已在 R12-N8 移除 `_onMapUpdated` 订阅,改由 `OnExplorationChanged` 统一处理。但 `MinimapHUD` 未跟进:
每次房间被探索或标记时,`MapManager` 先后触发:
1. `_onMapUpdated?.Raise(roomId)``MinimapHUD.OnMapUpdated(roomId)` — 更新单个格子
2. `OnExplorationChanged?.Invoke()``MinimapHUD.OnExplorationChanged()` — 刷新全部活跃格子
步骤 1 的工作完全被步骤 2 覆盖造成重复写入。对于小视野49 格)影响可忽略,但在视野半径较大时每次探索会多做一次无效的单格更新。
**修复**:从 `MinimapHUD` 移除 `_onMapUpdated` SerializeField 及其订阅,保留 `[HideInInspector, SerializeField]` 字段并标注废弃说明(与 MapPanel 的处理方式对齐,保留 Prefab 序列化兼容性),删除 `OnMapUpdated` 私有方法。
---
## 架构深度审查
### 接口层4 接口)
| 接口 | 实现 | 注册方式 | 状态 |
|------|------|----------|------|
| `IMapService` | `MapManager` | `ServiceLocator.Register<T>` in Awake | ✅ 完整 |
| `IPinService` | `MapPinManager` | ServiceLocator in Awake | ✅ 完整 |
| `IPlayerPositionProvider` | `MapPlayerTracker` | ServiceLocator in Awake | ✅ 完整 |
| `ITeleportService` | `TeleportService` | ServiceLocator in Awake | ✅ 完整 |
### 存档一致性3 处 ISaveable
| 实现类 | 防御拷贝 | 覆盖加载 | 广播更新 |
|--------|----------|----------|----------|
| `MapManager.OnSave/OnLoad` | ✅ `new HashSet<>(_x)` | ✅ `new HashSet<>` 后赋值 | ✅ `OnExplorationChanged?.Invoke()` |
| `MapPinManager.OnSave/OnLoad` | ✅ `new List<>(_pins)` | ✅ `new List<>` 后赋值 | ✅ `PinsVersion++` |
| `TeleportService.OnSave/OnLoad` | ✅ `new HashSet<>(_unlockedRoomIds)` | ✅ Clear + foreach Add | ✅(通过传送服务事件) |
### 对象池完整性
| 池 | 容纳类型 | 所在组件 | 入池时机 |
|----|----------|----------|----------|
| `_cellPool` | MapRoomCellUI | MapPanel | RebuildAll |
| `_pinPool` | Image (Pin) | MapPanel | ClearPins |
| `_exitPool` | Image (Exit) | MapPanel | ClearExits |
| `_cellPool` | MapRoomCellUI | MinimapHUD | RefreshView 裁剪过期格子 |
| `_pinPool` | Image (Pin) | MinimapHUD | ClearPins |
### 性能关键路径
| 路径 | 复杂度 | 机制 |
|------|--------|------|
| `MinimapHUD.RefreshView` 新格子查询 | O(viewRadius²) | `MapDatabaseSO.GetRoomIdAtCell` 共享空间索引 |
| `MapPanel.LateUpdate` 服务查询 | O(1) 短路 | `_servicesReady` bool 门控 |
| `MapPanel.LateUpdate` Pin 渲染 | O(1) 检查 | `PinsVersion` 脏检查 |
| `MapManager.GetRoomsByRegion` | O(1) | `_regionCache` 懒加载字典 |
| `MapDatabaseSO.GetRoom` | O(1) | `_index` 字符串→SO 哈希索引 |
| `MapDatabaseSO.GetRoomIdAtCell` | O(1) | `_cellToRoom` 格子坐标→ID 空间索引 |
| `MinimapHUD.UpdatePlayerDot` | O(1) | roomId + NormPos 双字段脏检查 |
| `MapLayoutEditorWindow.EnsureLabelStyles` | O(1) | `_cachedZoomForStyle` 脏检查 |
### 编辑器工具覆盖面
| 工具 | 能力 |
|------|------|
| `MapLayoutEditorWindow` | 可视化布局预览;滚轮缩放;中键/Alt 平移房间拖拽编辑Undo区域配色验证 + 错误高亮搜索高亮R17 UX 改善图例Play Mode 玩家点叠加 |
| `MapDatabaseEditor` | Inspector 内嵌"在布局编辑器中打开"按钮 |
| `MapRoomDataEditor` | SceneGUI 双角控制点可视化 GridPosition/GridSize |
| `MapRoomAutoRegister` | `AssetPostprocessor` 自动注册新 SO 到默认 Database删除 null 清理;可禁用 |
---
## R18 修复预期评分
| 维度 | 权重 | 当前 | 修复后 | 变化 |
|------|------|------|--------|------|
| 架构解耦 | 20% | 95 | 95 | — |
| 功能完整性 | 20% | 92 | 92 | — |
| 性能 | 15% | 91 | 93 | +2N1+N3|
| 编辑器工具 | 15% | 93 | 94 | +1N2|
| 可扩展性 | 10% | 95 | 95 | — |
| 代码质量 | 10% | 93 | 95 | +2N2+N3 架构一致)|
| 玩家体验设计 | 10% | 88 | 92 | +4N1 修复后平移手感正确)|
**综合修复前92.4 / 100 → 修复后93.8 / 100**
---
## 总结
经过 18 轮迭代,小地图系统整体架构已达到生产级 2D 探索类游戏标准:
- **接口完整**四接口零具体类依赖ServiceLocator 完全解耦
- **存档对称**:三处 ISaveable 均实现防御拷贝,读档后广播正确事件
- **性能到位**:五池 + O(viewRadius²) + 多级脏检查 + _servicesReady 短路
- **编辑器成熟**拖拽、搜索、Undo、Play Mode 叠加、自动注册一体化
R18 新发现 3 项问题均为低/中优先级不影响功能正确性N1 影响手感N2/N3 为轻微性能与架构一致性问题)。