- 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.
151 lines
5.9 KiB
Markdown
151 lines
5.9 KiB
Markdown
# 小地图系统 独立审查报告 Round 13
|
||
|
||
**审查范围**:`Assets/_Game/Scripts/World/Map/`(Runtime 17 文件)+
|
||
`Assets/_Game/Scripts/Editor/World/Map/`(Editor 4 文件)
|
||
|
||
**对标标准**:成熟 2D Metroidvania 类型游戏的专业编辑器扩展级别,
|
||
面向开发人员和策划人员,要求架构解耦、高性能、高可扩展性。
|
||
|
||
---
|
||
|
||
## 总评分
|
||
|
||
| 维度 | 满分 | 得分 | 说明 |
|
||
|---|---|---|---|
|
||
| 架构解耦 | 10 | 9.0 | 接口 + ServiceLocator 完整;唯一遗留:MapPin.cs 文件名与类名不符(历史问题) |
|
||
| 性能 | 10 | 9.0 | 对象池完整、dirty check 完整;FormatException 风险影响稳定性 |
|
||
| 编辑器 UX | 10 | 8.5 | 可视化布局编辑器完善;缺少快捷键说明与快速创建 |
|
||
| 数据模型 | 10 | 8.5 | RoomType [Flags] + HasCustomExitPos 完善;但 DrawExits 未使用 HasCustomExitPos |
|
||
| 输入系统 | 10 | 7.5 | InputReaderSO 对接完整;但 CycleZoom 无绑定、缺少"居中"快捷键 |
|
||
| 功能完整性 | 10 | 7.5 | ITeleportService 接口已定义但无实现 |
|
||
| 代码质量 | 10 | 9.0 | 注释质量高;MapProgressDisplay.Refresh() 缺少异常防护 |
|
||
| 可扩展性 | 10 | 9.0 | SO 驱动、Event Channel 解耦、Region 机制完善 |
|
||
| **总分** | **80** | **68.0** | **换算 100 分:85.0 / 100(B+)** |
|
||
|
||
> **相比 R12(86.2/100)**:发现 2 个遗留 Bug(N1 DrawExits、N2 FormatException)和 3 个增强点,分数略有下调。修复后预计 91+。
|
||
|
||
---
|
||
|
||
## Bug 发现
|
||
|
||
### N1(严重):MapPanel.DrawExits() 忽略 HasCustomExitPos
|
||
|
||
**文件**:`MapPanel.cs`,`DrawExits()` 方法
|
||
|
||
**问题**:出口连接线的位置直接使用 `exit.ExitGridPos * FullMapCellPixels`,
|
||
未检查 `HasCustomExitPos` 标志。当策划未配置自定义出口坐标时,
|
||
`ExitGridPos` 默认为 `Vector2Int.zero`,所有连接线均渲染在 `_roomContainer` 原点 (0, 0) 处。
|
||
|
||
`MapLayoutEditorWindow.DrawExitLines()` 已在 R12 修复了同样问题,但 `MapPanel.DrawExits()` 被遗漏。
|
||
|
||
```csharp
|
||
// ❌ 当前代码:未检查 HasCustomExitPos
|
||
conn.rectTransform.anchoredPosition = new Vector2(
|
||
exit.ExitGridPos.x * MapGridConstants.FullMapCellPixels,
|
||
exit.ExitGridPos.y * MapGridConstants.FullMapCellPixels);
|
||
|
||
// ✅ 修复:回退到方向中点
|
||
Vector2Int gridPos = exit.HasCustomExitPos
|
||
? exit.ExitGridPos
|
||
: GetExitFallbackGridPos(room, exit); // 按 ExitDirection 计算房间边缘中点
|
||
```
|
||
|
||
---
|
||
|
||
### N2(中等):MapProgressDisplay.Refresh() 无 FormatException 防护
|
||
|
||
**文件**:`MapProgressDisplay.cs`,`Refresh()` 方法
|
||
|
||
**问题**:`_globalFormat` / `_regionFormat` 是 Inspector 可编辑字段;
|
||
策划填写错误格式字符串(如 `{2}` 超出参数范围)时,
|
||
`string.Format(...)` 抛出 `FormatException`,导致运行时异常。
|
||
|
||
```csharp
|
||
// ❌ 当前代码:无异常防护
|
||
_globalProgressText.text = string.Format(_globalFormat, progress);
|
||
|
||
// ✅ 修复:try-catch + fallback
|
||
try { _globalProgressText.text = string.Format(_globalFormat, progress); }
|
||
catch (FormatException)
|
||
{ _globalProgressText.text = $"{progress:P0}";
|
||
Debug.LogWarning($"[MapProgressDisplay] 格式字符串错误:{_globalFormat}", this); }
|
||
```
|
||
|
||
---
|
||
|
||
## 增强点
|
||
|
||
### N3(高优先):MinimapHUD.CycleZoom() 无输入绑定
|
||
|
||
**文件**:`MinimapHUD.cs`,`CycleZoom()` 方法
|
||
|
||
`CycleZoom()` 是一个 `public` 方法,设计意图是绑定到按键。但:
|
||
- `InputReaderSO` 没有 `CycleMinimapZoomEvent` 事件
|
||
- 没有 `MinimapInputHandler` 组件订阅该方法
|
||
|
||
**修复**:
|
||
1. 在 `InputReaderSO` 添加 `CycleMinimapZoomEvent`
|
||
2. 新建 `MinimapInputHandler.cs` 组件,绑定按键 → `CycleZoom()`
|
||
|
||
---
|
||
|
||
### N4(中等):MapInputHandler 缺少"居中到玩家"快捷键
|
||
|
||
**文件**:`MapInputHandler.cs`,`MapPanel.cs`
|
||
|
||
全屏地图打开后无"居中"快捷键(常见 UX 需求)。
|
||
`MapPanel.CenterOnCurrentRoom()` 为 `private`,外部无法调用。
|
||
|
||
**修复**:
|
||
1. 在 `InputReaderSO` 添加 `MapCenterEvent`
|
||
2. 将 `CenterOnCurrentRoom()` 改为 `public`
|
||
3. 在 `MapInputHandler.OnEnable/OnDisable` 中订阅
|
||
|
||
---
|
||
|
||
### FA(缺失):ITeleportService 无具体实现
|
||
|
||
**文件**:`ITeleportService.cs`(接口已定义;无对应实现类)
|
||
|
||
定义了完整的传送服务接口,但无任何具体实现类。地图 UI 无法调用传送功能。
|
||
|
||
**修复**:新建 `TeleportService.cs`,实现 `ITeleportService`:
|
||
- `CanTeleportTo`:检查解锁状态 + `IMapService.IsExplored`
|
||
- `RequestTeleport`:触发 `OnTeleportRequested` 事件 + 经由 `StringEventChannelSO` 驱动场景加载
|
||
- `ISaveable` 持久化已解锁传送点列表
|
||
|
||
---
|
||
|
||
## 已验证正常的项目
|
||
|
||
经本轮全量重读确认以下 R12 修复均完整到位:
|
||
|
||
| 项目 | 状态 |
|
||
|---|---|
|
||
| MapPanel Cell/Exit 对象池(N1) | ✅ |
|
||
| DrawExitLines HashSet 去重(N2) | ✅ |
|
||
| MapPinConfigSO + O(1) dict cache(N3) | ✅ |
|
||
| MapRoomAutoRegister null cleanup(N4) | ✅ |
|
||
| HasCustomExitPos flag in MapLayoutEditorWindow(N5) | ✅ |
|
||
| RegionNameDisplay O(1) dict lookup(N6) | ✅ |
|
||
| _servicesReady 短路(N7) | ✅ |
|
||
| 移除 OnMapUpdated 双重订阅(N8) | ✅ |
|
||
| RoomFlags [Flags] 枚举兼容(N9) | ✅ |
|
||
| PlayRevealAnim 协程(FC) | ✅ |
|
||
| CycleZoom() 方法存在(FA — 但无绑定) | ⚠ 见 N3 |
|
||
| TryGetRoomAtWorldPos(FB) | ✅ |
|
||
| CreatePinAtWorldPos 扩展(FE) | ✅ |
|
||
| MapProgressDisplay 组件存在(FF) | ⚠ 见 N2 |
|
||
|
||
---
|
||
|
||
## 实现计划
|
||
|
||
| 编号 | 改动 | 文件 |
|
||
|---|---|---|
|
||
| N1 | DrawExits 使用 HasCustomExitPos + 方向回退 | `MapPanel.cs` |
|
||
| N2 | Refresh() FormatException 防护 | `MapProgressDisplay.cs` |
|
||
| N3 | CycleMinimapZoomEvent + MinimapInputHandler | `InputReaderSO.cs`、新建 `MinimapInputHandler.cs` |
|
||
| N4 | MapCenterEvent + 公开 CenterOnCurrentRoom | `InputReaderSO.cs`、`MapPanel.cs`、`MapInputHandler.cs` |
|
||
| FA | TeleportService 具体实现 | 新建 `TeleportService.cs` |
|