- 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.
8.5 KiB
小地图系统独立评审 Round 21
评审时间:R21(基于 R20 全部修复已落地的代码基线)
评审范围:Assets/_Game/Scripts/World/Map/ 全部 19 个运行时文件 + 4 个编辑器扩展文件
评分基准:专业 2D Metroidvania 编辑器扩展标准(架构解耦 / 高性能 / 可扩展 / 开发者友好)
R20 修复确认
| 编号 | 内容 | 状态 |
|---|---|---|
| R20-N1 | RunRevealAnim 包装协程,完成后 _revealCoroutines.Remove(roomId) |
✅ 确认(L214-218) |
| R20-N2 | ChooseDisplayIcon 集中到 MapRoomDataSO;MapPanel/MinimapHUD 改单行委托 |
✅ 确认(L61-69 / L478-480 / L367-368) |
各维度评分
1. 架构设计(Architecture) 18.5 / 20
亮点
- ServiceLocator + 4 接口层(IMapService / IPinService / IPlayerPositionProvider / ITeleportService)完全解耦
- 事件驱动(C# Action),无轮询
- ISaveable 三处防御性拷贝对称实现
- MapRoomCellUI 在 MapPanel / MinimapHUD 共享,零重复 Prefab
ChooseDisplayIconR20-N2 后唯一入口,无漂移风险
新发现问题 N1(中等严重):MinimapHUD 缺少 LateUpdate 服务重试机制
MapPanel.LateUpdate 对未就绪的服务执行重试:
// MapPanel.cs L169-170
if (!_servicesReady)
SubscribeServices();
MinimapHUD 仅在 Awake 和 OnEnable 中调用 SubscribeServices(),无 LateUpdate 重试。
MapPlayerTracker(IPlayerPositionProvider)无 [DefaultExecutionOrder],初始化顺序不确定:
- 若
MinimapHUD.Awake先于MapPlayerTracker.Awake执行 →_playerProvider永久为 null - 后果:玩家圆点不渲染,
OnRoomChanged永不触发,HUD 完全静止
扣分:−1.5
2. 性能(Performance) 19.5 / 20
亮点
- MapPanel 3 对象池(Cell / Pin / Exit),MinimapHUD 2 对象池(Cell / Pin)
- O(viewRadius²) 空间索引查询(共享
MapDatabaseSO._cellToRoom) _servicesReady短路(MapPanel),消除每帧 ServiceLocator 查询_revealCoroutines自清理(R20-N1)- 复用缓冲区:
_toRemove/_roomsInViewBuffer/_newlyAddedBuffer - PinsVersion 脏检查 + 玩家位置脏检查
_regionCache懒加载,避免 LINQ 全扫
说明(信息级,不扣分):MapPanel.BuildGrid 末尾调用 LayoutRebuilder.ForceRebuildLayoutImmediate。
若 _scrollRect.content 含 ContentSizeFitter,此调用是必要的(确保 CenterOnCurrentRoom 读到正确 content 尺寸);无 LayoutGroup 时代价极低。设计合理。
扣分:−0.5(MinimapHUD 无 _servicesReady 短路,每次 OnEnable 均执行三次 ServiceLocator 查询)
3. 代码质量(Code Quality) 18.5 / 20
亮点
CurrentZoom属性(R19-N1)消除双份状态RunRevealAnim自清理协程(R20-N1)ChooseDisplayIcon单一职责集中(R20-N2)- OnValidate RoomFlags 迁移兼容
HasCustomExitPos语义布尔替代哨兵值[Obsolete]+[HideInInspector]废弃字段注解完整
新发现问题 N2(轻微):MapPanel 集合字段缺少 readonly 修饰
MinimapHUD 同类字段已标 readonly,两者不一致:
// MinimapHUD.cs(已正确标注)
private readonly Dictionary<string, MapRoomCellUI> _cells = new();
private readonly List<Image> _pinImages = new();
private readonly Stack<Image> _pinPool = new();
private readonly Stack<MapRoomCellUI> _cellPool = new();
// MapPanel.cs(缺少 readonly,可被意外重新赋值)
private Dictionary<string, MapRoomCellUI> _cells = new();
private List<Image> _pinImages = new();
private Stack<Image> _pinPool = new();
private Stack<MapRoomCellUI> _cellPool = new();
private Stack<Image> _exitPool = new();
private List<Image> _exitImages= new();
_revealCoroutines 已正确标 readonly,其余集合字段漏标。
扣分:−1.0(N2 readonly 不一致)+ −0.5(MapInputHandler _zoom 在 OnScroll 仍作为局部累加器,与 _zoomTarget.localScale.x 功能重叠,配置错误时存在漂移风险)
4. 编辑器扩展(Editor Tools) 14.5 / 15
亮点
MapLayoutEditorWindow:缩放/平移/拖拽/搜索/区域着色/验证/Undo/热改_cachedZoomForStyle脏检查,Style 不在每帧重建_noResultStyle首次初始化缓存(R18-N2)MapDatabaseEditor、MapRoomDataEditor、MapRoomAutoRegister覆盖完整工作流DrawExitLines去重缓存(_drawnExitPairs)
扣分:−0.5(信息级:MapLayoutEditorWindow 无单元测试覆盖)
5. 功能完整性(Feature Completeness) 14.5 / 15
亮点
- 三级可见性(Unknown / Explored / Mapped)完整实现
- 类型图标优先级(Override > SavePoint > Boss > Shop > Teleport)
- Pin 系统(持久化、类型化、可视半径内渲染)
- 出口连接线 + Fallback 位置(R13-N1)
- 区域检测与 RegionChanged 事件
- 存档/读档(ISaveable)
- 房间发现动画(RevealAnim 自清理)
- 全屏地图 + 角落 HUD 双视图
N1 影响:若 MinimapHUD 初始化顺序不利,玩家圆点和 HUD 响应将缺失,属功能可靠性问题。扣分:−0.5
6. 输入系统(Input System) 9.5 / 10
亮点
- 全部使用 InputSystem(InputReaderSO)
MapInputHandler:Navigate / MapCenter / OnScrollMinimapInputHandler:CycleMinimapZoom- OnEnable/OnDisable 对称订阅/取消
扣分:−0.5(信息级:MapInputHandler._zoom 与 _panel.CurrentZoom 职责轻微重叠)
综合评分
| 维度 | 满分 | 得分 |
|---|---|---|
| 架构设计 | 20 | 18.5 |
| 性能 | 20 | 19.5 |
| 代码质量 | 20 | 18.5 |
| 编辑器扩展 | 15 | 14.5 |
| 功能完整性 | 15 | 14.5 |
| 输入系统 | 10 | 9.5 |
| 合计 | 100 | 95.0 |
注:R21 较 R20(94.2)略有提升,主要因 R20 修复全部落地确认;
N1(服务重试缺失)是本轮最高优先级问题。
问题清单与修复建议
N1 — MinimapHUD 缺少服务重试(高优先级)
根因:MapPlayerTracker 无 [DefaultExecutionOrder],可能晚于 MinimapHUD 注册服务。
修复方案 A(推荐,防御性最强):在 MinimapHUD.LateUpdate 中补充重试。
// MinimapHUD.cs
private bool _servicesReady;
private void LateUpdate()
{
if (!_servicesReady)
SubscribeServices();
UpdatePlayerDot();
RenderPinsIfDirty();
}
private void SubscribeServices()
{
// 原有逻辑不变,末尾加:
if (_playerProvider != null && _mapSvc != null && _pinService != null)
_servicesReady = true;
}
修复方案 B(互补):给 MapPlayerTracker 加执行顺序,确保早于无顺序 MonoBehaviour 注册。
[DefaultExecutionOrder(-600)] // 晚于 MapManager(-700),早于默认 0
public class MapPlayerTracker : MonoBehaviour, IPlayerPositionProvider
建议两个方案同时实施:B 保证常规路径,A 防御异常路径。
N2 — MapPanel 集合字段缺少 readonly(低优先级)
修复:在 MapPanel.cs 的集合字段声明处补充 readonly。
private readonly Dictionary<string, MapRoomCellUI> _cells = new();
private readonly List<Image> _pinImages = new();
private readonly Stack<Image> _pinPool = new();
private readonly Stack<MapRoomCellUI> _cellPool = new();
private readonly Stack<Image> _exitPool = new();
private readonly List<Image> _exitImages= new();
N3 — MinimapHUD.OnDisable 未重置 _servicesReady(伴随 N1 修复引入)
实施 N1 修复后,若 _servicesReady 不在 UnsubscribeServices / OnDestroy 时重置,销毁后重建场景的实例将跳过重订阅。
private void UnsubscribeServices()
{
_servicesReady = false; // 补充
// 其余原有逻辑不变
}
评分历史
| 轮次 | 评分 | 关键改动 |
|---|---|---|
| R14 | 92.3 | — |
| R15 | 91.3 | — |
| R16 | 92.5 | — |
| R17 | 93.0 | — |
| R18 | 93.8 | MinimapHUD 废弃 _onMapUpdated 订阅 |
| R19 | 93.8 | CurrentZoom 属性;_revealCoroutines 防泄漏 |
| R20 | 94.2 | RunRevealAnim 自清理;ChooseDisplayIcon 集中 |
| R21 | 95.0 | R20 修复确认;识别 N1(服务重试缺失)N2(readonly)N3(执行顺序) |