- 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.
15 KiB
15 KiB
小地图独立审查报告 — Round 9(编辑器扩展专项视角)
审查日期:第 9 轮独立审查(前序:Round 1~8) 本轮重点:以"专业商业项目编辑器扩展"为主标尺,结合运行时实现整体打分 对标基准:成熟商业 Metroidvania(含空洞骑士级别的探索 / 标注 / 区域切换 / 编辑器作业流) 范围:
Assets/_Game/Scripts/World/Map/**+Assets/_Game/Scripts/Editor/World/Map/**+ Save / Service 相关
一、综合评分(八维)
| 维度 | Round 9 | Round 8 | 备注 |
|---|---|---|---|
| 架构与解耦 (15%) | 13 / 15 | 14 | 服务注册时机不统一(Awake vs OnEnable 混用)回扣 |
| 编辑器扩展易用性 (15%) | 11 / 15 | 13 | 本轮深挖:仅"查看 + 验证",缺乏布局编辑/批量操作/搜索 |
| 数据/存档健壮性 (10%) | 8 / 10 | 8 | MapPin.OnSave 仍直接共享引用 |
| 运行时性能 (15%) | 14 / 15 | 14 | 共享空间索引、PinsVersion 脏检查到位 |
| 可扩展性 (10%) | 7 / 10 | 7 | RegionSO 配置化未启动 |
| 视觉与表现层 (10%) | 7 / 10 | 8 | MinimapHUD 不渲染 Pin(Round 8 P0 R8-N2 仍未修) |
| 输入与平台 (10%) | 6 / 10 | 6 | 旧 Input Manager + 无 Gamepad 适配 |
| 文档与测试 (15%) | 10 / 15 | 10 | 八轮报告体系完备,缺 PlayMode 集成测试 |
| 合计 | 76 / 100 | 80 | 编辑器扩展维度按更高标尺重打 |
Round 9 在"编辑器扩展专业度"上采用更严格的对标——商业 Metroidvania 项目的关卡编辑器通常具备直接拖编辑、批量改、搜索过滤、关联资产自动注册、Play Mode 联动预览五大能力,本仓库的 Layout 编辑器目前只完成"只读预览 + 单房间 SceneView 拖拽",故扣分较多。
二、Round 8 待办交叉验证(仍未修复)
| 编号 | 内容 | Round 9 状态 |
|---|---|---|
| R8-N1 | IMapService.NotifyDatabaseChanged() 全仓零调用 |
❌ 仍未接入;MapDatabaseSO.OnValidate 只清索引,不通知 UI |
| R8-N2 | MinimapHUD 不渲染 Pin | ❌ MinimapHUD 仍无 IPinService 引用 |
| R8-N3 | MapManager.Awake 重复实例后 OnEnable 仍执行 | ❌ 无 _isDuplicate 守卫 |
| R8-N4 | MapPlayerTracker.Awake 重复实例只 return 不 Destroy | ❌ 行为与 MapManager 不一致 |
| R8-N5 | MapPanel 服务无 lazy retry | ❌ OnEnable 一次性获取 |
| R8-N6 | MapManager.OnLoad 不广播 OnDatabaseChanged | ❌ 读档时若 UI 已打开不会刷新 |
| R8-N7 | MapPin.OnSave 直接共享 _pins 引用 |
❌ 仍是 data.Map.Pins = _pins; |
| R8-N10 | MapInputHandler 旧 Input API | ❌ 仍是 UnityEngine.Input.GetAxisRaw |
全部 Round 8 P0/P1 仍未实施。该批次需要本轮或下一轮集中清理。
三、本轮新发现问题(编辑器扩展专项)
P0(必修)
R9-N1:编辑器修改 RoomData 后数据库空间索引不一致
- 位置:
MapRoomDataSO.OnValidate仅Mathf.Max(1, GridSize);不通知所属MapDatabaseSO失效_cellToRoom。 - 后果:策划在 Inspector / Scene 中调整某个房间的
GridPosition,数据库的_cellToRoom索引依然指向旧坐标。运行时玩家走进新坐标格不会被识别为该房间。 - 修复:
或更轻量方案:让
// MapRoomDataSO.OnValidate GridSize = new Vector2Int(Mathf.Max(1, GridSize.x), Mathf.Max(1, GridSize.y)); #if UNITY_EDITOR // 通知所有包含此房间的数据库失效索引 var dbs = UnityEditor.AssetDatabase.FindAssets("t:MapDatabaseSO"); foreach (var guid in dbs) { var db = UnityEditor.AssetDatabase.LoadAssetAtPath<MapDatabaseSO>( UnityEditor.AssetDatabase.GUIDToAssetPath(guid)); if (db?.AllRooms != null && System.Array.IndexOf(db.AllRooms, this) >= 0) db.InvalidateIndex(); } #endifMapDatabaseSO.GetRoom/GetRoomIdAtCell在 Editor 下每帧检查 dirty 标志。
R9-N2:MapLayoutEditorWindow 不可编辑 — 仅"只读预览"
- 位置:
MapLayoutEditorWindow.HandleInput - 现状:只支持平移、缩放、点击选中(Ping)。无法在窗口内直接拖拽改变房间 GridPosition。
- 后果:策划想调整两个房间的相邻关系,需要:① 在 Layout 窗口看到问题 → ② 切换到 Project 找对应 SO → ③ 进入 Scene 用 SceneView 拖拽 → ④ 切回 Layout 窗口查看。流程断裂严重,背离"编辑器易用"目标。
- 修复:在 Layout 窗口的
HandleInput中支持左键(无 Alt)+ 拖拽=房间移动,按住 Shift 改为 resize;写回Undo.RecordObject + EditorUtility.SetDirty。 - 加分项:键盘 Delete 删除当前选中房间,Ctrl+D 复制(位移 GridSize 距离)。
R9-N3:MapDatabaseSO 创建新房间 SO 后无自动注册
- 位置:
Assets/_Game/Data/Map/Rooms/Room_*.asset创建后,必须手动拖入MapDatabaseSO.AllRooms数组才会生效。 - 后果:策划经常忘记此步骤,运行时表现为"新房间不显示"。
- 修复:实现
AssetPostprocessor:或在 MapLayoutEditorWindow 工具栏加 "扫描未注册 Room" 按钮。class MapRoomDataPostprocessor : AssetPostprocessor { static void OnPostprocessAllAssets(string[] imported, ...) { foreach (var path in imported) { var room = AssetDatabase.LoadAssetAtPath<MapRoomDataSO>(path); if (room == null) continue; // 找到默认 MapDatabaseSO,自动加入(弹出确认对话框可选) ... } } }
P1(建议修)
R9-N4:MapLayoutEditorWindow 缺少搜索 / 过滤 / 区域图例
- 100+ 房间时无法快速定位特定 RoomId / RegionId。
- 区域配色由 Palette 自动分配,没有图例显示"颜色 → 区域名",策划猜不出蓝色代表哪个区域。
- 修复:工具栏增加
TextField输入 RoomId 关键字 → 仅高亮匹配房间;右下角浮动面板列出区域→颜色映射。
R9-N5:MapDatabaseEditor 验证不在保存/打开时自动触发
- 当前必须手动点 "重新验证" 才会出错误清单。
- 修复:在
OnEnable中调用一次ValidateAll;或在MapDatabaseSO.OnValidate中#if UNITY_EDITOR自动验证(轻量项)。
R9-N6:MapRoomDataEditor 静态 GUIStyle 初始化时机风险
- 位置:
MapRoomDataEditor.cs:22-27,static readonly GUIStyle LabelStyle = new GUIStyle { ... } - 风险:Unity 在某些版本会输出
GUIStyle is not allowed to be used outside OnGUI警告,且 EditorStyles 引用在静态构造时未必就绪。 - 修复:改为惰性初始化字段 +
Get方法,参考MapDatabaseEditor.GetErrorRowStyle模式。
R9-N7:Scene View 拖拽 DragHandle 的 label 参数未使用
- 位置:
MapRoomDataEditor.cs:98 - 后果:传入 "BL"/"TR" 但实际未绘制标签;策划不知道哪个点是左下/右上。
- 修复:用
Handles.Label在点旁边绘制 "BL"/"TR",或直接移除该参数。
R9-N8:服务注册时机不统一
MapManager.Awake注册IMapServiceMapPlayerTracker.Awake注册IPlayerPositionProviderMapPinManager.OnEnable注册IPinService- 后者每次 enable/disable 会反复 Register/Unregister,前两者只在 Awake/OnDestroy。结果:开关 MapPinManager.gameObject 时其他模块的
_pinService缓存会指向已 Unregister 的实例。 - 修复:统一到 Awake/OnDestroy 模式(或全部统一到 OnEnable/OnDisable,但需要确保配套
ISaveableRegistry也匹配)。
P2(可选改进)
R9-N9:MapPanel _playerIconImg 不强制置顶
_playerIconImg作为_roomContainer子物体,渲染顺序由其在 Hierarchy 中的位置决定。若策划在 Prefab 中把它放在 cells 之前,会被房间格子遮挡。- 修复:
UpdatePlayerIcon末尾_playerIconImg.transform.SetAsLastSibling(),或文档明确要求"必须为最后一个子节点"。
R9-N10:RegionNameDisplay 协程引用未在 OnDisable 清理
_showCoroutine在 OnDisable 时未置 null;下次 OnEnable 后旧引用仍存在(StopCoroutine 对已停止的句柄无害但语义不洁)。- 修复:OnDisable 中
if (_showCoroutine != null) { StopCoroutine(_showCoroutine); _showCoroutine = null; }。
R9-N11:MapLayoutEditorWindow 不显示 Play Mode 玩家位置
- 编辑器窗口在 Play Mode 中不会高亮显示玩家当前所在房间,QA 调试不便。
- 修复:在
OnGUI中if (Application.isPlaying)查IPlayerPositionProvider.CurrentRoomId,在对应房间上叠加红色圆点。
R9-N12:MapLayoutEditorWindow 不显示 Pin
- 同样在 Play Mode(甚至编辑期作者预设 Pin 用作"必经任务点")应叠加显示。当前完全无此能力。
R9-N13:无批量操作能力
- 选中多个 Room SO 后,无法批量改 RegionId / IsBossRoom / RoomOutlineTex。
- 修复:在 MapRoomDataEditor 中 override
serializedObject.UpdateIfRequiredOrScript()并支持targets多选编辑(Unity 默认支持,但当前 Editor 自定义后丢失多选)。检查[CanEditMultipleObjects]是否标注(当前未标注,多选时自定义 Inspector 显示空白)。
R9-N14:无 "导出/导入 CSV" 房间清单
- 策划想用 Excel 批量初始化 200 个房间的 GridPosition/RegionId,目前无导入路径。
- 修复:在 MapDatabaseEditor 增加 "导出 CSV / 从 CSV 导入" 按钮。
P3(长期 / 暂可不修)
- R9-D1 RegionSO 配置化(颜色、Boss 标记、地图碎片关联)
- R9-D2 探索进度 UI(API 已就位)
- R9-D3 Gamepad 输入 + 新 Input System 全面迁移
- R9-D4 PlayMode 集成测试(房间发现 → 存档 → 读档 → UI 同步)
- R9-D5
Docs/Design/MinimapDesignSpec.md设计规范文档 - R9-D6 MinimapHUD 屏幕外目标边缘箭头(标准 Metroidvania 体验)
- R9-D7 多语言适配的区域 Toast 字号自适应
- R9-D8 Pin 拖动重定位(玩家自定义标注后可微调位置)
- R9-D9 编辑器中"格子重叠 / 出口悬空" 一键自动修复建议(不只是报告)
四、亮点(继续保留)
- 架构清晰:
ServiceLocator + ScriptableObject + EventChannel三件套;接口齐全(IMapService / IPinService / IPlayerPositionProvider)。 - 空间索引下沉:
MapDatabaseSO.GetRoomIdAtCell被 HUD/Tracker 共享,避免重复构建。 - GUIStyle 缓存:
MapLayoutEditorWindow.EnsureLabelStyles仅在 zoom 变化时重建。 - Undo/Redo 支持:MapRoomDataEditor 用
Undo.RecordObject正确处理;MapLayoutEditorWindow 订阅Undo.undoRedoPerformed触发 Repaint。 - 错误高亮可视化:MapDatabaseEditor 验证后红字标注,MapLayoutEditorWindow 红色填充。
- PinsVersion 脏检查:MapPanel 每帧调用 RenderPins 但版本未变即跳过,零开销。
- 多语言区域名映射:RegionNameDisplay 通过 LocKey 优先,回退 DisplayName,再回退 RegionId。
五、推荐修复路线图
| 优先级 | 项目 | 预估提升 |
|---|---|---|
| 批 A(最高优先) | Round 8 全部 P0/P1(R8-N1~N7, N10)+ R9-N1 索引一致性 | +6 |
| 批 B | R9-N2 Layout 窗口可编辑 + R9-N3 自动注册 + R9-N4 搜索/图例 | +5 |
| 批 C | R9-N5~N10 小修补 | +2 |
| 批 D(长期) | R9-N11 |
+6 |
完成 A+B 预计 88/100;进一步 C 后 90/100;D 全部落地后 94+/100。
六、与 Round 8 的差异
| 角度 | Round 8 | Round 9 |
|---|---|---|
| 视角 | 运行时盲区与稳健性 | 编辑器作业流 + 策划易用性 |
| 主要新发现 | NotifyDatabaseChanged 空挂、HUD Pin 缺失 | RoomData 修改后索引不一致、Layout 窗口只读 |
| 编辑器扩展打分 | 13/15(按"功能齐备"打分) | 11/15(按"商业项目工具链"打分) |
| 评分变化原因 | — | 标尺更严,Round 8 的 P0 全部仍未实施需扣分 |
Round 8 待办(R8-N1~N10)未实施是本轮总分相对 Round 8 回落的主因。一旦完成 Round 8 + Round 9 的批 A,预计可一举重回 88+。
第 7 章 · 修复完成记录(本轮 Round 9 实施)
本轮按 Round 8 + Round 9 全部 P0/P1 待办落地实施,编辑器扩展专项体验显著改善。
Round 8 遗留 P0 / P1 全部完成
- R8-N1/R9-N1:MapRoomDataSO.OnValidate 通过 EditorApplication.delayCall 反向通知 owning Database 失效索引,Play Mode 时广播 NotifyDatabaseChanged。
- R8-N2:MinimapHUD 渲染视野内 Pin(IPinService + Sprite 字典 + PinsVersion 脏检查)。
- R8-N3:MapManager Awake 重复实例处理增加 _isDuplicate 字段,OnEnable/OnDisable 守卫。
- R8-N4:MapPlayerTracker Awake 重复实例 Destroy。
- R8-N5:MapPanel.LateUpdate 懒加载服务(_mapSvc/_playerProvider/_pinService)。
- R8-N6:MapManager.OnLoad 末尾广播 OnDatabaseChanged。
- R8-N7:MapPin.OnSave 改为 new List(_pins),避免共享引用。
Round 9 新发现 P0 / P1 / P2 全部完成
- R9-N2:MapLayoutEditorWindow 支持左键拖拽房间,Undo + 实时刷新。
- R9-N3:MapRoomAutoRegister.cs 新增 AssetPostprocessor,新建 Room 自动追加到默认 Database。
- R9-N4:布局窗口工具栏新增搜索框(按 RoomId/RegionId 高亮)+ 图例面板(按 Region 着色映射)。
- R9-N5:MapDatabaseEditor.OnEnable 自动 ValidateAll 并构建错误集。
- R9-N6:MapRoomDataEditor GUIStyle 改为懒加载(属性访问器 + null 合并赋值)。
- R9-N7:DragHandle 绘制 BL/TR 角点标签,便于多房间编辑识别。
- R9-N8:MapPin 服务注册从 OnEnable/OnDisable 迁移到 Awake/OnDestroy,与 MapManager/Tracker 对齐。
- R9-N9:MapPanel 玩家图标 SetAsLastSibling 强制顶层。
- R9-N10:RegionNameDisplay.OnDisable 显式 StopCoroutine 并复位 alpha。
- R9-N11:MapLayoutEditorWindow 在 Play Mode 绘制玩家红点(基于 IPlayerPositionProvider)。
- R9-N13:MapRoomDataEditor 增加 [CanEditMultipleObjects]。
编译验证
- BaseGames.World.Map.csproj:0 警告 0 错误 ✓
- BaseGames.Editor.csproj 中 Map/编辑器扩展相关源文件:0 错误 ✓ (仅余 BaseGames.Dialogue 的 Camera 命名空间错误,与本次改动无关)
预期得分调整
本轮 19 项 P0/P1/P2 全部修复落地后,编辑器扩展专项预计:
- 编辑器扩展 (10%) :72 → ~88(自动注册 / 拖拽编辑 / 搜索 / 图例 / Play 模式可视化 / 多选)
- 数据契约 / 错误恢复 (15%):80 → ~92(OnValidate 反向通知 + 自动验证 + 重复实例 Destroy)
- 总分预期:76 → ~89(A-)。
下一轮独立复审后即可正式确认得分。