- 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.1 KiB
Minimap System — Round 18 Independent Review
评审范围
全部 23 个源文件(4 接口 + 15 运行时 + 4 编辑器),在 R17 全部修复已落地的基础上进行全面重新审查。
R17 修复确认
| 编号 | 描述 | 验证状态 |
|---|---|---|
| R17-N1 | 删除 MapLayoutEditorWindow._cachedErrorRoomIds 死代码字段 |
✅ 已确认,字段已不存在 |
| R17-N2 | 搜索活跃时非匹配房间 alpha 0.28 → 0.08 | ✅ 已确认,第 335 行 |
| R17-N3 | 搜索无结果时居中显示提示文本 | ✅ 已确认,第 363–373 行 |
R18 全方面评分(修复前)
| 维度 | 权重 | 分数 | 说明 |
|---|---|---|---|
| 架构解耦 | 20% | 95 | 四接口全 ServiceLocator;事件单向流;无循环依赖;服务订阅 Awake 长期持有不随 OnEnable/Disable 失效 |
| 功能完整性 | 20% | 92 | 三级可见性、Pin、传送、本地化区域名、地图碎片解锁动画、存档一致;全部覆盖 |
| 性能 | 15% | 91 | 三池(cell/pin/exit)+ O(viewRadius²) 剔除 + PinsVersion 脏检查 + _servicesReady 短路;N1 键盘平移未乘缩放;N3 MinimapHUD 双重刷新 |
| 编辑器工具 | 15% | 93 | 布局编辑器:拖拽/搜索/图例/Play Mode 叠加/Undo-Redo/AssetPostprocessor 自动注册;R17 UX 修复有效;N2 每帧重建 noResultStyle |
| 可扩展性 | 10% | 95 | RoomType[Flags] 可任意叠加新类型;SO 驱动;接口易替换;MapPinConfigSO 集中 Pin 配置 |
| 代码质量 | 10% | 93 | 命名规范、注释充实、防御拷贝三处对称;N2 GUIStyle 每 OnGUI 帧重建 |
| 玩家体验设计 | 10% | 88 | 键盘平移范围未乘 _zoom,缩放后平移速度感知偏差 |
综合(修复前):92.4 / 100
R18 新发现问题
N1(中,正确性):MapInputHandler 键盘平移范围未乘缩放系数
文件:Assets/_Game/Scripts/World/Map/MapInputHandler.cs 第 76–87 行
现状:
Vector2 contentSize = content.rect.size;
Vector2 viewportSize = viewport.rect.size;
float rangeX = contentSize.x - viewportSize.x;
float rangeY = contentSize.y - viewportSize.y;
RectTransform.rect.size 是本地空间(未缩放)的尺寸。当 _zoomTarget.localScale = (_zoom, _zoom, 1) 改变后,内容在屏幕上的视觉尺寸为 contentSize × _zoom,ScrollRect 的实际可滚动范围也随之放大。
当前代码中 rangeX = contentSize.x - viewportSize.x 不含缩放因子,导致:
- 放大时(
_zoom > 1):每帧移动距离delta.x / rangeX被高估,平移速度感知快于预期; - 缩小时(
_zoom < 1):平移速度感知慢于预期; - 同样问题影响
MapPanel.CenterOnCurrentRoom中的rangeX / rangeY计算。
修复:
// MapInputHandler.Update
float rangeX = contentSize.x * _zoom - viewportSize.x;
float rangeY = contentSize.y * _zoom - viewportSize.y;
// MapPanel.CenterOnCurrentRoom — 同步修复 rangeX/rangeY
float rangeX = contentSize.x * /* zoom */ - viewSize.x; // 需从 MapInputHandler 传入或独立持有
注意:
MapPanel.CenterOnCurrentRoom中无法直接读取MapInputHandler._zoom;最简方案是从_roomContainer.localScale.x读取当前缩放值。
N2(低,编辑器性能):noResultStyle 每次 OnGUI 重新分配
文件:Assets/_Game/Scripts/Editor/World/Map/MapLayoutEditorWindow.cs 第 365–371 行
现状:
var noResultStyle = new GUIStyle(EditorStyles.boldLabel)
{
alignment = TextAnchor.MiddleCenter,
normal = { textColor = new Color(1f, 0.8f, 0.2f, 0.8f) },
fontSize = 13,
};
此段仅在"搜索有内容 && 无匹配"时执行,但每次 OnGUI(编辑器 60 fps 或更高频率)均分配一个新 GUIStyle 对象。与已有的 _roomLabelStyle、_badgeBossStyle、_badgeNormalStyle 的缓存模式不一致。
修复:添加 _noResultStyle 字段,在 EnsureLabelStyles() 中一并初始化(fontSize = 13 固定,无需随 _zoom 变化)。
N3(低,架构一致性):MinimapHUD 保留 _onMapUpdated 订阅导致双重刷新
文件:Assets/_Game/Scripts/World/Map/MinimapHUD.cs 第 90 行 + 第 205–209 行
现状:
// OnEnable:
_onMapUpdated?.Subscribe(OnMapUpdated).AddTo(_subs);
// OnMapUpdated:
private void OnMapUpdated(string roomId)
{
if (_cells.TryGetValue(roomId, out var cell))
cell.SetVisibility(_mapSvc.GetVisibility(roomId));
}
MapPanel 已在 R12-N8 移除 _onMapUpdated 订阅,改由 OnExplorationChanged 统一处理。但 MinimapHUD 未跟进:
每次房间被探索或标记时,MapManager 先后触发:
_onMapUpdated?.Raise(roomId)→MinimapHUD.OnMapUpdated(roomId)— 更新单个格子OnExplorationChanged?.Invoke()→MinimapHUD.OnExplorationChanged()— 刷新全部活跃格子
步骤 1 的工作完全被步骤 2 覆盖,造成重复写入。对于小视野(4–9 格)影响可忽略,但在视野半径较大时每次探索会多做一次无效的单格更新。
修复:从 MinimapHUD 移除 _onMapUpdated SerializeField 及其订阅,保留 [HideInInspector, SerializeField] 字段并标注废弃说明(与 MapPanel 的处理方式对齐,保留 Prefab 序列化兼容性),删除 OnMapUpdated 私有方法。
架构深度审查
接口层(4 接口)
| 接口 | 实现 | 注册方式 | 状态 |
|---|---|---|---|
IMapService |
MapManager |
ServiceLocator.Register<T> in Awake |
✅ 完整 |
IPinService |
MapPinManager |
ServiceLocator in Awake | ✅ 完整 |
IPlayerPositionProvider |
MapPlayerTracker |
ServiceLocator in Awake | ✅ 完整 |
ITeleportService |
TeleportService |
ServiceLocator in Awake | ✅ 完整 |
存档一致性(3 处 ISaveable)
| 实现类 | 防御拷贝 | 覆盖加载 | 广播更新 |
|---|---|---|---|
MapManager.OnSave/OnLoad |
✅ new HashSet<>(_x) |
✅ new HashSet<> 后赋值 |
✅ OnExplorationChanged?.Invoke() |
MapPinManager.OnSave/OnLoad |
✅ new List<>(_pins) |
✅ new List<> 后赋值 |
✅ PinsVersion++ |
TeleportService.OnSave/OnLoad |
✅ new HashSet<>(_unlockedRoomIds) |
✅ Clear + foreach Add | ✅(通过传送服务事件) |
对象池完整性
| 池 | 容纳类型 | 所在组件 | 入池时机 |
|---|---|---|---|
_cellPool |
MapRoomCellUI | MapPanel | RebuildAll |
_pinPool |
Image (Pin) | MapPanel | ClearPins |
_exitPool |
Image (Exit) | MapPanel | ClearExits |
_cellPool |
MapRoomCellUI | MinimapHUD | RefreshView 裁剪过期格子 |
_pinPool |
Image (Pin) | MinimapHUD | ClearPins |
性能关键路径
| 路径 | 复杂度 | 机制 |
|---|---|---|
MinimapHUD.RefreshView 新格子查询 |
O(viewRadius²) | MapDatabaseSO.GetRoomIdAtCell 共享空间索引 |
MapPanel.LateUpdate 服务查询 |
O(1) 短路 | _servicesReady bool 门控 |
MapPanel.LateUpdate Pin 渲染 |
O(1) 检查 | PinsVersion 脏检查 |
MapManager.GetRoomsByRegion |
O(1) | _regionCache 懒加载字典 |
MapDatabaseSO.GetRoom |
O(1) | _index 字符串→SO 哈希索引 |
MapDatabaseSO.GetRoomIdAtCell |
O(1) | _cellToRoom 格子坐标→ID 空间索引 |
MinimapHUD.UpdatePlayerDot |
O(1) | roomId + NormPos 双字段脏检查 |
MapLayoutEditorWindow.EnsureLabelStyles |
O(1) | _cachedZoomForStyle 脏检查 |
编辑器工具覆盖面
| 工具 | 能力 |
|---|---|
MapLayoutEditorWindow |
可视化布局预览;滚轮缩放;中键/Alt 平移;房间拖拽编辑(Undo);区域配色;验证 + 错误高亮;搜索高亮(R17 UX 改善);图例;Play Mode 玩家点叠加 |
MapDatabaseEditor |
Inspector 内嵌"在布局编辑器中打开"按钮 |
MapRoomDataEditor |
SceneGUI 双角控制点可视化 GridPosition/GridSize |
MapRoomAutoRegister |
AssetPostprocessor 自动注册新 SO 到默认 Database;删除 null 清理;可禁用 |
R18 修复预期评分
| 维度 | 权重 | 当前 | 修复后 | 变化 |
|---|---|---|---|---|
| 架构解耦 | 20% | 95 | 95 | — |
| 功能完整性 | 20% | 92 | 92 | — |
| 性能 | 15% | 91 | 93 | +2(N1+N3) |
| 编辑器工具 | 15% | 93 | 94 | +1(N2) |
| 可扩展性 | 10% | 95 | 95 | — |
| 代码质量 | 10% | 93 | 95 | +2(N2+N3 架构一致) |
| 玩家体验设计 | 10% | 88 | 92 | +4(N1 修复后平移手感正确) |
综合(修复前):92.4 / 100 → 修复后:93.8 / 100
总结
经过 18 轮迭代,小地图系统整体架构已达到生产级 2D 探索类游戏标准:
- 接口完整:四接口零具体类依赖,ServiceLocator 完全解耦
- 存档对称:三处 ISaveable 均实现防御拷贝,读档后广播正确事件
- 性能到位:五池 + O(viewRadius²) + 多级脏检查 + _servicesReady 短路
- 编辑器成熟:拖拽、搜索、Undo、Play Mode 叠加、自动注册一体化
R18 新发现 3 项问题均为低/中优先级,不影响功能正确性(N1 影响手感,N2/N3 为轻微性能与架构一致性问题)。