如何可视化查看Bundle的循环依赖

解读

在国内 Unity 项目里,AssetBundle(简称 Bundle)循环依赖是面试高频“坑”。
面试官真正想确认的是:

  1. 你是否理解 Bundle 级依赖资源级依赖 的区别;
  2. 能否用低成本、可落地的可视化方案把环暴露出来,而不是只停留在“理论上应该解耦”;
  3. 是否具备自动化监控意识,能在打包流水线里提前报警,而不是上线后靠用户反馈崩溃。

因此,回答必须给出编辑器内可视化+CI 流水线可视化两条路线,并指出如何定位环的入口资源

知识点

  • Bundle 级循环依赖:A.bundle 依赖 B.bundle,B.bundle 又依赖 A.bundle,导致运行时无法单独加载任一 Bundle。
  • Unity 内置依赖数据AssetBundleManifest.GetDirectDependencies() 只到 Bundle 层,不到资源层
  • 资源级依赖:需用 AssetDatabase.GetDependencies()UnityEditor.Build.Content.ContentBuildInterface 拿到。
  • 可视化核心:把依赖图转成有向图,用拓扑排序检测环,再用Graphviz/Unity Editor 窗口/CI 网页渲染。
  • 官方工具链:Unity 2021 LTS 起提供的 Asset Dependency Graph 包(com.unity.assetdependencygraph)可直接生成 DOT 文件。
  • 热更新影响:循环依赖会导致首包资源冗余IL2CPP 裁剪误判Addressables 增量打包失效,面试时主动提及可加分。

答案

我采用“编辑器快速定位 + 流水线自动报警”两步走,全程零人工翻包。

  1. 编辑器内可视化(当天就能用
    a. 引入官方 Asset Dependency Graph 包,写一条 Editor 菜单:

    [MenuItem("Tools/Bundle/可视化循环依赖")]
    static void ExportBundleCycle()
    {
        var manifest = AssetBundleManifest.LoadFromFile("Assets/StreamingAssets/StandaloneWindows64");
        var bundleNames = manifest.GetAllAssetBundles();
        var edges = new List<(string,string)>();
        foreach(var b in bundleNames)
            foreach(var dep in manifest.GetDirectDependencies(b))
                edges.Add((b, dep));
    
        var cycles = TarjanDetectCycle(edges);   // 自己封装的强连通分量算法
        File.WriteAllText("BundleCycle.dot", ToDot(cycles));
        Process.Start("dot.exe", "-Tpng BundleCycle.dot -o BundleCycle.png");
        EditorUtility.RevealInFinder("BundleCycle.png");
    }
    

    b. 生成的 PNG 直接用 红色高亮环,双击节点可跳转到对应 Bundle 的 .manifest 文件,10 秒内定位入口资源

  2. 流水线自动报警(防止回潮
    在 Jenkins 的 AssetBundle 打包 Stage 后追加一步:

    • com.unity.assetdependencygraph 导出 bundleDependency.dot
    • 调用 graphviztred(传递归约)工具,如果输出行数与输入行数不一致,说明存在环;
    • 若检测到环,直接中断打包,把环的节点列表以 Markdown 表格形式推送到企业微信机器人,@责任人
  3. 解环策略(回答里必须提一句
    把公共 Shader/Texture 拆成 Shared.bundle,用 Addressables Label 做预加载;
    对代码层环,采用 接口层抽象 + Assembly Definition 拆分,不让脚本引用打到同一个 Bundle

拓展思考

  • 资源级循环依赖比 Bundle 级更难察觉:例如 A.prefab 引用 B.mat,B.mat 又引用 A.prefab 上的 Texture。此时需要把 ContentBuildInterface.CalculatePlayerDependencies 结果也写进 DOT,双层图对比才能彻底干净。
  • 大型项目 Bundle 数量过万,Graphviz 直接渲染会炸内存,可改用 Unity 的 GraphView 扩展做增量展开,或输出 JSON 给前端 D3.js 做交互式查看。
  • 如果项目已用 Addressables,可开启 Build Layout(菜单 Addressables→Analyze→Generate Build Layout),它输出的 buildlayout.txt 自带 Bundle 依赖段,用 Python 脚本解析后一样能画环,无需重复打整包
  • 面试反向提问:可以问面试官“贵公司 Bundle 量级多少?是否允许运行时动态挂载 Bundle?”——既展示深度,又能套取实际工程信息,为后续谈薪做铺垫。