- 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.
8.0 KiB
小地图系统 独立审查报告 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)定义为:
public interface ISaveable
{
void OnSave(SaveData saveData);
void OnLoad(SaveData saveData);
}
但 TeleportService 实现的是:
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 未正确实现该接口,注册调用将在运行时无效。
修复方案:
-
SaveData.cs— 在MapSaveData中添加:public HashSet<string> UnlockedTeleportRoomIds = new(); -
TeleportService.cs— 替换SaveKey/Serialize/Deserialize为: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 也无法据此渲染传送图标。
// 当前
public enum RoomType
{
None = 0,
BossRoom = 1 << 0,
SavePoint = 1 << 1,
Shop = 1 << 2,
Merchant = 1 << 3,
Challenge = 1 << 4,
// ❌ 缺少 TeleportStation
}
修复:
RoomType添加TeleportStation = 1 << 5MapPanel添加[SerializeField] private Sprite _iconTeleport;字段MapPanel.ChooseIcon()中补充传送站图标逻辑MapPanel获取ITeleportService,在BuildGrid/RefreshCell时区分
"已解锁传送站"和"未解锁传送站"的颜色/图标
N3(中等):TeleportService._pendingSourceRoomId 是死代码
文件:TeleportService.cs,第 108 行
问题:
_pendingSourceRoomId = sourceRoomId; // ← 赋值后从未读取
OnTeleportRequested?.Invoke(sourceRoomId, targetRoomId); // sourceRoomId 已直接传入
_pendingSourceRoomId 只被写入,永远不被读取,是无用的私有字段。
修复:删除 _pendingSourceRoomId 字段及赋值语句。
N4(低优先级):MapLayoutEditorWindow.DrawExitLines 每帧 new HashSet
文件:MapLayoutEditorWindow.cs,DrawExitLines() 方法(约第 453 行)
问题:
private void DrawExitLines(MapDatabaseSO db, ...)
{
var drawn = new HashSet<(string, string)>(); // ❌ 每次 OnGUI 都分配新对象
...
}
编辑器 OnGUI 以每秒多次频率调用,持续产生 GC 分配,可能导致编辑器卡顿。
修复:将 drawn 提升为类字段 _drawnExitPairs,在方法内仅调用 Clear()。
N5(低优先级):MapPanel.OnMapUpdated 未标注 [Obsolete]
文件:MapPanel.cs,第 313 行
问题:
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 |