- 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.
189 lines
8.5 KiB
Markdown
189 lines
8.5 KiB
Markdown
# 小地图系统 R26 独立评审报告
|
||
|
||
**评审轮次**:Round 26(R25 修复后全面重审)
|
||
**评审基准**:成熟 2D Metroidvania 游戏商业级标准 + 专业编辑器扩展
|
||
|
||
---
|
||
|
||
## 一、各维度评分
|
||
|
||
| 维度 | 满分 | 得分 | vs R25(修复后) |
|
||
|------|------|------|----------------|
|
||
| 架构设计 & 解耦合 | 25 | 24.0 | -0.5 |
|
||
| 性能 & 运行时效率 | 20 | 19.5 | ±0 |
|
||
| 编辑器工具质量 | 15 | 14.5 | ±0 |
|
||
| 代码质量 & 可维护性 | 15 | 13.8 | -0.2 |
|
||
| 存档 & 持久化 | 10 | 9.5 | ±0 |
|
||
| 可扩展性 | 10 | 9.5 | ±0 |
|
||
| 功能完整性 | 5 | 5.0 | ±0 |
|
||
| **综合** | **100** | **95.8** | **-1.7** |
|
||
|
||
> R25(修复后基准):97.5 → R26:95.8(发现 2 项新问题)
|
||
|
||
---
|
||
|
||
## 二、R25 修复全部落地确认
|
||
|
||
| 修复项 | 状态 |
|
||
|--------|------|
|
||
| C1:TeleportService.CanTeleportTo `_mapSvc == null \|\| ...` → `_mapSvc != null && ...` | ✅ |
|
||
| N1:MapPanel._subs 死代码字段及 Clear() 调用全部移除 | ✅ |
|
||
| N2:MapInputHandler.Awake 展开 + `#if UNITY_EDITOR \|\| DEVELOPMENT_BUILD` 校验警告 | ✅ |
|
||
|
||
---
|
||
|
||
## 三、持续表现优秀的设计
|
||
|
||
| 维度 | 设计亮点 |
|
||
|------|---------|
|
||
| **接口解耦** | IMapService / IPlayerPositionProvider / IPinService / ITeleportService 全通过 ServiceLocator;UI 层无任何具体类引用 |
|
||
| **执行顺序链** | MapManager(-700)→MapPlayerTracker(-600)→MapPinManager(-500)→TeleportService(-400)→UI(0);服务注册先于所有消费方 |
|
||
| **空间索引共享** | MapDatabaseSO 统一维护 `_cellToRoom` 字典;MinimapHUD 的 O(viewRadius²) 剔除与 MapPlayerTracker 的 O(1) 房间判定共享同一索引,无重复构建 |
|
||
| **五类对象池** | MapPanel(Cell/Exit/Pin)+ MinimapHUD(Cell/Pin);全量 Disable 入池、Enable 出池,OnDestroy 销毁剩余;发现动画协程自清理(R20-N1)|
|
||
| **ISaveable 防御性拷贝** | MapManager / MapPinManager / TeleportService 三处对称:OnSave new HashSet/List,OnLoad Clear+foreach Add |
|
||
| **_servicesReady 短路** | MapPanel + MinimapHUD 均在三服务就绪后置 true,消除每帧 ServiceLocator 查询 |
|
||
| **LateUpdate 脏检查** | MapPanel 玩家图标(RoomId + NormPos 双字段);MinimapHUD 玩家圆点(同);Pin 版本号脏检查——全部避免无效 RectTransform 读写 |
|
||
| **DRY** | ChooseDisplayIcon 集中在 MapRoomDataSO;BuildRegionDict / ResolveRegionDisplayName 集中在 MapServiceExtensions;MapPanel + MinimapHUD 均通过委托调用 |
|
||
| **编辑器工具** | 拖拽/缩放/验证(4类错误)/搜索(RoomId/RegionId/RoomType三维)/图例/Play Mode 玩家叠加;GUIStyle 缓存;MatchesRoomType 不区分大小写 |
|
||
| **存档健壮性** | OnLoad 空集合防御、空 id 过滤;OnValidate 自动 Trim RoomId、迁移旧 bool 字段;`delayCall -=;+=` 防止 Inspector 快速操作重复触发 |
|
||
| **UnsubscribeServices 有意不对称** | MapPanel(OnDestroy 末尾不清空引用)vs MinimapHUD(置 null + 重置标志,支持跨场景重连)——均有注释说明 |
|
||
|
||
---
|
||
|
||
## 四、新发现问题
|
||
|
||
### N1 — Normal:ITeleportService 接口不完整(`UnlockTeleportStation` / `NotifyTeleportCompleted` 缺失)
|
||
|
||
**文件**:`ITeleportService.cs`(接口缺失),`TeleportService.cs`(L103–115 公开方法)
|
||
|
||
**问题**:
|
||
|
||
```csharp
|
||
// ITeleportService 接口中存在的方法:
|
||
bool CanTeleportTo(string roomId);
|
||
void RequestTeleport(string targetRoomId);
|
||
event Action<string, string> OnTeleportRequested;
|
||
event Action<string> OnTeleportCompleted;
|
||
|
||
// ─── 缺失的两个写操作方法 ───
|
||
// TeleportService 上有 public 定义,但不在接口中:
|
||
public void NotifyTeleportCompleted(string arrivedRoomId); // 场景加载系统须调用
|
||
public void UnlockTeleportStation(string roomId); // 游戏触发器须调用
|
||
```
|
||
|
||
**影响**:
|
||
- **场景加载系统**完成传送后需调用 `NotifyTeleportCompleted`,但通过 `ServiceLocator.GetOrDefault<ITeleportService>()` 只能拿到接口,无法访问该方法;调用方被迫使用具体类型(`TeleportService`)或反射,**破坏接口解耦原则**。
|
||
- **传送站触发器**(`OnTriggerEnter` 等)需要调用 `UnlockTeleportStation`,同样面临相同问题。
|
||
- 接口 + ServiceLocator 模式在整个系统中一致使用,此处缺口会让后续维护者混淆。
|
||
|
||
**修复**:在 `ITeleportService` 中补全两个方法:
|
||
```csharp
|
||
/// <summary>解锁指定房间的传送点(游戏触发器调用)。</summary>
|
||
void UnlockTeleportStation(string roomId);
|
||
|
||
/// <summary>场景加载系统传送完成后调用,触发 OnTeleportCompleted 事件。</summary>
|
||
void NotifyTeleportCompleted(string arrivedRoomId);
|
||
```
|
||
|
||
---
|
||
|
||
### N2 — Normal:MapPinManager 缺少 `_isDuplicate` 单例保护(与 MapManager / MapPlayerTracker 不一致)
|
||
|
||
**文件**:`MapPin.cs`(MapPinManager 类,L32–37)
|
||
|
||
**问题**:
|
||
|
||
```csharp
|
||
// MapManager(正确):
|
||
private void Awake()
|
||
{
|
||
if (ServiceLocator.GetOrDefault<IMapService>() != null) { _isDuplicate = true; Destroy(gameObject); return; }
|
||
ServiceLocator.Register<IMapService>(this);
|
||
}
|
||
|
||
// MapPlayerTracker(正确):
|
||
private void Awake()
|
||
{
|
||
if (ServiceLocator.GetOrDefault<IPlayerPositionProvider>() != null) { _isDuplicate = true; ... }
|
||
...
|
||
}
|
||
|
||
// MapPinManager(缺少保护):
|
||
private void Awake()
|
||
{
|
||
// ← 无重复实例检测
|
||
ServiceLocator.Register<IPinService>(this); // 若已有注册,直接覆盖
|
||
}
|
||
```
|
||
|
||
**影响**:
|
||
- 若场景中意外存在两个 MapPinManager(Persistent 场景加载两次、DontDestroyOnLoad 重复等),第二个实例会覆盖 ServiceLocator 注册,但第一个实例的 `ISaveable` 仍保持注册状态(`OnEnable` 中加入 SaveableRegistry),导致存档时 `OnSave` 被调用两次、集合竞争写入 `SaveData.Map.Pins`。
|
||
- 系统其他三处服务均有 `_isDuplicate` 守卫,此处缺失属于架构一致性漏洞。
|
||
|
||
**修复**:
|
||
```csharp
|
||
private void Awake()
|
||
{
|
||
if (ServiceLocator.GetOrDefault<IPinService>() != null)
|
||
{
|
||
_isDuplicate = true;
|
||
Destroy(gameObject);
|
||
return;
|
||
}
|
||
ServiceLocator.Register<IPinService>(this);
|
||
}
|
||
|
||
// OnEnable / OnDisable / OnDestroy 首行加 if (_isDuplicate) return;
|
||
```
|
||
|
||
---
|
||
|
||
## 五、各文件评分摘要
|
||
|
||
| 文件 | 状态 | 备注 |
|
||
|------|------|------|
|
||
| MapManager.cs | ✅ 完整 | 执行顺序/ISaveable/IMapService/缓存/LINQ 仅在非热路径 |
|
||
| MapPlayerTracker.cs | ✅ 完整 | O(1) 空间索引/平滑归一化/单例保护 |
|
||
| **MapPinManager(MapPin.cs)** | ⚠️ N2 | 缺少 _isDuplicate 保护 |
|
||
| **TeleportService.cs** | ⚠️ N1 | 接口缺口(UnlockTeleportStation/NotifyTeleportCompleted) |
|
||
| MapPanel.cs | ✅ 完整 | R25-N1 _subs 死代码已移除;五层解耦;三对象池 |
|
||
| MinimapHUD.cs | ✅ 完整 | O(viewRadius²) 剔除;跨场景重连;GC 友好缓冲区 |
|
||
| MapInputHandler.cs | ✅ 完整 | R24-N2 单一 scale 状态;R25-N2 配置警告 |
|
||
| MinimapInputHandler.cs | ✅ 完整 | 干净委托,单职责 |
|
||
| MapRoomCellUI.cs | ✅ 完整 | 双场景复用;PlayRevealAnim 颜色恢复 |
|
||
| MapRoomDataSO.cs / MapDatabaseSO | ✅ 完整 | ChooseDisplayIcon 集中;空间索引共享;OnValidate 含延迟通知防抖 |
|
||
| MapServiceExtensions.cs | ✅ 完整 | 纯静态无状态;DRY 中心 |
|
||
| RegionNameDisplay.cs | ✅ 完整 | CompositeDisposable 正确使用;协程 OnDisable 终止安全 |
|
||
| **ITeleportService.cs** | ⚠️ N1 | 缺少两个写操作方法声明 |
|
||
| MapLayoutEditorWindow.cs(编辑器) | ✅ 完整 | 搜索+图例+拖拽+验证+GUIStyle 缓存+✕按钮 |
|
||
|
||
---
|
||
|
||
## 六、修复优先级
|
||
|
||
| 编号 | 严重度 | 文件 | 影响 | 建议 |
|
||
|------|--------|------|------|------|
|
||
| N1 | 🟡 Normal | ITeleportService.cs + TeleportService.cs | 接口不完整导致调用方被迫使用具体类型 | 立即修复 |
|
||
| N2 | 🟡 Normal | MapPin.cs(MapPinManager) | 多实例场景存档竞争写入 | 立即修复 |
|
||
|
||
---
|
||
|
||
## 七、评分历史
|
||
|
||
| 轮次 | 综合评分 | 主要变化 |
|
||
|------|---------|---------|
|
||
| R17 | 93.0 | 初轮重构后 |
|
||
| R18–R19 | 93.8 | 平移/缩放/协程修复 |
|
||
| R20 | 94.2 | DRY + 协程自清理 |
|
||
| R21 | 95.0 | ServicesReady 对称 |
|
||
| R22 | 96.5 | BuildRegionDict 集中 |
|
||
| R23 | 97.5 | MatchesRoomType + 执行顺序 |
|
||
| R24 | 97.5 | TeleportService 重建(遗留 C1) |
|
||
| R25(修复前) | 95.5 | 发现 C1 逻辑反转 + N1 死代码 + N2 无警告 |
|
||
| R25(修复后基准) | 97.5 | 三项全修复 |
|
||
| **R26** | **95.8** | 发现 N1(接口不完整-1.2)+ N2(单例缺失-0.5)|
|
||
|
||
---
|
||
|
||
*R26 评审完成时间:2026-05-25*
|