- 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.
221 lines
8.0 KiB
Markdown
221 lines
8.0 KiB
Markdown
# 小地图系统 独立审查报告 Round 14
|
||
|
||
**审查范围**:`Assets/_Game/Scripts/World/Map/`(Runtime 19 文件)+
|
||
`Assets/_Game/Scripts/Editor/World/Map/`(Editor 4 文件)
|
||
|
||
**对标标准**:成熟 2D Metroidvania 类型游戏的专业编辑器扩展级别,
|
||
面向开发人员和策划人员,要求架构解耦、高性能、高可扩展性。
|
||
|
||
---
|
||
|
||
## 总评分(修复前)
|
||
|
||
| 维度 | 满分 | 得分 | 说明 |
|
||
|---|---|---|---|
|
||
| 架构解耦 | 10 | 9.0 | 接口 + ServiceLocator 完整;MapPin.cs 文件名历史遗留 |
|
||
| 性能 | 10 | 9.0 | 对象池完整、dirty check 完整;DrawExitLines 每帧 new HashSet |
|
||
| 编辑器 UX | 10 | 9.0 | 可视化布局编辑器成熟;DrawExitLines HashSet GC 小问题 |
|
||
| 数据模型 | 10 | 8.5 | RoomType [Flags] 完善;缺少 TeleportStation 类型 |
|
||
| 输入系统 | 10 | 8.5 | InputSystem 软绑定完整;MinimapInputHandler R13 已加 |
|
||
| 功能完整性 | 10 | 6.5 | TeleportService 存在但 MapPanel 无传送 UI 集成;SaveKey 模式错误 |
|
||
| 代码质量 | 10 | 8.5 | 注释质量高;TeleportService 含死代码字段 |
|
||
| 存档系统 | 10 | 5.0 | **TeleportService.ISaveable 签名错误(编译错误)** |
|
||
| 可扩展性 | 10 | 9.0 | SO 驱动、Event Channel 解耦、Region 机制完善 |
|
||
| **总分** | **90** | **73.0** | **换算 100 分:81.1 / 100(B)** |
|
||
|
||
> **相比 R13(85.0/100)**:R13 N1–N4 + FA 修复提升了输入系统与 Bug 修复维度,
|
||
> 但 TeleportService 的 ISaveable 签名引入了编译错误(N1),使存档分大幅下降。
|
||
> 所有修复后预计 **92+**。
|
||
|
||
---
|
||
|
||
## Bug 发现
|
||
|
||
### N1(致命 — 编译错误):TeleportService 实现了错误的 ISaveable 签名
|
||
|
||
**文件**:`TeleportService.cs`(R13-FA 新增),`SaveData.cs`
|
||
|
||
**问题**:
|
||
`ISaveable` 接口(`BaseGames.Core.Save`)定义为:
|
||
|
||
```csharp
|
||
public interface ISaveable
|
||
{
|
||
void OnSave(SaveData saveData);
|
||
void OnLoad(SaveData saveData);
|
||
}
|
||
```
|
||
|
||
但 `TeleportService` 实现的是:
|
||
|
||
```csharp
|
||
public string SaveKey => "TeleportService"; // ❌ 接口中不存在此成员
|
||
public string Serialize() { ... } // ❌ 接口中不存在此方法
|
||
public void Deserialize(string data) { ... } // ❌ 接口中不存在此方法
|
||
// ❌ 缺少 OnSave(SaveData) / OnLoad(SaveData)
|
||
```
|
||
|
||
这导致 `BaseGames.World.Map` 程序集**无法编译**,整个地图系统全部失效。
|
||
此外,`MapSaveData`(`SaveData.cs`)中没有 `UnlockedTeleportRoomIds` 字段,
|
||
即使签名正确,传送数据也无处存储。
|
||
|
||
**同时**,`TeleportService` 向 `ISaveableRegistry` 注册自身(OnEnable 中),
|
||
但 `ISaveableRegistry` 期望的是 `ISaveable` 对象,
|
||
而 `TeleportService` 未正确实现该接口,注册调用将在运行时无效。
|
||
|
||
**修复方案**:
|
||
|
||
1. **`SaveData.cs`** — 在 `MapSaveData` 中添加:
|
||
```csharp
|
||
public HashSet<string> UnlockedTeleportRoomIds = new();
|
||
```
|
||
|
||
2. **`TeleportService.cs`** — 替换 `SaveKey/Serialize/Deserialize` 为:
|
||
```csharp
|
||
public void OnSave(SaveData saveData)
|
||
{
|
||
saveData.Map.UnlockedTeleportRoomIds = new HashSet<string>(_unlockedRoomIds);
|
||
}
|
||
public void OnLoad(SaveData saveData)
|
||
{
|
||
_unlockedRoomIds.Clear();
|
||
if (saveData.Map.UnlockedTeleportRoomIds != null)
|
||
foreach (var id in saveData.Map.UnlockedTeleportRoomIds)
|
||
_unlockedRoomIds.Add(id);
|
||
}
|
||
```
|
||
移除 `ISaveableRegistry` 手动注册(`OnSave/OnLoad` 由 `SaveManager` 直接调用,无需 Registry)。
|
||
|
||
---
|
||
|
||
### N2(高优先级):RoomType 缺少 TeleportStation 标志位
|
||
|
||
**文件**:`MapRoomDataSO.cs`,`MapPanel.cs`
|
||
|
||
**问题**:
|
||
`RoomType` [Flags] 枚举目前有 BossRoom / SavePoint / Shop / Merchant / Challenge,
|
||
但没有 `TeleportStation`。`TeleportService` 的解锁状态存储于运行时,
|
||
但**策划无法在 SO 上标记"此房间有传送站"**,`MapPanel` 也无法据此渲染传送图标。
|
||
|
||
```csharp
|
||
// 当前
|
||
public enum RoomType
|
||
{
|
||
None = 0,
|
||
BossRoom = 1 << 0,
|
||
SavePoint = 1 << 1,
|
||
Shop = 1 << 2,
|
||
Merchant = 1 << 3,
|
||
Challenge = 1 << 4,
|
||
// ❌ 缺少 TeleportStation
|
||
}
|
||
```
|
||
|
||
**修复**:
|
||
1. `RoomType` 添加 `TeleportStation = 1 << 5`
|
||
2. `MapPanel` 添加 `[SerializeField] private Sprite _iconTeleport;` 字段
|
||
3. `MapPanel.ChooseIcon()` 中补充传送站图标逻辑
|
||
4. `MapPanel` 获取 `ITeleportService`,在 `BuildGrid` / `RefreshCell` 时区分
|
||
"已解锁传送站"和"未解锁传送站"的颜色/图标
|
||
|
||
---
|
||
|
||
### N3(中等):TeleportService._pendingSourceRoomId 是死代码
|
||
|
||
**文件**:`TeleportService.cs`,第 108 行
|
||
|
||
**问题**:
|
||
|
||
```csharp
|
||
_pendingSourceRoomId = sourceRoomId; // ← 赋值后从未读取
|
||
OnTeleportRequested?.Invoke(sourceRoomId, targetRoomId); // sourceRoomId 已直接传入
|
||
```
|
||
|
||
`_pendingSourceRoomId` 只被写入,永远不被读取,是无用的私有字段。
|
||
|
||
**修复**:删除 `_pendingSourceRoomId` 字段及赋值语句。
|
||
|
||
---
|
||
|
||
### N4(低优先级):MapLayoutEditorWindow.DrawExitLines 每帧 new HashSet
|
||
|
||
**文件**:`MapLayoutEditorWindow.cs`,`DrawExitLines()` 方法(约第 453 行)
|
||
|
||
**问题**:
|
||
|
||
```csharp
|
||
private void DrawExitLines(MapDatabaseSO db, ...)
|
||
{
|
||
var drawn = new HashSet<(string, string)>(); // ❌ 每次 OnGUI 都分配新对象
|
||
...
|
||
}
|
||
```
|
||
|
||
编辑器 `OnGUI` 以每秒多次频率调用,持续产生 GC 分配,可能导致编辑器卡顿。
|
||
|
||
**修复**:将 `drawn` 提升为类字段 `_drawnExitPairs`,在方法内仅调用 `Clear()`。
|
||
|
||
---
|
||
|
||
### N5(低优先级):MapPanel.OnMapUpdated 未标注 [Obsolete]
|
||
|
||
**文件**:`MapPanel.cs`,第 313 行
|
||
|
||
**问题**:
|
||
|
||
```csharp
|
||
private void OnMapUpdated(string roomId) { /* R12-N8 已废弃 */ }
|
||
```
|
||
|
||
方法已废弃但未加 `[Obsolete]` 标注,后续开发者可能误以为该方法仍有业务逻辑。
|
||
|
||
**修复**:添加 `[Obsolete("R12-N8: 由 OnExplorationChanged 统一处理,仅保留序列化兼容性。")]`。
|
||
|
||
---
|
||
|
||
## 架构亮点(保持优秀)
|
||
|
||
以下设计在本轮审查中仍被评定为业界优秀水平:
|
||
|
||
| 亮点 | 说明 |
|
||
|---|---|
|
||
| 接口 + ServiceLocator | 4 个服务接口完全解耦,UI 层零具体类型依赖 |
|
||
| O(1) 空间索引 | `MapDatabaseSO.GetRoomIdAtCell` 惰性构建,`MinimapHUD` + `MapPlayerTracker` 共享 |
|
||
| 三重对象池 | `MapPanel`(cell/pin/exit)+ `MinimapHUD`(cell/pin)零 GC 渲染 |
|
||
| Dirty Flag 模式 | 面板关闭期间收到事件,OnEnable 时应用——无事件遗漏 |
|
||
| `_servicesReady` 短路 | 三服务就绪后跳过 LateUpdate 的 ServiceLocator 查询 |
|
||
| `PinsVersion` 脏检查 | 无需事件订阅,整数比较即可判断 Pin 集合是否变化 |
|
||
| 可视化布局编辑器 | MapLayoutEditorWindow:拖拽/吸附/缩放/搜索/Play 模式覆盖层 |
|
||
| MapRoomAutoRegister | AssetPostprocessor 自动注册新房间 SO,零手动操作 |
|
||
| InputSystem 软绑定 | `throwIfNotFound: false` 防 InputActionAsset 缺失崩溃 |
|
||
| `HasCustomExitPos` 标志 | 消除 `Vector2Int.zero` 哨兵值歧义(R12-N5,R13-N1 已修复) |
|
||
|
||
---
|
||
|
||
## 修复优先级总表
|
||
|
||
| 编号 | 严重程度 | 文件 | 说明 |
|
||
|---|---|---|---|
|
||
| N1 | ★★★★★ 致命 | `TeleportService.cs` + `SaveData.cs` | ISaveable 签名错误导致编译失败 |
|
||
| N2 | ★★★★ 高 | `MapRoomDataSO.cs` + `MapPanel.cs` | 缺少 TeleportStation RoomType + MapPanel 传送 UI 集成 |
|
||
| N3 | ★★★ 中 | `TeleportService.cs` | `_pendingSourceRoomId` 死代码 |
|
||
| N4 | ★★ 低 | `MapLayoutEditorWindow.cs` | DrawExitLines HashSet 每帧分配 |
|
||
| N5 | ★ 极低 | `MapPanel.cs` | OnMapUpdated 缺 [Obsolete] |
|
||
|
||
---
|
||
|
||
## 修复后预估评分
|
||
|
||
| 维度 | 修复前 | 修复后 |
|
||
|---|---|---|
|
||
| 架构解耦 | 9.0 | 9.0 |
|
||
| 性能 | 9.0 | 9.5(N4 修复) |
|
||
| 编辑器 UX | 9.0 | 9.0 |
|
||
| 数据模型 | 8.5 | 9.5(N2 TeleportStation) |
|
||
| 输入系统 | 8.5 | 8.5 |
|
||
| 功能完整性 | 6.5 | 9.0(N2 传送 UI) |
|
||
| 代码质量 | 8.5 | 9.0(N3/N5) |
|
||
| 存档系统 | 5.0 | 9.5(N1 修复) |
|
||
| 可扩展性 | 9.0 | 9.0 |
|
||
| **总分** | **81.1** | **≈ 92.3** |
|