# UIPrefabCheck 能力清单与优化方案 > 源文件:`Assets/Editor/UIPrefabCheck.cs`(1434 行,本目录含只读副本,MD5 `74D4A63A33869AE560BA6B522E2DABFC`) > 菜单入口:`Tools/UI/Prefab 和图片优化扫描工具`、`Tools/UI/界面性能巡检` > 评估时间:2026-09-10 | **结论:工具本身设计扎实(只读、分页、LSH 相似图、dHash 带 alpha 包围盒),但对"降 DC"这个目标存在 4 个关键能力缺口,其中 2 个改动量很小、收益很大。** --- ## 第一章 它现在是什么(两个类,7 个标签页) 一个文件里两个独立部分: | 类 | 行号 | 职责 | 运行时机 | | --- | --- | --- | --- | | `UIRuntimePerformanceAudit` | L17-478 | 界面性能巡检(自动逐个 Show/Close 面板 + 手动录制实际操作) | **必须 Play Mode** | | `UIPrefabCheck : EditorWindow` | L480-1433 | Prefab / 图片 / 图集静态扫描(6 个页)+ 承载上面那个巡检页 | 编辑模式即可 | ### 1.1 七个标签页逐项能力 | 标签页 | 绘制方法 | 扫什么 | 判定标准 / 阈值 | 能回答什么问题 | | --- | --- | --- | --- | --- | | 引用明细 | `DrawReferencePage` L662 | 每个 prefab 里的 `Image.sprite` / `RawImage.texture` / `SpriteRenderer.sprite` | 图片不在 `gui/` 或 `PSD2UI/UI/` 下 → 标"非标准 UI 资源目录"(`IsStandardUiPath` L1431) | 某张图被哪个 prefab 的哪个节点用了、属于哪个图集(可点击定位图集) | | 多预制体共用 | `DrawSharedPage` L696 | 按 `AssetPath` 分组,`PrefabPaths.Count >= 2` | — | 哪些图被多个 prefab 共用(共用度高 = 更该进同一个图集) | | 疑似无用图片 | `DrawUnusedImagePage` L722 | `UnusedImageRoots`(L487-491:`gui` + `PSD2UI/UI`)下的 `t:Texture2D` | 既不在 `prefabDependencies`(仅来自 prefabRoot 下 prefab 的递归依赖,L895),也没有静态代码引用(`HasStaticCodeReference` L1148:guid / 全路径 / Resources 相对路径的**字面**子串) | 哪些图可能是老项目遗留的废图 | | 节点统计 | `DrawNodeStatisticsPage` L760 | 每个 prefab 的 `Transform` 总数 | 超过 `GetSuggestedNodeCount` L942 的硬编码值(UI面板 150 / UI组件 50 / 特效 80 / 角色 100 / 通用 80)→ 标"建议关注"或"建议拆分" | 哪些 prefab 层级过深/节点冗余(影响 Rebuild 与合批) | | 大图标记 | `DrawLargeImagePage` L796 | `UnusedImageRoots` 下的 `t:Texture2D` | 源文件 > 500 KB(L484)**或**分辨率 ≥ 1024×1024(L485) | 哪些图源文件太大 | | 相似图 | `DrawSimilarImagePage` L831 | `UnusedImageRoots` 下的 png/jpg/jpeg(L1282) | dHash 64bit(**带 alpha 包围盒裁剪** L1311-1320,再采 8×9 亮度网格 L1323-1333)+ 宽高比差 ≤ 0.05(L1363)+ 汉明距离 ≤ 6(L486);用 8 字节分桶 LSH 避免 O(n²)(`FindSimilarImagePairs` L1346) | 哪些图是重复/近似重复(老项目遗留的典型症状) | | 界面性能 | `UIRuntimePerformanceAudit.DrawEmbedded` L83 | 从 `GameInitWrapper` 源码正则提取 `AddItem`(L176)发现所有注册面板 | 自动巡检:预热 10 帧 + 采样 60 帧(L19-20),输出 打开耗时 / 平均 / P95 / 最大帧耗时 / GC 峰值 / 关闭后残留对象数;手动录制:轮询可见面板,记录实际停留期间的帧耗时 | 哪个面板打开慢、哪个面板停留时拖帧、哪个面板关不干净 | ### 1.2 值得肯定的设计(不要在优化时破坏) 1. **全程只读**:`PrefabUtility.LoadPrefabContents` + `finally UnloadPrefabContents`(L908-916),不改 prefab、不改图集、不写 Undo。 2. **dHash 前先做 alpha 包围盒裁剪**(L1311-1320)——对 UI 图至关重要:UI 图周围大量透明,不裁剪的话所有图都"相似",误报会淹没结果。这一步做得比很多商业工具细。 3. **LSH 分桶代替两两比对**(L1346-1375):1715 张图两两比对是 147 万对,分桶后只比对同桶候选,扫描时间从分钟级降到秒级。 4. **分页 100 条/页 + 筛选 + 可点击 Ping**(`DrawPager` L1403、`DrawAtlasLink` L974):结果多也不会卡 GUI。 5. **CSV 导出带 UTF8 BOM**(L1426、L458):Excel 直接打开不乱码。 6. **性能巡检有"关闭后残留对象"检查**(`FinishClosing` L416-427):能发现"面板关了但子对象还活着",这正是 T5 FullScreenCover 复活问题的同类症状。 --- ## 第二章 它对"降 DC"能帮什么、不能帮什么 ### 2.1 能帮(已在 T5 验证过的用法) - **找图集分散问题**:引用明细页每行都显示图集归属,`FindAtlases` 用 `atlas.CanBindTo(sprite)`(L968)判断,比读 meta 可靠。一个面板里图的图集名越杂,批次越多。 - **找重复图**:相似图页直接指出"这两张图其实是一张",合并后既降包体又降批次。 - **找废图**:疑似无用图片页(但判定弱,见 §3 B1)。 - **量化面板性能**:界面性能页给出 P95 帧耗时,可作为优化前后对比的基线。 ### 2.2 不能帮(4 个关键缺口) 1. **测不出 Draw Call / 批次数**。`PanelResult`(L37-50)只有帧耗时和 GC,**降 DC 的成果无法用这个工具量化**——改完补丁只能靠 Frame Debugger 手工数,做不了"全项目面板 DC 排行"。 2. **看不出"谁在断批"**。工具只扫 `Image` / `RawImage` / `SpriteRenderer` 三种组件(L912-914),**完全没扫 `Mask` / `RectMask2D` / `UIEffect` / `UIEffectReplica` / 嵌套 `Canvas` / TMP 字体资产**——而这些才是断批的真正元凶。 > T5 真实案例:`LevelGiftItem.prefab` 里 1 个 `UIEffect` + 6 个 `UIEffectReplica`(参数全是默认 None、无视觉效果),运行时每条克隆出 7 个独立材质,直接把面板顶到 101 批。**这个工具当时没能报出这个问题**。 3. **不做合批预估**。已经有"每张图属于哪个图集"的数据,却没有按 prefab 聚合出"这个面板涉及几个图集 / 几张散图 / 预估最少批次",无法批量排出"最该优化的面板 TOP N"。 4. **不看纹理导入设置**。大图页只看**源文件大小**和分辨率,但真机成本是**运行时显存**,由 `TextureImporter` 的压缩格式 / maxSize / mipmap / isReadable 决定。一张 1024×1024 的图:RGBA32 未压缩 = 4 MB 显存,ASTC 6×6 = 约 0.5 MB——工具现在看不出这个差别,而 T5 光 png 就有 302 MB / 1715 张。 --- ## 第三章 优化清单(按优先级,含代码位置与改法) ### A 级:能力补齐(对降 DC / 瘦身目标收益最大) #### A1 给性能巡检加 DC / 批次 / SetPass 指标 ★首推 - **现状**:`StartRecorder` L443-448 只起了一个 `ProfilerRecorder`(`ProfilerCategory.Memory` 的 `GC Allocated In Frame`);采样在 `Tick` L384-393。 - **改法**:照 `gcRecorder` 的同一套写法,多起 3 个 Render 类别的 recorder: ```csharp drawCallRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "Draw Calls Count"); batchRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "Batches Count"); setPassRecorder = ProfilerRecorder.StartNew(ProfilerCategory.Render, "SetPass Calls Count"); ``` 在 `Tick` 的 Sampling 分支里按现有风格累积(`Math.Max(..., recorder.LastValue)` 取峰值;再加一个累加/计数得平均值); `PanelResult`(L37)加 `MaxDrawCalls` / `AvgDrawCalls` / `MaxBatches` / `MaxSetPass` 四个字段; `DrawEmbedded` 的结果表头(L140-149)与 `ExportCsv` 的列(L455-457)同步加列;`Finish` L440 处一并 Dispose。 - **备选**:`UnityEditor.UnityStats.drawCalls / batches / setPassCalls` 更简单,但受编辑器自身渲染和 Scene 视图影响更大,建议以 `ProfilerRecorder` 为主。 - **诚实提示(务必写进界面 HelpBox)**:编辑器 Play Mode 测得的 DC 含编辑器渲染开销,**只能用于同环境下的前后对比**,不能当真机绝对值;真机结论仍需 Profiler 连真机复测。这与 `05_排查方法论/DC与帧率排查经验.md` §2.1 的立场一致。 - **收益**:降 DC 从此可量化、可排行、可回归。做完这一项,"改了补丁到底降了多少"不再靠肉眼数 Frame Debugger。 - **工作量**:约 30-40 行,半天内含验证。 #### A2 新增"合批体检"页(静态分析,不需 Play Mode) ★首推 - **现状**:`FindAtlases` L962 已能给出每张图的图集归属,但数据只逐行展示,没有按 prefab 聚合;`ScanPrefab` L906 只遍历三种图形组件。 - **改法**: 1. `ScanPrefab` 里补扫:`Mask` / `RectMask2D` 数量、`UIEffect` / `UIEffectReplica` 数量(用 `GetComponentsInChildren(true)` 可一次拿到两者)、嵌套 `Canvas` 数量(`GetComponentsInChildren(true).Length - 1`)、TMP 文本用到的字体资产 distinct 数; 2. 新增 `BatchHealthRecord`:`PrefabPath`、涉及图集数、散图数(`Atlases == "未加入图集"`)、Mask 数、UIEffect 数、嵌套 Canvas 数、字体种类数、**预估最少批次数**; 3. 预估公式(标注为估算,非实测): `预估批次 ≈ distinct图集数 + 散图数 + distinct字体图集数 + UIEffect配置种类数 + Mask分区数 + 嵌套Canvas数` 4. 新增第 8 个 `ViewTab`,按"预估批次"降序排列,支持导出 CSV。 - **收益**:一次扫描就能列出"全项目最该优化的面板 TOP N"以及每个面板的**具体病因**(是图集分散、还是 UIEffect 断批、还是 Mask 太多)。这正是丢失的 `UIDCProbeTool` 的核心价值,而且**静态就能算、可批量、可导表做优化前后对比**,比原来那个只能在运行时看单个 Canvas 的版本更实用。 - **工作量**:约 150 行,一天内。 - **依赖注意**:引用 `Coffee.UIEffects` 命名空间会让这个 Editor 脚本依赖 UIEffect 包。若担心耦合,可改用 `GetComponentsInChildren` + `GetType().Name` 字符串判断(`"UIEffect"` / `"UIEffectReplica"`),零编译依赖。 #### A3 新增纹理导入设置审计页(服务"源文件瘦身") - **现状**:`ScanLargeImages` L1185 只看 `GetAssetFileSize`(L1215,源文件字节数)和 `image.width/height`。 - **改法**:对每张图取 `AssetImporter.GetAtPath(path) as TextureImporter`,输出并判定: | 检查项 | 判定 | 浪费量级 | | --- | --- | --- | | `mipmapEnabled` | UI 图(`textureType == Sprite`)开 mipmap = 白扔 | 约 +33% 显存 | | `isReadable` | 非必须读取却开着 = 内存里存两份 | ×2 显存 | | `maxTextureSize` | 远大于 prefab 里实际显示尺寸 | 按平方倍 | | 平台 override 格式 | Android/iOS 用 RGBA32 / ARGB32 未压缩 | 4-8 倍于 ASTC | | `npotScale` | 非 2 的幂图被放大到 2 的幂 | 最多 4 倍 | | `sRGBTexture` / `alphaIsTransparency` | 设置与用途不符 | 视觉问题 | 同时给出**预估运行时显存**(宽 × 高 × 每像素字节数,按 format 查表),并与源文件大小并列显示——两者差异常常是数量级的。 - **收益**:T5 有 302 MB png / 1715 张 + 65 MB TGA + 11 MB PSD,瘦身真正的肥肉在导入设置而不是源文件。这一页直接指向"改哪个设置省多少 MB"。 - **工作量**:约 120 行,一天内。 #### A4 扩大扫描范围(配置化) - **现状**:`DefaultPrefabRoot` L482 固定为 `gui/panel`;`UnusedImageRoots` L487-491 硬编码两个目录(`gui` + `PSD2UI/UI`)。T5 的 `Resources_moved` 共 739.6 MB,其中 `effects` 254.5 MB、`res3d` 202.3 MB、`font` 93.8 MB、`animation` 45 MB **全都扫不到**。 - **改法**:把两个常量改成可编辑字段 + `EditorPrefs` 持久化(参考 `UnusedResourceAuditTool.cs` 的做法),并支持多目录(`string[]`);prefabRoot 也支持多个。 - **收益**:从"只能体检 UI 目录"变成"能体检全工程"。 - **工作量**:约 40 行。 --- ### B 级:判定准确性(降低误报/漏报) #### B1 "疑似无用图片"漏判动态引用 ★与另一工具冲突,必须处理 - **现状**:`HasStaticCodeReference` L1148-1158 只做三种**字面**子串匹配:guid、资源全路径、`/Resources/` 之后的相对路径(去扩展名)。`DrawUnusedImagePage` 的 HelpBox(L724-726)已诚实声明"配置表、字符串拼接、反射和运行时下载资源无法可靠识别"。 - **漏判的典型形态**(T5 实测存在): - 配置表驱动:`achievement.json` 里 `"icon":"achievement_icon_1"`——代码和路径里都没有这个名字; - 代码拼接:`Config_Const.Config_path_ui_task + "btn_go"`(T5 有 21 处此类模式); - 插值前缀 + 表数字后缀:`AtlasUtils.cs` 里 `$"rarity_{rarity}"`、`$"img_card{rarity}"`、`$"phase_{phases}"`——资源实际叫 `rarity_1`,语料里永远只有 `rarity_{rarity}`; - Addressables 条目 / Label、场景文件引用、材质与图集 packables 引用。 - **关键冲突**:`Assets/Editor/UnusedResourceAuditTool.cs`(菜单 `Tools/只读资源巡检/未引用资源候选分析`)**已经实现了完整的 5 层动态识别链**(代码字面量 → Resources 拼接 → 语料子串 → 图集/控制器/字体/Spine 配置名 → 插值/拼接前缀+数字后缀),还带 Addressables、场景根、打包可见性分析和三阶段备份流程。两个工具会对同一张图给出**矛盾结论**(这里说"无用",那里说"疑似动态引用")。 - **改法(推荐第 2 种)**: 1. 最小改动:在 `DrawUnusedImagePage` 的 HelpBox 里加一句指引——"完整分析请用 `Tools/只读资源巡检/未引用资源候选分析`",并把该页改名为"疑似无用图片(弱判定,仅供参考)"; 2. 彻底改动:把 `UnusedResourceAuditTool` 里的语料构建与前缀命中逻辑抽成一个共享静态类(如 `DynamicReferenceCorpus`),两个工具都调它,结论自然一致。 - **工作量**:方案 1 约 5 行;方案 2 约 1 天(含抽取与回归)。 #### B2 依赖面太窄导致误报 - **现状**:`prefabDependencies` L586 只在 `Scan` L895 里由 **prefabRoot 下的 prefab** 累积(`AssetDatabase.GetDependencies(path, true)`)。默认 prefabRoot = `gui/panel`。 - **后果**:被**场景文件**、**其他目录的 prefab**(如 `gui/item`、`effects`)、**材质**、**SpriteAtlas 的 packables** 引用的图,都会被判成"疑似无用"。 - **改法**:根集合补上 `t:Scene`、全工程 `t:Prefab`(或至少 `gui` 全域)、`t:Material`、`t:SpriteAtlas`、Addressables 条目——即 `UnusedResourceAuditTool.CollectRoots` 的那套。或者干脆按 B1 方案 2 合并。 #### B3 `warningsOnly` 在 4 个页无效 - **现状**:只有 `Matches`(L1053,引用明细页)和 `RefreshFilteredNodeRecords`(L1174-1176,节点统计页)判了 `warningsOnly`;`RefreshFilteredSharedRecords` L1100、`RefreshFilteredUnusedImages` L1160、`RefreshFilteredLargeImages` L1227、`RefreshFilteredSimilarImagePairs` L1391 都只判 `filter`。 - **后果**:勾上"仅显示需确认项"后,这 4 个页列表毫无变化,也没有任何提示,容易被误认为"这些页没有问题项"。 - **改法**:要么给这些页的 record 补 `NeedsConfirm` 语义并参与过滤,要么在这些页顶部显示一行说明"本页不受'仅显示需确认项'影响"。约 15 行。 --- ### C 级:工具自身性能与测量可信度 #### C1 手动录制每帧全量遍历所有 GameObject ★自测量污染,必须修 - **现状**:`TickManualRecording` L258 每帧调用 `GetVisibleRegisteredPanels` L289,后者执行 `Resources.FindObjectsOfTypeAll()`(L292)——遍历**全工程所有 GameObject**(含资产、prefab 内的对象,不只是场景对象)。 - **后果**:**工具自己在拖慢被测帧**。手动录制测出来的 P95 帧耗时里包含了这次全量遍历的开销,面板越多、工程越大,污染越严重。这是"测量工具影响测量结果"的经典陷阱,会让所有手动录制数据偏高。 - **改法(三选一,按效果排序)**: 1. **最优**:按作者自己在 L301-306 留的 TODO 做——在 `MFrame.PanelMgr` 的 `Show` / `completeHandler` / `Close` 里加生命周期事件(`OnPanelShowRequested` / `OnPanelShown` / `OnPanelClosed`),工具订阅事件。既零轮询开销,又能拿到真实的"请求 Show → 可见"耗时,不再是一帧近似值; 2. **次优**:轮询降频——加一个 `manualPollIntervalSeconds`(如 0.1 s),未到间隔就跳过本轮 `GetVisibleRegisteredPanels`;帧耗时采样仍每帧进行,不受影响; 3. **最省事**:把 `Resources.FindObjectsOfTypeAll()` 换成 `Object.FindObjectsByType(FindObjectsInactive.Exclude, FindObjectsSortMode.None)`(只返回场景对象,且跳过排序),并预先把 `Entries` 的 prefab 名做成查找表。 - **工作量**:方案 2 约 8 行;方案 3 约 5 行;方案 1 约半天(要动框架代码,需谨慎,属于"改 UI 框架"范畴,动手前应单独确认)。 #### C2 代码语料是 O(n×m) 全文扫描 - **现状**:`ReadAllProjectCode` L1136 把 `Assets` 下**所有 .cs** 拼成一个大字符串(T5 有 4659 个文件,语料是 MB 级);`HasStaticCodeReference` L1150-1157 对每张图做 **3 次** `string.IndexOf` 全文搜索。1715 张图 = 5145 次 MB 级全文扫描。 - **改法**:一次性用正则从语料里提取所有字符串字面量和标识符 token,塞进 `HashSet`;再对每个资源名/路径做 O(1) 查表 + 前缀匹配。`UnusedResourceAuditTool.BuildCodeCorpus` 已经是这个做法,可直接复用。 - **收益**:扫描时间从"十几秒到几十秒"降到"1-2 秒"。 #### C3 dHash 对全尺寸图取像素 - **现状**:`TryGetDifferenceHash` L1310 `image.GetPixels32()` 取**整张解码图**的像素,再双重循环遍历每个像素找 alpha 包围盒(L1312-1319)。 - **后果**:一张 4096×4096 的图 = 64 MB 托管内存 + 1677 万次循环。相似图扫描在含大图的目录上会明显卡顿甚至触发 GC 尖峰。 - **改法**:`ImageConversion.LoadImage` 解码后,先 `Graphics.Blit` 到一张 128×128 的 `RenderTexture` 再 `ReadPixels` 回小 `Texture2D`,在小图上找包围盒并采样。dHash 最终只取 8×9 = 72 个点(L1323-1333),128² 的精度完全够,而内存从 64 MB 降到 64 KB。 - **注意**:包围盒精度会略降(小图上 alpha≤10 的判定 L1316 会因缩放插值而边缘模糊)。稳妥做法是**双阶段**:先在 128² 上算粗略包围盒,再按该包围盒比例回到原图取那 72 个采样点(只取 72 个像素,不再全图遍历)。 - **工作量**:约 30 行。 #### C4 逐 prefab 递归依赖 - **现状**:`Scan` L890-896 对每个 prefab 调 `AssetDatabase.GetDependencies(path, true)`(递归全依赖)。566 个 prefab = 566 次递归依赖解析。 - **改法**:先收集全部 prefab 路径,一次 `AssetDatabase.GetDependencies(string[] pathNames, bool recursive)` 批量调用(Unity 支持数组重载);或改为 `GetDependencies(path, false)` 只取直接依赖 + 自行按 `prefabDependencies` 传递闭包。 - **收益**:扫描时间下降(幅度取决于依赖深度)。 --- ### D 级:UI / 一致性小问题 | 编号 | 现状(代码位置) | 问题 | 改法 | | --- | --- | --- | --- | | D1 | `ExportCsv` L1419-1428 | 导出按钮(L624)在任何标签页都可见,但**永远只导引用明细**(`records`)。站在"大图标记"页点导出,拿到的却是引用明细表 | 按 `activeTab` 分派导出内容,或把导出按钮移进各页;至少在非引用明细页禁用并提示 | | D2 | `GetSuggestedNodeCount` L942-952 | 节点数建议值全部硬编码(150/50/80/100/80),不同项目的 UI 复杂度差异很大 | 提到常量区并允许界面覆盖 + `EditorPrefs` 持久化;或按本工程实际分布给分位数建议(如 P90) | | D3 | 两个 MenuItem L599 / L607 | 两个菜单打开的是**同一个** `UIPrefabCheck` 窗口,只是 `activeTab` 不同。从"界面性能巡检"进去后点"扫描",会切到静态页,容易迷失 | 用 `EditorPrefs` 记住上次所在页;或在标题上反映当前页 | | D4 | `DrawUnusedImagePage` L732 | "扫描疑似无用图片"按钮每次都重跑 `ReadAllProjectCode`(见 C2),连点两次就是两次全量读盘 | 语料缓存 + 失效判断(按 .cs 文件最后修改时间) | | D5 | `Record.Warning` L959 | 唯一的告警是"非标准 UI 资源目录",判据是路径前缀(`IsStandardUiPath` L1431)——这是**目录约定**告警,不是性能告警 | 配合 A2 增加真正的合批告警(散图、跨图集、UIEffect 断批) | --- ## 第四章 如果只做三件事 按"收益 / 工作量"比排序: | 优先级 | 项目 | 收益 | 工作量 | | --- | --- | --- | --- | | 1 | **A1** 性能巡检加 DC / 批次 / SetPass 指标 | 降 DC 成果从"肉眼数 Frame Debugger"变成"可量化、可排行、可回归" | 30-40 行 | | 2 | **A2** 新增静态"合批体检"页 | 一次扫描列出全项目最该优化的面板 TOP N + 具体病因(图集分散 / UIEffect / Mask / 嵌套 Canvas) | 约 150 行 | | 3 | **C1** 修手动录制的每帧全量遍历(方案 2 或 3) | 消除工具对被测帧的污染,让手动录制数据可信 | 5-8 行 | 其次:**B1**(避免与 `UnusedResourceAuditTool` 结论打架)、**A3**(导入设置审计,服务包体瘦身)、**C2/C3**(扫描提速)。 --- ## 第五章 跨项目可移植性(归档前必须知道) 这个工具**不是通用工具**,有框架绑定: | 部分 | 依赖 | 能否直接搬到别的项目 | | --- | --- | --- | | 6 个静态扫描页(引用明细/共用/无用图/节点/大图/相似图) | 只用 `UnityEditor` + `UnityEngine.UI` + `UnityEngine.U2D`;唯一框架引用是文件头 `using MFrame;`(L10) | **可以**,删掉 `using MFrame;` 后基本即用(需按项目改 `DefaultPrefabRoot` L482 与 `UnusedImageRoots` L487) | | 界面性能页(自动巡检) | `PanelMgr.Instance.Show/Close`(L366、L410)+ 从 `GameInitWrapper` 源码正则解析 `AddItem`(L172-176) | **不能**,必须改成目标项目的面板管理器 API 与注册方式 | | 界面性能页(手动录制) | `ManualPanelNameLookup` 由 `DiscoverPanels` 建立,同样依赖上面两项 | **不能**,同上 | 移植时的最小改动清单: 1. 删 `using MFrame;`(L10),确认剩余部分能编译; 2. 改 `DefaultPrefabRoot`(L482)与 `UnusedImageRoots`(L487-491)为目标项目的 UI 目录; 3. `IsStandardUiPath`(L1431)里的两个路径前缀同步改; 4. 界面性能页:替换 `DiscoverPanels`(L167)的面板发现方式与 `PanelMgr.Instance.Show/Close` 调用(L366 / L410 / L432),或在不需要时整块删掉 `UIRuntimePerformanceAudit`(L17-478)与 `ViewTab.界面性能`(L567)及其分派(L644-647)。 --- ## 第六章 作者留下的 TODO(原文摘录,L301-306) 工具里已有一段明确的改进计划,做 C1 方案 1 时直接照它执行: ``` // TODO(精确手动录制耗时):在 MFrame.PanelMgr 中增加 UI 生命周期事件。 // 1. 在 Show(name, data, completeHandler) 入口记录 startTime,并触发 OnPanelShowRequested(name, startTime)。 // 2. 在资源加载完成且 PanelBase._OnShow 执行完成后的 completeHandler 中,触发 OnPanelShown(name, startTime, elapsedMs)。 // 3. 在 Close(name) 成功后触发 OnPanelClosed(name)。 // 4. 本工具订阅上述事件,以 name 关联会话,便可得到真实的"请求 Show 到可见"耗时, // 不再依赖当前轮询 GameObject 激活状态所得的一帧近似值。 ``` > 注意:这一项要动 `MFrame` 框架代码(`PanelMgr` / `PanelBase`)。T5 有过教训——**改 UI 框架前必须确认现有全屏遮挡机制(FullScreenCover)不被破坏**,且改动要能被业务面板无感继承。动手前单独确认,不要顺手做。