- 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.
199 lines
10 KiB
Markdown
199 lines
10 KiB
Markdown
# 小地图系统独立评审 Round 23
|
||
|
||
**评审时间**:R23(基于 R22 全部修复已落地的代码基线)
|
||
**评审范围**:`Assets/_Game/Scripts/World/Map/` 全部 19 个运行时文件 + 4 个编辑器扩展文件
|
||
**评分基准**:专业 2D Metroidvania 编辑器扩展标准(架构解耦 / 高性能 / 可扩展 / 开发者友好)
|
||
|
||
---
|
||
|
||
## R22 修复确认
|
||
|
||
| 编号 | 内容 | 状态 |
|
||
|------|------|------|
|
||
| R22-N1 | `BuildRegionDict/ResolveRegionDisplayName` 迁移至 `MapServiceExtensions`;两个组件单行委托 | ✅ 确认(`MapServiceExtensions.cs:17,32`;两组件第 104-108 行) |
|
||
| R22-N2 | `DrawRoomBadge` 注释更新为 `MapRoomDataSO.ChooseDisplayIcon` | ✅ 确认(`MapLayoutEditorWindow.cs:480,488`) |
|
||
| R22-N3 | `MapPinManager [DefaultExecutionOrder(-500)]` | ✅ 确认(`MapPin.cs:20`) |
|
||
|
||
---
|
||
|
||
## 各维度评分
|
||
|
||
### 1. 架构设计(Architecture)19.5 / 20
|
||
|
||
**亮点(全面审查确认)**
|
||
- 4 接口(IMapService / IPinService / IPlayerPositionProvider / ITeleportService)+ ServiceLocator,零硬依赖
|
||
- 事件驱动(Action,无帧轮询):OnDatabaseChanged / OnExplorationChanged / OnRoomMapped 语义独立
|
||
- ISaveable 防御性拷贝三处对称(MapManager:`new HashSet<>` ×2;MapPinManager:`new List<>` 双向;TeleportService:`readonly` + Clear+Add)
|
||
- `ChooseDisplayIcon` 单一入口(MapRoomDataSO),MapPanel / MinimapHUD / Editor 三处均委托
|
||
- `MapServiceExtensions`:3 个无状态方法(GetVisibility / CreatePinAtWorldPos / BuildRegionDict+Resolve),消费方零重复逻辑
|
||
- `MapRoomCellUI` 双视图(MapPanel 全屏 + MinimapHUD 角落)复用同一 Prefab
|
||
- `RoomType [Flags]` 枚举支持多类型组合,`OnValidate` 自动迁移旧 bool 字段
|
||
|
||
**信息级观察**:`MapPanel.UnsubscribeServices()` 不 null 服务引用,而 `MinimapHUD.UnsubscribeServices()` 会 null 三个字段并重置 `_servicesReady`。这是有意的架构差异——MapPanel 仅在 OnDestroy 调用 UnsubscribeServices(生命周期末尾,引用已无意义),而 MinimapHUD 作为持久 HUD 需支持跨场景重连。在目标持久服务架构下,两者均不会出现悬空引用。**无需修复,但值得注释说明设计意图。**
|
||
|
||
**扣分:−0.5**(UnsubscribeServices 不对称缺少注释,可读性略低)
|
||
|
||
---
|
||
|
||
### 2. 性能(Performance)19.5 / 20
|
||
|
||
**亮点**
|
||
- `_servicesReady` 短路(MapPanel R12-N7;MinimapHUD R21-N1),消除每帧 ServiceLocator 查询
|
||
- MapPanel 对象池 × 3(`readonly` 集合:`_cellPool` / `_pinPool` / `_exitPool`);MinimapHUD × 2
|
||
- O(viewRadius²) 空间索引(`MapDatabaseSO.GetRoomIdAtCell`),MinimapHUD 无需扫描全量房间
|
||
- 4 个复用缓冲区(`_toRemove` / `_roomsInViewBuffer` / `_newlyAddedBuffer` + `_cells` 增量更新)
|
||
- `PinsVersion` 脏检查:两 UI 均跳过无变化重绘
|
||
- `_totalRoomCount` 懒加载缓存(MapManager),`_regionCache` 懒加载(GetRoomsByRegion 零 LINQ)
|
||
- 玩家图标双脏标记(roomId + normPos),消除无效 RectTransform 写入
|
||
|
||
**信息级**:`MapProgressDisplay.Refresh()` 区域进度遍历 O(N/region),仅在 `OnExplorationChanged` 触发(非每帧),当前规模无性能风险。
|
||
|
||
**扣分:−0.5**(`MapProgressDisplay` 区域遍历未缓存已探索计数;极大地图时有轻微冗余)
|
||
|
||
---
|
||
|
||
### 3. 代码质量(Code Quality)19.5 / 20
|
||
|
||
**亮点**
|
||
- 全部集合字段补齐 `readonly`(MapPanel × 6;MinimapHUD × 4;`_revealCoroutines` 亦为 `readonly`)
|
||
- `CurrentZoom` 属性消除双份缩放状态(MapPanel L379)
|
||
- `RunRevealAnim` 自清理协程:完成后自动 `_revealCoroutines.Remove(roomId)`(R20-N1)
|
||
- `HasCustomExitPos` 语义布尔替代 `== Vector2Int.zero` 哨兵(R13-N1)
|
||
- `[Obsolete]` + `[HideInInspector]` 废弃字段注解完整
|
||
- `RoomId.Trim()` 自动修剪防空格污染(OnValidate)
|
||
- `DrawLine` try/finally 保证 `GUI.matrix` 恢复(R11-N12)
|
||
- `MapPin.cs` 文件头注释明确历史遗留原因
|
||
|
||
**信息级**:`MapPanel.UnsubscribeServices()` 相比 `MinimapHUD.UnsubscribeServices()` 缺少注释说明为何不 null 服务引用。新加入代码者可能误认为是疏漏。
|
||
|
||
**扣分:−0.5**(UnsubscribeServices 不对称未有注释解释意图)
|
||
|
||
---
|
||
|
||
### 4. 编辑器扩展(Editor Tools)14.5 / 15
|
||
|
||
**亮点**
|
||
- `MapLayoutEditorWindow`:缩放 / 平移 / 房间拖拽 / 搜索 / 区域着色 / 验证 / Undo / 热改全功能
|
||
- `_cachedZoomForStyle` 脏检查 + `_noResultStyle` 首次初始化缓存(R18-N2)
|
||
- `DrawExitLines` 字段级 `_drawnExitPairs` 去重,OnGUI 零分配
|
||
- `OnProjectChange` + `OnUndoRedo` 自动失效验证缓存并触发重绘
|
||
- `SetDatabase` 公共 API(避免 MapDatabaseEditor 反射访问私有字段)
|
||
- `MapRoomAutoRegister` AssetPostprocessor 自动注册工作流
|
||
- `MapDatabaseSO.ValidateAll()` 四类校验(null / 空 RoomId / 重复 / 格子重叠 / 出口悬空)
|
||
- `DrawRoomBadge` 注释已更新为 `MapRoomDataSO.ChooseDisplayIcon`(R22-N2)
|
||
|
||
**扣分:−0.5**(`MapLayoutEditorWindow` 搜索框匹配范围仅 RoomId/RegionId 子串,无法按 RoomType 过滤;策划批量检查某类型房间时需逐个点击)
|
||
|
||
---
|
||
|
||
### 5. 功能完整性(Feature Completeness)15.0 / 15
|
||
|
||
**所有特性完整实现:**
|
||
- 三级可见性(Unknown / Explored / Mapped)+ 雾效覆盖层(R12-FD)
|
||
- 图标优先级唯一入口(Override > SavePoint > Boss > Shop > Teleport)
|
||
- Pin 系统(持久化 / 类型化 / 视野内渲染 / 64 字符 note 限制)
|
||
- 出口连接线 + Fallback 位置(HasCustomExitPos 语义布尔)
|
||
- 传送系统(TeleportService:解锁 / 验证 / 请求 / 完成回调)
|
||
- 区域检测 + 区域名本地化显示(RegionNameDisplay + MapProgressDisplay)
|
||
- 存档/读档(ISaveable 三处,防御性拷贝对称)
|
||
- 房间发现动画(PlayRevealAnim + RunRevealAnim 自清理)
|
||
- 全屏地图 + 角落 HUD 双视图;小地图视野档位循环切换(CycleZoom)
|
||
- `RoomType [Flags]` 枚举 + `OnValidate` 向后兼容迁移
|
||
|
||
**扣分:0**
|
||
|
||
---
|
||
|
||
### 6. 输入系统(Input System)9.5 / 10
|
||
|
||
**亮点**
|
||
- 全部使用 InputReaderSO(InputSystem),无硬编码按键
|
||
- `MapInputHandler`:Navigate / MapCenter / OnScroll 完整
|
||
- `MinimapInputHandler`:CycleMinimapZoom 路由 MinimapHUD.CycleZoom()
|
||
- OnEnable/OnDisable 对称订阅/取消
|
||
|
||
**信息级**:`MapInputHandler._zoom` 作为本地累加器,与 `_panel.CurrentZoom` 读取路径不同但结果一致(OnScroll 写 `_roomContainer.localScale` → `CurrentZoom` 从 `_roomContainer.localScale.x` 读取,无环,无状态漂移)。
|
||
|
||
**扣分:−0.5**(`MapInputHandler._zoom` 轻微职责重叠,正确但不够直观)
|
||
|
||
---
|
||
|
||
## 综合评分
|
||
|
||
| 维度 | 满分 | 得分 | 较 R22 |
|
||
|------|------|------|--------|
|
||
| 架构设计 | 20 | 19.5 | +0.5 |
|
||
| 性能 | 20 | 19.5 | ±0 |
|
||
| 代码质量 | 20 | 19.5 | +0.5 |
|
||
| 编辑器扩展 | 15 | 14.5 | ±0 |
|
||
| 功能完整性 | 15 | 15.0 | ±0 |
|
||
| 输入系统 | 10 | 9.5 | ±0 |
|
||
| **合计** | **100** | **97.5** | **+1.0** |
|
||
|
||
---
|
||
|
||
## 问题清单
|
||
|
||
### N1 — MapPanel.UnsubscribeServices 缺少意图注释(低优先级)
|
||
|
||
**现状**:`MinimapHUD.UnsubscribeServices()` 会 null 三个服务引用并重置 `_servicesReady`;`MapPanel.UnsubscribeServices()` 不做此操作(也不需要,因为仅在 OnDestroy 调用)。两者不对称,可读性略差。
|
||
|
||
**修复**:在 `MapPanel.UnsubscribeServices()` 首行补注释:
|
||
```csharp
|
||
// OnDestroy 时调用,生命周期末尾不需要 null 服务引用(与 MinimapHUD.UnsubscribeServices 的设计差异:
|
||
// MinimapHUD 需支持 OnDestroy 后跨场景重连,MapPanel 不需要)。
|
||
```
|
||
|
||
---
|
||
|
||
### N2 — MapLayoutEditorWindow 搜索框不支持 RoomType 过滤(低优先级)
|
||
|
||
**场景**:策划需要批量确认所有 SavePoint / BossRoom 房间的位置时,当前搜索只能匹配 RoomId/RegionId 子串,无法用 `SavePoint` 等类型关键字过滤。
|
||
|
||
**建议方案**:在搜索逻辑中加入 RoomType 枚举名匹配:
|
||
```csharp
|
||
// 在 DrawMapArea 的搜索匹配部分补充:
|
||
bool matchesType = !string.IsNullOrEmpty(_searchText) &&
|
||
System.Enum.GetNames(typeof(RoomType))
|
||
.Any(name => room.RoomFlags.HasFlag((RoomType)System.Enum.Parse(typeof(RoomType), name))
|
||
&& name.IndexOf(_searchText, System.StringComparison.OrdinalIgnoreCase) >= 0);
|
||
bool matches = matchesId || matchesRegion || matchesType;
|
||
```
|
||
|
||
---
|
||
|
||
### N3 — TeleportService 缺少 [DefaultExecutionOrder](信息级)
|
||
|
||
`TeleportService.Awake()` 注册 `ITeleportService`。当前 UI 组件(MinimapHUD / MapPanel)的 `SubscribeServices` 不查询 `ITeleportService`,因此无执行顺序风险。但与其他三个服务(MapManager -700 / MapPlayerTracker -600 / MapPinManager -500)相比,未标注顺序,一致性略差。
|
||
|
||
```csharp
|
||
[DefaultExecutionOrder(-400)] // 晚于 MapPinManager(-500),早于默认 0;ITeleportService 在 UI 初始化前可用
|
||
public class TeleportService : MonoBehaviour, ITeleportService, ISaveable
|
||
```
|
||
|
||
---
|
||
|
||
## 评分历史
|
||
|
||
| 轮次 | 评分 | 关键改动 |
|
||
|------|------|----------|
|
||
| R18 | 93.8 | MinimapHUD 废弃 _onMapUpdated 订阅 |
|
||
| R19 | 93.8 | CurrentZoom 属性;_revealCoroutines 防泄漏 |
|
||
| R20 | 94.2 | RunRevealAnim 自清理;ChooseDisplayIcon 集中 |
|
||
| R21 | 95.0 | MinimapHUD _servicesReady;MapPlayerTracker 执行顺序;readonly 补全 |
|
||
| R22 | 96.5 | RegionNameEntry DRY 消除;注释更新;MapPinManager 执行顺序 |
|
||
| **R23** | **97.5** | R22 修复确认;识别 N1(注释不对称)N2(搜索缺 RoomType 过滤)N3(TeleportService 执行顺序) |
|
||
|
||
---
|
||
|
||
## 总评
|
||
|
||
经过 23 轮迭代,小地图系统已达到**专业商业 2D Metroidvania 发布标准**:
|
||
|
||
- **架构**:接口 + ServiceLocator + 事件驱动,各层职责清晰,扩展无需改动已有代码
|
||
- **性能**:全部热路径均有脏检查 / 对象池 / 短路机制保护,大地图下无性能风险
|
||
- **开发体验**:编辑器工具完整,策划可视化配置房间、验证错误、调试布局
|
||
- **代码健康**:DRY 违反已全部消除,readonly / 防御性拷贝 / 对象池三要素一致应用
|
||
|
||
剩余 2.5 分差距为信息级的可读性优化与编辑器小功能增强,不影响运行时正确性。
|