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

12 KiB
Raw Permalink Blame History

Minimap System — Round 19 Independent Review

评审范围

全部 23 个源文件4 接口 + 15 运行时 + 4 编辑器),在 R18 全部修复已落地的基础上进行全面重新审查。


R18 修复确认

编号 描述 验证状态
R18-N1a MapInputHandler.Update rangeX/rangeY 乘以 _zoom 第 7980 行
R18-N1b MapPanel.CenterOnCurrentRoom_roomContainer.localScale.x 读 zoom修正 rangeX/rangeY 及 cellX/cellY 第 377384 行
R18-N2 _noResultStyle 缓存为字段,在 EnsureLabelStyles() 首次调用时初始化 第 435445 行
R18-N3 MinimapHUD._onMapUpdated[HideInInspector],移除订阅和 OnMapUpdated 方法 第 3639 行

R19 全方面评分

维度 权重 分数 评分依据
架构解耦 20% 95 4 接口全 ServiceLocatorC# 事件单向流无循环依赖Awake/OnDestroy 长期订阅模式MapServiceExtensions 集中无状态查询逻辑
功能完整性 20% 93 三级可见性、地图碎片解锁动画、Pin/传送/区域名本地化、探索进度显示、雾效覆盖层、存档全覆盖
性能 15% 93 五对象池cell×2 / pin×2 / exit×1O(viewRadius²) 剔除PinsVersion 脏检查_servicesReady 短路MapDatabaseSO 双重 O(1) 索引R18-N1 键盘平移已修正N1c 潜在风险(见下方)
编辑器工具 15% 94 布局编辑器可视化+拖拽+搜索+图例+UndoMapDatabaseEditor 自动验证MapRoomDataEditor SceneGUI 双角控制点AssetPostprocessor 自动注册+null 清理R18-N2 GUIStyle 缓存到位
可扩展性 10% 95 RoomType[Flags] 枚举可任意叠加新类型SO 驱动MapDatabaseSO / MapPinConfigSO接口易替换MapGridConstants 统一常量管理
代码质量 10% 95 全面注释命名规范3 处 ISaveable 防御性拷贝对称try/finally 保护 GUI.matrix[FormerlySerializedAs] 迁移;[HideInInspector] 废弃兼容_noResultStyle 已缓存
玩家体验设计 10% 92 三级可见性传送点快速旅行循环缩放视野Play Mode 编辑器叠加区域名淡入动画R18-N1 平移手感已修正

综合评分93.8 / 100(与 R18 修复后预测值完全吻合)


架构层深度审查

接口与服务完整矩阵

接口 实现类 ServiceLocator 注册 ISaveable 备注
IMapService MapManager Awake重复实例保护 防御拷贝 + OnLoad 广播 OnExplorationChanged
IPinService MapPinManager Awake PinsVersion 版本号驱动脏检查
IPlayerPositionProvider MapPlayerTracker Awake重复实例保护 (不需要) LateUpdate O(1) 空间索引 + NormalizedPositionInRoom 每帧插值
ITeleportService TeleportService Awake 防御拷贝;事件通道驱动场景过渡

数据流方向(单向)

MapManager  ──── OnRoomEntered ────────▶  OnExplorationChanged (C# event)
                                              ▶ MapPanel.OnExplorationChanged
                                              ▶ MinimapHUD.OnExplorationChanged
                                              ▶ MapProgressDisplay.Refresh

MapManager  ──── SetMapped / SetMappedBatch ──▶  OnRoomMapped (C# event)
                                              ▶ MapPanel.OnRoomMappedAnim (发现动画)

MapManager  ──── _onRegionChanged.Raise ──────▶ RegionNameDisplay
                                              ▶ MapProgressDisplay.OnRegionChanged

MapPlayerTracker ── OnRoomChanged (C# event) ▶ MinimapHUD.OnRoomChanged → RefreshView
                  ─ NormalizedPositionInRoom  ▶ MapPanel.LateUpdate脏检查
                                              ▶ MinimapHUD.LateUpdate.UpdatePlayerDot

_onMapUpdated StringEventChannel 仍在 MapManager 的 3 处发射,但地图 UI 已全部移除订阅MapPanel R12-N8、MinimapHUD R18-N3。该通道保留以供非 Map UI 的外部系统订阅,属于已知设计状态。

对象池完整性

类型 宿主 入池时机 出池时机
_cellPool MapRoomCellUI MapPanel RebuildAll / OnDestroy BuildGrid
_pinPool Image (Pin) MapPanel ClearPins / OnDestroy RenderPins
_exitPool Image (Exit) MapPanel ClearExits / OnDestroy DrawExits
_cellPool MapRoomCellUI MinimapHUD RefreshView 裁剪过期 / OnDestroy RefreshView 新增
_pinPool Image (Pin) MinimapHUD ClearPins / OnDestroy RebuildPins

所有池均正确实现:入池时 SetActive(false) + Push,出池时 Pop + SetActive(true)Instantiate 兜底。

ISaveable 对称性审查

OnSave 防御拷贝 OnLoad 防御拷贝 读档后事件广播
MapManager new HashSet<>(x2) new HashSet<>(x2) OnExplorationChanged?.Invoke()
MapPinManager new List<>(_pins) new List<>(data.Pins) PinsVersion++
TeleportService new HashSet<>(_unlockedRoomIds) Clear + foreach Add

三处均对称,无共享引用风险。

性能关键路径

路径 复杂度 机制
MinimapHUD.RefreshView 新格子查询 O(viewRadius²) MapDatabaseSO.GetRoomIdAtCell 共享空间索引
MinimapHUD.LateUpdate.UpdatePlayerDot O(1) roomId + NormPos 双字段脏检查
MapPanel.LateUpdate 服务查询 O(1) 短路 _servicesReady bool 门控
MapPanel.LateUpdate.RenderPins O(1) 检查 PinsVersion 脏检查
MapManager.GetRoomsByRegion O(1) _regionCache 懒加载字典
MapDatabaseSO.GetRoom O(1) _index 字典哈希
MapDatabaseSO.GetRoomIdAtCell O(1) _cellToRoom 空间哈希
MapPinConfigSO.GetSprite O(1) _cache 惰性字典
RegionNameDisplay / MapProgressDisplay.ResolveDisplayName O(1) 预建 _regionDict
MapLayoutEditorWindow.EnsureLabelStyles O(1) _cachedZoomForStyle 脏检查
MapLayoutEditorWindow._noResultStyle O(1) 首次初始化后不重建

R19 新发现问题

N1配置健壮性_roomContainer_zoomTarget 为两个独立引用,配置不一致时静默偏差

涉及文件MapInputHandler.cs(第 21 行)、MapPanel.cs(第 23 行)

现状

  • MapInputHandler._zoomTargetOnScroll 写入 _zoomTarget.localScaleUpdate 使用 _zoom 字段
  • MapPanel._roomContainerCenterOnCurrentRoom 读取 _roomContainer.localScale.x

两者仅在 _zoomTarget == _roomContainer(即 Prefab 中两个引用指向同一 GameObject时保持同步。若开发者在 Prefab 中错误配置,CenterOnCurrentRoom 将读到错误的缩放系数。当前无运行时断言或警告。

影响:误配置后打开地图并缩放,再按"居中"快捷键MapCenterEvent定位会偏移但无错误提示。

修复建议

// MapInputHandler.cs — OnEnable / OnScroll 中向 MapPanel 暴露当前缩放值
// 方案AMapPanel 增加 public 属性
public float CurrentZoom => _roomContainer != null ? _roomContainer.localScale.x : 1f;
// MapInputHandler.OnScroll 改从 _panel.CurrentZoom 读,而非独立维护 _zoom消除两份状态

// 方案B最小改动Awake 中断言 _zoomTarget == _scrollRect.content

N2正确性边缘MapPanel.OnRoomMappedAnim 协程在目标格子被回收后继续运行

文件MapPanel.cs 第 198202 行

现状

protected virtual void OnRoomMappedAnim(string roomId)
{
    if (_cells.TryGetValue(roomId, out var cell) && cell != null)
        StartCoroutine(cell.PlayRevealAnim(_revealFlashColor, _revealDuration));
}

StartCoroutine 挂在 MapPanelthis协程生命周期由 MapPanel 管理。若在动画播放期间触发 RebuildAllOnDatabaseChanged),目标 cell 会被入池(SetActive(false))。协程继续运行并向已入池的 cell 的 _bg.color 写入,直到 _revealDuration 结束。下次该 cell 出池并调用 Setup → SetVisibility 时颜色会被正确覆盖,因此视觉影响自愈。

严重程度:极低(数据库热更极少发生,动画持续 0.4 s。但在频繁使用编辑器热更的开发流程中可能产生轻微闪烁。

修复建议

// MapPanel 添加字段
private readonly Dictionary<string, Coroutine> _revealCoroutines = new();

// OnRoomMappedAnim 中:
if (_revealCoroutines.TryGetValue(roomId, out var old) && old != null)
    StopCoroutine(old);
_revealCoroutines[roomId] = StartCoroutine(cell.PlayRevealAnim(...));

// RebuildAll 中清理:
foreach (var c in _revealCoroutines.Values)
    if (c != null) StopCoroutine(c);
_revealCoroutines.Clear();

N3信息架构说明MapManager._onMapUpdated 通道现在在地图 UI 内无订阅者

文件MapManager.cs 第 85、107、122 行

经 R12-N8MapPanel和 R18-N3MinimapHUD地图 UI 不再订阅 _onMapUpdated StringEventChannelSO。MapManager 仍在 OnRoomEnteredSetMappedSetMappedBatch 三处调用 Raise()

这不是 BugSO 事件通道天然支持外部系统订阅(如 Achievement 系统、音效触发器);保留 Raise() 符合开放设计原则。

仅建议添加文档注释

[Header("Event Channels")]
[Tooltip("房间被标记时广播Explored/Mapped供地图外部系统订阅地图 UI 已改用 C# 事件)。")]
[SerializeField] private StringEventChannelSO _onMapUpdated;

编辑器工具覆盖面(策划/开发视角)

工具 目标用户 核心能力
MapLayoutEditorWindow 策划 + 开发 可视化房间布局;滚轮缩放;中键/Alt 平移左键选中拖拽编辑房间坐标Undo实时重叠检测红色警示区域配色搜索高亮无结果提示图例Play Mode 玩家点叠加;出口连线(>12px 时)
MapDatabaseEditor 开发 数据库统计(房间数/出口数一键验证Inspector 内嵌"打开布局编辑器";可折叠房间列表+Ping错误行红色高亮
MapRoomDataEditor 关卡设计 + 开发 SceneGUI 双角控制点可视化;拖拽吸附到整格;防反转保护;居中 SceneView 快捷按钮HelpBox 坐标系说明
MapRoomAutoRegister 开发 AssetPostprocessor 自动注册新 SO 到默认 Databasenull 清理;IsDefault 优先级;可通过 EditorPrefs 禁用

总结

经过 19 轮迭代,小地图系统已达到成熟的生产级 2D 探索类游戏标准:

强项

  • 接口完整4 接口全 ServiceLocatorUI 层无具体实现依赖
  • 存档正确3 处 ISaveable 防御拷贝对称,读档后事件广播完整
  • 性能到位5 对象池 + O(viewRadius²) + 多级脏检查 + 双重 O(1) 索引
  • 编辑器成熟:拖拽布局 + 搜索 + Undo + 验证 + SceneGUI 一体化,策划友好
  • 事件方向清晰:所有 UI 更新均由 C# 事件(OnExplorationChanged)单向驱动,无双重刷新

R19 新发现3 项,全为低优先级)

  • N1_zoomTarget vs _roomContainer 双引用无配置一致性断言(配置层风险)
  • N2OnRoomMappedAnim 协程可能写入已回收的格子(视觉自愈,影响微小)
  • N3_onMapUpdated 通道在地图 UI 内零订阅(信息提示,非缺陷)

评分历史

轮次 评分 主要改进
R14 92.3 ISaveable 签名修复TeleportStation 枚举值
R15 91.3 DrawRoomBadge 补 TeleportStationMapProgressDisplay 区域名
R16 92.5 DrawRoomBadge 优先级MinimapHUD 4 Sprite + ChooseIcon
R17 93.0 死代码清理,搜索 UX 改善alpha + 无结果提示)
R18 93.8 键盘平移范围修正GUIStyle 缓存MinimapHUD 双重刷新消除
R19 93.8 全部 R18 修复确认到位,识别 3 项新低优先级问题