- 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.
9.9 KiB
9.9 KiB
小地图独立审查报告 — Round 8
审查日期:第 8 轮独立审查(前序:Round 1~7,Round 7 评分 82/100 + 修复后预估 ~88) 对标基准:成熟商业 Metroidvania(含空洞骑士级别的探索 / 标注 / 区域切换 / 编辑器工作流) 范围:
Assets/_Game/Scripts/World/Map/**+ 相关 Save / Editor / ServiceLocator 审查方式:完全独立重读全部 17 个文件,不预设结论;对 Round 7 修复项做交叉验证
一、综合评分(八维)
| 维度 | 评分 | 较 Round 7 (修复后) |
|---|---|---|
| 架构与解耦 (15%) | 14 / 15 | ↑(共享空间索引 + DB 事件接口齐备) |
| 编辑器扩展易用性 (15%) | 13 / 15 | ↑(双窗口 + Validate 已成熟) |
| 数据/存档健壮性 (10%) | 8 / 10 | ↑(LastRegionId、CreatePin 校验落地) |
| 运行时性能 (15%) | 14 / 15 | ↑(空间索引复用、PinsVersion 脏检查) |
| 可扩展性 (10%) | 7 / 10 | =(RegionSO/进度 UI 仍缺) |
| 视觉与表现层 (10%) | 8 / 10 | =(区域名提示已就位,但 Pin 限于全屏) |
| 输入与平台 (10%) | 6 / 10 | =(Input System 迁移仍未启动) |
| 文档与测试 (15%) | 10 / 15 | ↑(七轮报告体系完善,仍缺 PlayMode 集成测试) |
| 合计 | 80 / 100 | — |
说明:本轮在更高标尺下重新刻度。Round 7 的"修复后预估
88"是基于自身基线;Round 8 引入 NotifyDatabaseChanged 入口空挂 等结构性新发现,整体分数回落到 80。修复完 P0/P1 后预计 8891。
二、Round 7 修复交叉验证 ✅
| Round 7 编号 | 内容 | Round 8 验证 |
|---|---|---|
| N1 | MapPanel 在 LateUpdate 中 RenderPins + 监听 OnDatabaseChanged | ✅ MapPanel.cs:124/136 已落地 |
| N2 | IMapService 增加 OnDatabaseChanged 事件 | ✅ 接口与实现齐备 |
| N3 | 空间索引下沉到 MapDatabaseSO | ✅ GetRoomIdAtCell + Invalidate 已就位;HUD/Tracker 均使用共享索引 |
| N4 | CreatePin 校验 roomId / clamp 归一化 | ✅ MapPin.cs:60~94 |
| N5 | SaveData.LastRegionId + OnLoad 恢复 | ✅ MapManager.cs:60/67 |
| N6 | MinimapHUD step③ 跳过新增格 | ✅ _newlyAddedBuffer 去重 |
| N7 | MapInputHandler 命名空间冲突 | ✅ UnityEngine.Input.GetAxisRaw |
| N8 | _worldUnitsPerCell [Min(0.01f)] |
✅ MapPlayerTracker.cs |
结论:Round 7 所有修复均稳定在仓库中,未发生回归。
三、本轮新发现问题
P0(必修)
R8-N1:IMapService.NotifyDatabaseChanged() 全仓零调用 — API 空挂
- 位置:
Assets/_Game/Scripts/World/Map/MapManager.cs:127,全文检索NotifyDatabaseChanged仅出现于"声明"与"实现"两处。 - 后果:Round 7 N2 修复在接口层补齐了 DB 热更新通知通道,但调用端缺失。
- 编辑器中通过 Inspector 修改某个
MapRoomDataSO的GridPosition、新增/删除MapDatabaseSO.AllRooms数组元素,运行时不会触发任何 UI 重建。 - MapPanel/MinimapHUD 继续渲染过期布局,直到玩家进入新房间或重开面板。
- 编辑器中通过 Inspector 修改某个
- 修复:在
MapDatabaseSO.OnValidate中(#if UNITY_EDITOR && Application.isPlaying)回调ServiceLocator.GetOrDefault<IMapService>()?.NotifyDatabaseChanged();同时让MapRoomDataSO.OnValidate也对所属 Database 反向通知(或在数据库一侧做差量比较)。可选:暴露给编辑器窗口的 "Apply" 按钮显式调用。
R8-N2:MinimapHUD 完全不渲染玩家 Pin
- 位置:
MinimapHUD.cs未引用IPinService,仅 MapPanel 渲染图钉。 - 后果:玩家在全屏地图上标注的图钉无法在角落小地图上可见,违反主流 Metroidvania 体验(HUD 应给出最近的目标提示,避免反复打开大地图)。
- 修复:在 MinimapHUD 中订阅
IPinService.PinsVersion,在可视范围内挑选最近的 N 个 Pin(屏幕外用边缘箭头表示)。最小化实现:仅渲染当前视野单元格范围内的 Pin。
P1(建议修)
R8-N3:MapManager.Awake 重复实例 Destroy 后 OnEnable 仍会执行
- 位置:
MapManager.cs:36-46 - 分析:
Destroy(gameObject)在 Awake 中调用,但 Unity 生命周期中 Awake → OnEnable 同帧仍会触发;OnEnable 会订阅事件 + 注册 ISaveable,本帧末才被销毁后 OnDisable 取消订阅。期间若发生 OnSave(极少但存在),会写入"将被销毁"的实例。 - 修复:增加
private bool _isDuplicate;在 Awake 中标记,OnEnable/OnDisable 提前 return。
R8-N4:MapPlayerTracker 重复实例只 return 不 Destroy
- 位置:
MapPlayerTracker.Awake(与 MapManager 模式不一致)。 - 后果:场景中若意外存在两个 MapPlayerTracker,第二个不会被销毁,仍消耗 LateUpdate(虽然不注册服务,但
_database依然在 Start 中赋值)。 - 修复:与 MapManager 对齐 — 检测到已注册时
Destroy(gameObject); return;。
R8-N5:服务获取没有"懒加载/重试"机制
- 位置:
MapPanel.OnEnable(行 79~80)一次性获取_playerProvider/_pinService/_mapSvc。 - 后果:若 MapPanel 比 MapPinManager / MapManager 早 OnEnable(场景启动顺序未严格保证时),后续不会再尝试。用户必须关闭再打开面板。
- 修复:在 LateUpdate 起始位置增加 lazy 解析:
或采用"DefaultExecutionOrder + GameServiceRegistrar"统一保证启动顺序(已部分落实,但 Panel 是 UI 子物体,启动顺序更脆弱)。
if (_pinService == null) _pinService = ServiceLocator.GetOrDefault<IPinService>();
R8-N6:MapManager.OnLoad 不广播 EVT_MapUpdated / EVT_RegionChanged
- 位置:
MapManager.OnLoad(行 63~69)。 - 后果:若读档时 MapPanel 已打开(例如从设置菜单读档),探索状态、区域名提示不会即时刷新。
RefreshAllCells仅在 OnEnable 触发。 - 修复:在 OnLoad 末尾通过
IMapService.OnDatabaseChanged通知 UI 全量重建(语义略宽,但成本可接受);或为IMapService增加OnSaveLoaded专用事件。
P2(可选改进)
R8-N7:MapPin.OnSave 直接共享 _pins 引用,与 MapManager 拷贝模式不一致
- 位置:
MapPin.cs:98 - 风险:若未来 SaveSystem 引入异步序列化或重试,运行时
CreatePin/RemovePin修改集合可能与序列化冲突。 - 修复:
data.Map.Pins = new List<MapPin>(_pins);(与 MapManager 的new HashSet<>风格一致)。
R8-N8:MapManager.GetExplorationProgress 缓存 _totalRoomCount 但无 OnDatabaseChanged 失效钩子
- 位置:
MapManager.NotifyDatabaseChanged()内已 reset_totalRoomCount = -1,但前提是有人调用 NotifyDatabaseChanged(见 R8-N1)。需要联动确认。 - 修复:随 R8-N1 一并解决。
R8-N9:MapPin Save 字段命名考虑前向兼容
MapPin是同时充当运行时模型 + 存档结构的[Serializable] class。若未来字段重构(例如增加IsCompleted标志),旧存档反序列化可能在 BinaryFormatter 下损坏。- 建议:要么用 JSON 存档(已部分使用?需确认 SaveSystem 序列化器),要么显式提供
[OnDeserialized]migrations。
R8-N10:MapInputHandler 仍使用 UnityEngine.Input.GetAxisRaw(旧 Input Manager)
- 与项目其他模块(推测使用新 Input System)不一致。
- 建议:迁移到
IInputService(项目内已有的抽象)。Round 7 已标记,本轮再次确认为待办。
P3(长期/暂可不修)
- R8-D1:RegionSO 配置化(区域颜色、地图碎片关联、Boss 标记)仍未启动,目前 RegionId 仅是字符串。
- R8-D2:探索进度 UI(
GetExplorationProgressAPI 已存在)未在面板上呈现。 - R8-D3:手柄缩放 / 平移热键尚未对齐 PC + Gamepad 双输入。
- R8-D4:PlayMode 集成测试(房间发现 → 存档 → 读档 → UI 同步)尚未编写。
- R8-D5:
Docs/Design/MinimapDesignSpec.md设计文档(约束格子语义、颜色、图层)仍未补齐。
四、设计亮点(继续保留)
- 架构:
ServiceLocator + ScriptableObject + EventChannel三件套清晰分层;IMapService / IPinService / IPlayerPositionProvider抽象到位。 - 共享空间索引:
MapDatabaseSO.GetRoomIdAtCell统一了 HUD 与 Tracker 的查询路径,避免双份索引内存与同步开销。 - 编辑器扩展:
MapLayoutEditorWindow(俯视图拖拽)+MapRoomDataEditor(Scene 句柄两点确定矩形)+MapDatabaseEditor(一键 Validate)形成完整作业流。 - 存档健壮性:MapManager 复制 HashSet 防引用泄漏;LastRegionId 恢复消除"读档首次进房误触发区域 Toast"。
- 性能保护:CellPool、PinsVersion 脏检查、空间索引懒构建、Mathf.Clamp01 防御。
五、推荐修复优先级与预期得分
| 优先级 | 项目 | 预计提升 |
|---|---|---|
| P0 | R8-N1 NotifyDatabaseChanged 接入 + R8-N2 MinimapHUD Pin 渲染 | +5 |
| P1 | R8-N3 / N4(单例对齐)+ N5(lazy 解析)+ N6(OnLoad 广播) | +3 |
| P2 | R8-N7 / N8 / N9 | +1 |
| P3 | R8-D1~D5 | +3 |
完成 P0+P1 后整体预期 88/100;进一步完成 P2 后 89/100;P3 全部就位(含 RegionSO + 探索进度 UI + 设计文档)后可冲击 92~93/100。
六、与 Round 7 的差异说明
Round 7 报告以"接口补齐 + 局部 NRE 防御"视角给出修复后 88 的预估,但未审视已补 API 的调用闭环。Round 8 在更严标尺下:
- 发现
NotifyDatabaseChanged是"半完工 API"(声明 + 实现存在,但无调用方),列为 P0。 - 发现 MinimapHUD 不渲染 Pin 的功能盲区,列为 P0(属于 Round 1~7 一直未提及的功能性缺口)。
- 重新审视单例守护、服务懒解析、读档广播这三处稳健性细节,列为 P1。
修复方向已在第三章逐项给出,等待执行授权。