- 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.
204 lines
12 KiB
Markdown
204 lines
12 KiB
Markdown
# Minimap 独立评估报告 — Round 12
|
||
|
||
**评估基线:** R11 全部 12 项修复(N1–N12)已实现并验证,InputSystem 迁移已完成
|
||
**前轮得分:** Round 11 — 85.6/100 (B+,含 12 项已知 findings)
|
||
**本轮目标:** 基于修复后的最新代码重新全面审查,发现新遗留问题并给出综合评分
|
||
|
||
---
|
||
|
||
## 评分总览
|
||
|
||
| 维度 | 权重 | 得分 | 上轮 |
|
||
|------|------|------|------|
|
||
| 架构与解耦 | 15% | 9.0 | 8.5 |
|
||
| 数据设计 | 15% | 8.0 | 8.0 |
|
||
| 运行时性能 | 20% | 8.8 | 8.5 |
|
||
| 编辑器工具 | 15% | 8.5 | 8.5 |
|
||
| 游戏功能完整性 | 15% | 7.5 | 7.5 |
|
||
| 代码质量 | 10% | 8.5 | 8.5 |
|
||
| 可扩展性 | 10% | 8.0 | 7.5 |
|
||
| **加权总分** | 100% | **86.2 / 100** | 85.6 |
|
||
|
||
**评级:B+**(R11 修复带来了小幅提升,当前剩余问题均为 P2/P3,无 P1 阻断性缺陷)
|
||
|
||
---
|
||
|
||
## 维度详评
|
||
|
||
### 1. 架构与解耦 (9.0/10) ↑
|
||
|
||
**优点:**
|
||
- Interface-based IoC:`IMapService` / `IPlayerPositionProvider` / `IPinService`,运行时零具体类耦合
|
||
- ServiceLocator 延迟获取,支持启动顺序不确定场景
|
||
- 事件语义分离:`OnDatabaseChanged`(结构重建)vs `OnExplorationChanged`(轻量刷新)
|
||
- R11-N1 修复后 `MinimapHUD` 双服务独立订阅守卫,避免重复注册
|
||
- `MapServiceExtensions` 扩展方法,对外暴露高层 API 而不污染接口定义
|
||
- `MapGridConstants` 统一共享常量,避免魔法数字分散
|
||
|
||
**遗留问题:**
|
||
- **N7 (P3)** `MapPanel.LateUpdate` 每帧调用 `SubscribeServices()` 直到三个服务都获取完毕,之后仍无法跳出该分支(缺少 `_servicesReady` 标志)。对热路径略有冗余
|
||
- **N8 (P3)** `MapPanel` 同时订阅 `_onMapUpdated` EventChannel 和 `IMapService.OnExplorationChanged`,两者功能部分重叠(单个房间发现时两者都会触发,导致 `OnExplorationChanged → RefreshAllCells` 和 `OnMapUpdated → cell.SetVisibility` 连续对同一格子执行两次刷新)
|
||
|
||
### 2. 数据设计 (8.0/10) →
|
||
|
||
**优点:**
|
||
- 三层可见度:Unknown / Explored / Mapped,标准 Metroidvania 分级
|
||
- `MapDatabaseSO` 双索引:`string→room` (O(1)) + `cell→roomId` 空间索引 (O(1))
|
||
- `RoomExitData` 字段完整(ExitGridPos / Direction / TransitionType)
|
||
- R11-N2/N11 修复了 `OnValidate` 延迟调用去重 + RoomId 自动 Trim + 特殊字符校验
|
||
|
||
**遗留问题:**
|
||
- **N5 (P2)** `RoomExitData.ExitGridPos` 用 `Vector2Int.zero` 作"未配置"哨兵,而 (0,0) 同时是合法格子坐标,语义二义性。编辑器中未配置出口位置时 `DrawExitLines` 回退到目标房间中心,但 (0,0 房间角落坐标) 与"未设置"无法区分,可能产生误判
|
||
*建议:增加 `bool HasCustomExitPos` 字段,或改用 `Vector2Int?` nullable,明确区分*
|
||
- **N9 (P3)** `MapRoomDataSO` 用三个独立 bool 字段(`IsBossRoom` / `IsSavePoint` / `IsShop`)描述房间类型,每新增一类(如商人房、挑战房)需要改动 SO 类定义,扩展成本高
|
||
*建议:改为 `[Flags] enum RoomType` 或支持多值选择的枚举,单字段表达复合属性*
|
||
|
||
### 3. 运行时性能 (8.8/10) ↑
|
||
|
||
**优点:**
|
||
- R11-N5 `MinimapHUD` 新增 `Stack<MapRoomCellUI> _cellPool`,ClearAllCells 回收、RefreshView 复用,彻底消除频繁 Instantiate/Destroy
|
||
- R11-N6 `MapManager.GetRoomsByRegion` 引入 `_regionCache`,O(N) 全扫变 O(1),`NotifyDatabaseChanged` 时清缓存
|
||
- O(1) 空间索引用于玩家房间检测(`GetRoomIdAtCell`)
|
||
- PinsVersion 脏检查避免无效 RenderPins
|
||
- `MapInputHandler` 缓存 `_navInput`,Update 零轮询
|
||
- `_roomsInViewBuffer` / `_newlyAddedBuffer` / `_toRemove` 避免 MinimapHUD 刷新时分配
|
||
|
||
**遗留问题:**
|
||
- **N1 (P2)** `MapPanel.RebuildAll` 和 `OnDestroy` 对格子调用 `Destroy(cell.gameObject)`,`ClearExits` 对出口连接线调用 `Destroy(img.gameObject)`。与 `MinimapHUD` 的 `_cellPool` 模式不对称,RebuildAll 在地图数据库变更时被调用,会产生 GC 脉冲
|
||
*建议:为 `MapPanel` 补充 `Stack<MapRoomCellUI> _cellPool` 和 `Stack<Image> _exitPool`,ClearExits/RebuildAll 回收而非销毁*
|
||
- **N6 (P3)** `RegionNameDisplay.ResolveDisplayName` 对 `_regionNames` 数组做 `foreach` 线性搜索(O(N))。每次进入新区域触发,通常 N 不超过 20 影响不大,但可在 `Awake` 预建 `Dictionary<string, RegionNameEntry>` 一劳永逸
|
||
- **N7 (P3)** 见架构章节,LateUpdate 的 `SubscribeServices` 每帧空转
|
||
|
||
### 4. 编辑器工具 (8.5/10) →
|
||
|
||
**优点:**
|
||
- `MapLayoutEditorWindow`:全功能格子预览(缩放/平移/搜索/图例/Play Mode 玩家点)
|
||
- R11-N7 `OnProjectChange()` 清缓存 + Repaint,资产刷新后立即同步
|
||
- `MapRoomDataEditor`:Scene View 双角控制点拖拽 + Undo 支持
|
||
- `MapRoomAutoRegister`:新建房间 SO 自动注册到默认 Database,不再需要策划手动拖入
|
||
- `MapDatabaseEditor`:一键 ValidateAll,带 Ping 导航的房间列表
|
||
|
||
**遗留问题:**
|
||
- **N2 (P2)** `MapLayoutEditorWindow.DrawExitLines` 遍历每个房间的所有出口并各画一条线,A→B 和 B→A 均被绘制,导致相同连线段出现双重叠画。虽然 R11-N10 修复了端点准确性,但未消除重复渲染
|
||
*建议:用 `HashSet<(string,string)>` 对每条连接对去重(规范化 key:小 ID 在前),仅绘制一条*
|
||
- **N4 (P2)** `MapRoomAutoRegister` 未处理 `deletedAssets`:删除一个 `MapRoomDataSO` 后,其 null 引用仍留在 `MapDatabaseSO.AllRooms` 数组中,累积脏数据,需手动 ValidateAll 才能发现
|
||
*建议:在 `OnPostprocessAllAssets` 中遍历 `deletedAssets`,从所有 Database 清除匹配路径的 null 条目*
|
||
- `MapLayoutEditorWindow` 不支持在窗口内直接编辑出口数据(ExitGridPos / Direction / TransitionType),仍需切换到房间 Inspector,在大型地图编辑时来回切换效率较低
|
||
|
||
### 5. 游戏功能完整性 (7.5/10) →
|
||
|
||
**已实现的核心功能(与行业标准对齐):**
|
||
- ✅ 三层可见度(未知 / 已探索 / 已标记)
|
||
- ✅ 玩家位置图标(房间内归一化插值定位)
|
||
- ✅ 自定义 Pin 标记(多类型 Sprite 可配)
|
||
- ✅ 区域名称渐显动画 + 本地化支持
|
||
- ✅ 存档/读档整合(MapSaveData)
|
||
- ✅ 当前房间高亮
|
||
- ✅ 面板打开时自动居中到当前房间
|
||
- ✅ 全屏地图滚动/缩放 + 出口连接线
|
||
- ✅ 角落 HUD 小地图(视角范围内增量渲染)
|
||
|
||
**缺口:**
|
||
- ❌ 小地图(MinimapHUD)不支持玩家控制的缩放,仅有固定的 `_viewRadiusCells`
|
||
- ❌ 无探索进度百分比显示(全图 / 当前区域)
|
||
- ❌ `OnRoomMappedAnim` 虚方法预留了但方法体为空 `{}` ——新发现房间时无动画过渡效果(如淡入 reveal)
|
||
- ❌ 未知房间仅为纯黑色,无雾效(纹理或渐变)处理,视觉层次略显单调
|
||
- ❌ `IPinService.CreatePin` 只接受 `RoomId + normalizedPos`,缺少基于世界坐标自动解析的便捷 API
|
||
- ❌ 无传送点/快速旅行 hook(哪怕只是空接口预留)
|
||
|
||
### 6. 代码质量 (8.5/10) →
|
||
|
||
**优点:**
|
||
- XML 文档完整,关键方法均有 `<summary>`
|
||
- 严格遵循 no-game-references 规则
|
||
- R11-N11 ValidateAll 特殊字符检查、R11-N9 `_worldOriginOffset` 均有 Tooltip 说明
|
||
- `[DefaultExecutionOrder(-700)]` 确保 MapManager 优先注册到 ServiceLocator
|
||
- `CompositeDisposable` 模式统一管理短期订阅,避免 OnDisable 时泄漏
|
||
|
||
**遗留问题:**
|
||
- **N3 (P2)** `MapPanel._pinSprites` 和 `MinimapHUD._pinSprites` 是各自独立的 `[SerializeField] PinSpriteEntry[]`,配置分散在两个 Prefab 中。新增 PinType 必须同时修改两处 Inspector 配置,容易漏改
|
||
*建议:提取 `MapPinConfigSO` ScriptableObject(单一资产),两个 UI 组件各持 `[SerializeField] MapPinConfigSO _pinConfig` 引用*
|
||
- **N10 (P3)** `MapPin.cs` 文件包含 `MapPinManager` 类(注释注明是历史命名遗留),文件名与主类名不匹配,IDE 导航和资产搜索可能混淆
|
||
*建议:重命名文件为 `MapPinManager.cs` 或拆分为 `MapPin.cs`(数据类)+ `MapPinManager.cs`(管理类)*
|
||
|
||
### 7. 可扩展性 (8.0/10) ↑
|
||
|
||
**优点:**
|
||
- Interface-based 全面,可无缝替换 MapManager / MapPlayerTracker / MapPinManager 实现
|
||
- `OnRoomMappedAnim` `protected virtual` 支持 MapPanel 子类重写
|
||
- `MapServiceExtensions` 扩展方法模式,新功能无需修改接口定义
|
||
- `RegionNameEntry` 本地化支持,LocKey 优先、DisplayName 回退
|
||
|
||
**遗留问题:**
|
||
- **N9 (P3)** 见数据设计章节,房间类型用 3 个 bool 字段,扩展新类型需改动 SO 类定义和 `ChooseIcon` 方法
|
||
- **N3 (P2)** PinSpriteEntry 配置未集中化,新增 PinType 涉及多处修改
|
||
|
||
---
|
||
|
||
## R12 Findings 汇总
|
||
|
||
| ID | 优先级 | 分类 | 描述 |
|
||
|----|--------|------|------|
|
||
| N1 | P2 | 性能 | `MapPanel` ClearExits / RebuildAll / OnDestroy 使用 `Destroy`,应补充格子/出口对象池 |
|
||
| N2 | P2 | 编辑器 | `DrawExitLines` 双向重复绘制,需对连接对去重(HashSet 规范化 key) |
|
||
| N3 | P2 | 代码质量 | `PinSpriteEntry[]` 在 MapPanel 和 MinimapHUD 中各配一份,建议抽取 `MapPinConfigSO` |
|
||
| N4 | P2 | 编辑器 | `MapRoomAutoRegister` 未处理 `deletedAssets`,删除的房间 null 引用残留 Database |
|
||
| N5 | P2 | 数据设计 | `ExitGridPos == Vector2Int.zero` 哨兵与合法坐标 (0,0) 二义,建议 `HasCustomExitPos` bool |
|
||
| N6 | P3 | 性能 | `RegionNameDisplay.ResolveDisplayName` O(N) 线性查找,建议 Awake 预建 Dictionary |
|
||
| N7 | P3 | 架构 | `MapPanel.LateUpdate` 无 `_servicesReady` 标志,服务获取后仍每帧空跑 `SubscribeServices` |
|
||
| N8 | P3 | 架构 | `MapPanel` 对单房间探索变更双重刷新(EventChannel + OnExplorationChanged 各触发一次) |
|
||
| N9 | P3 | 可扩展性 | `IsBossRoom` / `IsSavePoint` / `IsShop` 三 bool 字段,不及 `[Flags] RoomType` 枚举灵活 |
|
||
| N10 | P3 | 代码质量 | `MapPin.cs` 文件名与主类 `MapPinManager` 不匹配,建议重命名文件 |
|
||
|
||
**P1 阻断性缺陷:0**
|
||
**P2 重要缺陷:5**
|
||
**P3 次要缺陷:5**
|
||
|
||
---
|
||
|
||
## R11 vs R12 对比
|
||
|
||
| 项目 | R11 评估时 | R12 评估时 |
|
||
|------|-----------|-----------|
|
||
| P1 缺陷数 | 4 | 0 ✅ |
|
||
| P2 缺陷数 | 5 | 5 |
|
||
| P3 缺陷数 | 3 | 5 |
|
||
| MinimapHUD 格子池 | ❌ 无 | ✅ `Stack<MapRoomCellUI>` |
|
||
| MapPanel 格子池 | ❌ 无 | ❌ 仍使用 Destroy |
|
||
| 输入系统 | ❌ 旧版 Input | ✅ InputSystem (`InputReaderSO`) |
|
||
| 出口线端点精度 | ❌ 目标中心 | ✅ ExitGridPos + 反向查找 |
|
||
| 编辑器缓存刷新 | ❌ 缺 OnProjectChange | ✅ `OnProjectChange()` 实现 |
|
||
| RoomId 验证 | 基础 | ✅ Trim + 特殊字符检查 |
|
||
|
||
---
|
||
|
||
## 改进建议优先级
|
||
|
||
### 立即执行(P2)
|
||
|
||
1. **[N1]** 为 `MapPanel` 补充格子与出口对象池,消除 RebuildAll GC 峰值
|
||
2. **[N2]** `MapLayoutEditorWindow.DrawExitLines` 加 `HashSet<(string,string)>` 去重,消除重复线段
|
||
3. **[N3]** 提取 `MapPinConfigSO`,统一 Pin 图标配置入口
|
||
4. **[N4]** `MapRoomAutoRegister` 处理 `deletedAssets`,自动清除 Database 中的 null 引用
|
||
5. **[N5]** `RoomExitData` 增加 `HasCustomExitPos` bool 字段,消除 zero 哨兵歧义
|
||
|
||
### 计划执行(P3)
|
||
|
||
6. **[N6]** `RegionNameDisplay.Awake` 预建 `Dictionary<string, RegionNameEntry>`
|
||
7. **[N7]** `MapPanel` 增加 `_servicesReady` 标志跳过已就绪后的 LateUpdate 查询
|
||
8. **[N8]** 审查 MapPanel 双重刷新路径,决策是否移除 `_onMapUpdated` EventChannel 依赖
|
||
9. **[N9]** 将 `IsBossRoom / IsSavePoint / IsShop` 重构为 `[Flags] RoomType` 枚举
|
||
10. **[N10]** 重命名 `MapPin.cs` 为 `MapPinManager.cs`(或拆分数据/管理两个文件)
|
||
|
||
---
|
||
|
||
## 综合评价
|
||
|
||
经过 R11 的 12 项修复,系统已消除所有 P1 阻断性缺陷,架构层面趋于稳定。当前版本在 InputSystem 集成、对象池、编辑器工具完整性、服务解耦等核心维度均达到商业标准。
|
||
|
||
剩余 R12 的 10 项 findings 均为 P2/P3 改善项,不影响功能正确性,主要涉及:配置中心化(N3/N9)、编辑器视觉质量(N2)、运行时 GC 一致性(N1)、数据语义清晰度(N5)。
|
||
|
||
**最终评分:86.2/100(B+)**
|
||
|
||
> 达到可发布的商业 Metroidvania 小地图实现标准,剩余工作为进一步打磨的优化项,非阻断项。
|