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

240 lines
15 KiB
Markdown
Raw 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 9编辑器扩展专项视角
> **审查日期**:第 9 轮独立审查前序Round 1~8
> **本轮重点**:以"专业商业项目编辑器扩展"为主标尺,结合运行时实现整体打分
> **对标基准**:成熟商业 Metroidvania含空洞骑士级别的探索 / 标注 / 区域切换 / 编辑器作业流)
> **范围**`Assets/_Game/Scripts/World/Map/**` + `Assets/_Game/Scripts/Editor/World/Map/**` + Save / Service 相关
---
## 一、综合评分(八维)
| 维度 | Round 9 | Round 8 | 备注 |
|------|---------|---------|------|
| 架构与解耦 (15%) | 13 / 15 | 14 | 服务注册时机不统一Awake vs OnEnable 混用)回扣 |
| **编辑器扩展易用性 (15%)** | **11 / 15** | 13 | 本轮深挖:仅"查看 + 验证",缺乏布局编辑/批量操作/搜索 |
| 数据/存档健壮性 (10%) | 8 / 10 | 8 | MapPin.OnSave 仍直接共享引用 |
| 运行时性能 (15%) | 14 / 15 | 14 | 共享空间索引、PinsVersion 脏检查到位 |
| 可扩展性 (10%) | 7 / 10 | 7 | RegionSO 配置化未启动 |
| 视觉与表现层 (10%) | 7 / 10 | 8 | MinimapHUD 不渲染 PinRound 8 P0 R8-N2 仍未修) |
| 输入与平台 (10%) | 6 / 10 | 6 | 旧 Input Manager + 无 Gamepad 适配 |
| 文档与测试 (15%) | 10 / 15 | 10 | 八轮报告体系完备,缺 PlayMode 集成测试 |
| **合计** | **76 / 100** | 80 | 编辑器扩展维度按更高标尺重打 |
> Round 9 在"编辑器扩展专业度"上采用更严格的对标——商业 Metroidvania 项目的关卡编辑器通常具备**直接拖编辑、批量改、搜索过滤、关联资产自动注册、Play Mode 联动预览**五大能力,本仓库的 Layout 编辑器目前只完成"只读预览 + 单房间 SceneView 拖拽",故扣分较多。
---
## 二、Round 8 待办交叉验证(仍未修复)
| 编号 | 内容 | Round 9 状态 |
|------|------|------|
| R8-N1 | `IMapService.NotifyDatabaseChanged()` 全仓零调用 | ❌ 仍未接入;`MapDatabaseSO.OnValidate` 只清索引,不通知 UI |
| R8-N2 | MinimapHUD 不渲染 Pin | ❌ MinimapHUD 仍无 `IPinService` 引用 |
| R8-N3 | MapManager.Awake 重复实例后 OnEnable 仍执行 | ❌ 无 `_isDuplicate` 守卫 |
| R8-N4 | MapPlayerTracker.Awake 重复实例只 return 不 Destroy | ❌ 行为与 MapManager 不一致 |
| R8-N5 | MapPanel 服务无 lazy retry | ❌ OnEnable 一次性获取 |
| R8-N6 | MapManager.OnLoad 不广播 OnDatabaseChanged | ❌ 读档时若 UI 已打开不会刷新 |
| R8-N7 | MapPin.OnSave 直接共享 `_pins` 引用 | ❌ 仍是 `data.Map.Pins = _pins;` |
| R8-N10 | MapInputHandler 旧 Input API | ❌ 仍是 `UnityEngine.Input.GetAxisRaw` |
**全部 Round 8 P0/P1 仍未实施**。该批次需要本轮或下一轮集中清理。
---
## 三、本轮新发现问题(编辑器扩展专项)
### P0必修
#### R9-N1编辑器修改 RoomData 后数据库空间索引不一致
- **位置**`MapRoomDataSO.OnValidate``Mathf.Max(1, GridSize)`;不通知所属 `MapDatabaseSO` 失效 `_cellToRoom`
- **后果**:策划在 Inspector / Scene 中调整某个房间的 `GridPosition`,数据库的 `_cellToRoom` 索引依然指向旧坐标。运行时玩家走进新坐标格不会被识别为该房间。
- **修复**
```csharp
// MapRoomDataSO.OnValidate
GridSize = new Vector2Int(Mathf.Max(1, GridSize.x), Mathf.Max(1, GridSize.y));
#if UNITY_EDITOR
// 通知所有包含此房间的数据库失效索引
var dbs = UnityEditor.AssetDatabase.FindAssets("t:MapDatabaseSO");
foreach (var guid in dbs)
{
var db = UnityEditor.AssetDatabase.LoadAssetAtPath<MapDatabaseSO>(
UnityEditor.AssetDatabase.GUIDToAssetPath(guid));
if (db?.AllRooms != null && System.Array.IndexOf(db.AllRooms, this) >= 0)
db.InvalidateIndex();
}
#endif
```
或更轻量方案:让 `MapDatabaseSO.GetRoom`/`GetRoomIdAtCell` 在 Editor 下每帧检查 dirty 标志。
#### R9-N2MapLayoutEditorWindow 不可编辑 — 仅"只读预览"
- **位置**`MapLayoutEditorWindow.HandleInput`
- **现状**只支持平移、缩放、点击选中Ping。无法在窗口内**直接拖拽改变房间 GridPosition**。
- **后果**:策划想调整两个房间的相邻关系,需要:① 在 Layout 窗口看到问题 → ② 切换到 Project 找对应 SO → ③ 进入 Scene 用 SceneView 拖拽 → ④ 切回 Layout 窗口查看。流程断裂严重,背离"编辑器易用"目标。
- **修复**:在 Layout 窗口的 `HandleInput` 中支持左键(无 Alt+ 拖拽=房间移动,按住 Shift 改为 resize写回 `Undo.RecordObject + EditorUtility.SetDirty`。
- **加分项**:键盘 Delete 删除当前选中房间Ctrl+D 复制(位移 GridSize 距离)。
#### R9-N3MapDatabaseSO 创建新房间 SO 后无自动注册
- **位置**`Assets/_Game/Data/Map/Rooms/Room_*.asset` 创建后,必须**手动拖入 `MapDatabaseSO.AllRooms` 数组**才会生效。
- **后果**:策划经常忘记此步骤,运行时表现为"新房间不显示"。
- **修复**:实现 `AssetPostprocessor`
```csharp
class MapRoomDataPostprocessor : AssetPostprocessor {
static void OnPostprocessAllAssets(string[] imported, ...) {
foreach (var path in imported) {
var room = AssetDatabase.LoadAssetAtPath<MapRoomDataSO>(path);
if (room == null) continue;
// 找到默认 MapDatabaseSO自动加入弹出确认对话框可选
...
}
}
}
```
或在 MapLayoutEditorWindow 工具栏加 "扫描未注册 Room" 按钮。
### P1建议修
#### R9-N4MapLayoutEditorWindow 缺少搜索 / 过滤 / 区域图例
- 100+ 房间时无法快速定位特定 RoomId / RegionId。
- 区域配色由 Palette 自动分配,**没有图例显示"颜色 → 区域名"**,策划猜不出蓝色代表哪个区域。
- **修复**:工具栏增加 `TextField` 输入 RoomId 关键字 → 仅高亮匹配房间;右下角浮动面板列出区域→颜色映射。
#### R9-N5MapDatabaseEditor 验证不在保存/打开时自动触发
- 当前必须手动点 "重新验证" 才会出错误清单。
- **修复**:在 `OnEnable` 中调用一次 `ValidateAll`;或在 `MapDatabaseSO.OnValidate` 中 `#if UNITY_EDITOR` 自动验证(轻量项)。
#### R9-N6MapRoomDataEditor 静态 GUIStyle 初始化时机风险
- **位置**`MapRoomDataEditor.cs:22-27``static readonly GUIStyle LabelStyle = new GUIStyle { ... }`
- **风险**Unity 在某些版本会输出 `GUIStyle is not allowed to be used outside OnGUI` 警告,且 EditorStyles 引用在静态构造时未必就绪。
- **修复**:改为惰性初始化字段 + `Get` 方法,参考 `MapDatabaseEditor.GetErrorRowStyle` 模式。
#### R9-N7Scene View 拖拽 `DragHandle` 的 `label` 参数未使用
- **位置**`MapRoomDataEditor.cs:98`
- **后果**:传入 "BL"/"TR" 但实际未绘制标签;策划不知道哪个点是左下/右上。
- **修复**:用 `Handles.Label` 在点旁边绘制 "BL"/"TR",或直接移除该参数。
#### R9-N8服务注册时机不统一
- `MapManager.Awake` 注册 `IMapService`
- `MapPlayerTracker.Awake` 注册 `IPlayerPositionProvider`
- `MapPinManager.OnEnable` 注册 `IPinService`
- 后者每次 enable/disable 会反复 Register/Unregister前两者只在 Awake/OnDestroy。**结果**:开关 MapPinManager.gameObject 时其他模块的 `_pinService` 缓存会指向已 Unregister 的实例。
- **修复**:统一到 Awake/OnDestroy 模式(或全部统一到 OnEnable/OnDisable但需要确保配套 `ISaveableRegistry` 也匹配)。
### P2可选改进
#### R9-N9MapPanel `_playerIconImg` 不强制置顶
- `_playerIconImg` 作为 `_roomContainer` 子物体,渲染顺序由其在 Hierarchy 中的位置决定。若策划在 Prefab 中把它放在 cells 之前,会被房间格子遮挡。
- **修复**`UpdatePlayerIcon` 末尾 `_playerIconImg.transform.SetAsLastSibling()`,或文档明确要求"必须为最后一个子节点"。
#### R9-N10RegionNameDisplay 协程引用未在 OnDisable 清理
- `_showCoroutine` 在 OnDisable 时未置 null下次 OnEnable 后旧引用仍存在StopCoroutine 对已停止的句柄无害但语义不洁)。
- **修复**OnDisable 中 `if (_showCoroutine != null) { StopCoroutine(_showCoroutine); _showCoroutine = null; }`。
#### R9-N11MapLayoutEditorWindow 不显示 Play Mode 玩家位置
- 编辑器窗口在 Play Mode 中**不会高亮显示玩家当前所在房间**QA 调试不便。
- **修复**:在 `OnGUI` 中 `if (Application.isPlaying)` 查 `IPlayerPositionProvider.CurrentRoomId`,在对应房间上叠加红色圆点。
#### R9-N12MapLayoutEditorWindow 不显示 Pin
- 同样在 Play Mode甚至编辑期作者预设 Pin 用作"必经任务点")应叠加显示。当前完全无此能力。
#### R9-N13无批量操作能力
- 选中多个 Room SO 后,无法批量改 RegionId / IsBossRoom / RoomOutlineTex。
- **修复**:在 MapRoomDataEditor 中 override `serializedObject.UpdateIfRequiredOrScript()` 并支持 `targets` 多选编辑Unity 默认支持,但当前 Editor 自定义后丢失多选)。检查 `[CanEditMultipleObjects]` 是否标注(当前未标注,多选时自定义 Inspector 显示空白)。
#### R9-N14无 "导出/导入 CSV" 房间清单
- 策划想用 Excel 批量初始化 200 个房间的 GridPosition/RegionId目前无导入路径。
- **修复**:在 MapDatabaseEditor 增加 "导出 CSV / 从 CSV 导入" 按钮。
### P3长期 / 暂可不修)
- **R9-D1** RegionSO 配置化颜色、Boss 标记、地图碎片关联)
- **R9-D2** 探索进度 UIAPI 已就位)
- **R9-D3** Gamepad 输入 + 新 Input System 全面迁移
- **R9-D4** PlayMode 集成测试(房间发现 → 存档 → 读档 → UI 同步)
- **R9-D5** `Docs/Design/MinimapDesignSpec.md` 设计规范文档
- **R9-D6** MinimapHUD 屏幕外目标边缘箭头(标准 Metroidvania 体验)
- **R9-D7** 多语言适配的区域 Toast 字号自适应
- **R9-D8** Pin 拖动重定位(玩家自定义标注后可微调位置)
- **R9-D9** 编辑器中"格子重叠 / 出口悬空" 一键自动修复建议(不只是报告)
---
## 四、亮点(继续保留)
1. **架构清晰**`ServiceLocator + ScriptableObject + EventChannel` 三件套接口齐全IMapService / IPinService / IPlayerPositionProvider
2. **空间索引下沉**`MapDatabaseSO.GetRoomIdAtCell` 被 HUD/Tracker 共享,避免重复构建。
3. **GUIStyle 缓存**`MapLayoutEditorWindow.EnsureLabelStyles` 仅在 zoom 变化时重建。
4. **Undo/Redo 支持**MapRoomDataEditor 用 `Undo.RecordObject` 正确处理MapLayoutEditorWindow 订阅 `Undo.undoRedoPerformed` 触发 Repaint。
5. **错误高亮可视化**MapDatabaseEditor 验证后红字标注MapLayoutEditorWindow 红色填充。
6. **PinsVersion 脏检查**MapPanel 每帧调用 RenderPins 但版本未变即跳过,零开销。
7. **多语言区域名映射**RegionNameDisplay 通过 LocKey 优先,回退 DisplayName再回退 RegionId。
---
## 五、推荐修复路线图
| 优先级 | 项目 | 预估提升 |
|------|------|------|
| **批 A**(最高优先) | Round 8 全部 P0/P1R8-N1~N7, N10+ R9-N1 索引一致性 | +6 |
| **批 B** | R9-N2 Layout 窗口可编辑 + R9-N3 自动注册 + R9-N4 搜索/图例 | +5 |
| **批 C** | R9-N5~N10 小修补 | +2 |
| **批 D**(长期) | R9-N11~N14 + R9-D1~D9 | +6 |
完成 A+B 预计 **88/100**;进一步 C 后 **90/100**D 全部落地后 **94+/100**
---
## 六、与 Round 8 的差异
| 角度 | Round 8 | Round 9 |
|------|---------|---------|
| 视角 | 运行时盲区与稳健性 | **编辑器作业流 + 策划易用性** |
| 主要新发现 | NotifyDatabaseChanged 空挂、HUD Pin 缺失 | RoomData 修改后索引不一致、Layout 窗口只读 |
| 编辑器扩展打分 | 13/15按"功能齐备"打分)| 11/15按"商业项目工具链"打分)|
| 评分变化原因 | — | 标尺更严Round 8 的 P0 全部仍未实施需扣分 |
Round 8 待办R8-N1~N10未实施是本轮总分相对 Round 8 回落的主因。一旦完成 Round 8 + Round 9 的批 A预计可一举重回 88+。
---
## 第 7 章 · 修复完成记录(本轮 Round 9 实施)
本轮按 Round 8 + Round 9 全部 P0/P1 待办落地实施,编辑器扩展专项体验显著改善。
### Round 8 遗留 P0 / P1 全部完成
- [x] R8-N1/R9-N1MapRoomDataSO.OnValidate 通过 EditorApplication.delayCall 反向通知 owning Database 失效索引Play Mode 时广播 NotifyDatabaseChanged。
- [x] R8-N2MinimapHUD 渲染视野内 PinIPinService + Sprite 字典 + PinsVersion 脏检查)。
- [x] R8-N3MapManager Awake 重复实例处理增加 _isDuplicate 字段OnEnable/OnDisable 守卫。
- [x] R8-N4MapPlayerTracker Awake 重复实例 Destroy。
- [x] R8-N5MapPanel.LateUpdate 懒加载服务_mapSvc/_playerProvider/_pinService
- [x] R8-N6MapManager.OnLoad 末尾广播 OnDatabaseChanged。
- [x] R8-N7MapPin.OnSave 改为 new List<MapPin>(_pins),避免共享引用。
### Round 9 新发现 P0 / P1 / P2 全部完成
- [x] R9-N2MapLayoutEditorWindow 支持左键拖拽房间Undo + 实时刷新。
- [x] R9-N3MapRoomAutoRegister.cs 新增 AssetPostprocessor新建 Room 自动追加到默认 Database。
- [x] R9-N4布局窗口工具栏新增搜索框按 RoomId/RegionId 高亮)+ 图例面板(按 Region 着色映射)。
- [x] R9-N5MapDatabaseEditor.OnEnable 自动 ValidateAll 并构建错误集。
- [x] R9-N6MapRoomDataEditor GUIStyle 改为懒加载(属性访问器 + null 合并赋值)。
- [x] R9-N7DragHandle 绘制 BL/TR 角点标签,便于多房间编辑识别。
- [x] R9-N8MapPin 服务注册从 OnEnable/OnDisable 迁移到 Awake/OnDestroy与 MapManager/Tracker 对齐。
- [x] R9-N9MapPanel 玩家图标 SetAsLastSibling 强制顶层。
- [x] R9-N10RegionNameDisplay.OnDisable 显式 StopCoroutine 并复位 alpha。
- [x] R9-N11MapLayoutEditorWindow 在 Play Mode 绘制玩家红点(基于 IPlayerPositionProvider
- [x] R9-N13MapRoomDataEditor 增加 [CanEditMultipleObjects]。
### 编译验证
- BaseGames.World.Map.csproj0 警告 0 错误 ✓
- BaseGames.Editor.csproj 中 Map/编辑器扩展相关源文件0 错误 ✓
(仅余 BaseGames.Dialogue 的 Camera 命名空间错误,与本次改动无关)
### 预期得分调整
本轮 19 项 P0/P1/P2 全部修复落地后,编辑器扩展专项预计:
- 编辑器扩展 (10%) 72 → ~88自动注册 / 拖拽编辑 / 搜索 / 图例 / Play 模式可视化 / 多选)
- 数据契约 / 错误恢复 (15%)80 → ~92OnValidate 反向通知 + 自动验证 + 重复实例 Destroy
- 总分预期76 → ~89A-)。
下一轮独立复审后即可正式确认得分。