AssetBundle依赖查找工具

发表于2017-03-20
评论1 4.5k浏览
AssetBundle依赖查找工具
     Unity有资源打包,利用AssetBundle,可以将几乎所有的资源都打包封装,只需要提前扫描所有要打包的资源,然后用AssetDatabase.GetDependencies获得所有的依赖,下面就给大家介绍下Unity中的AssetBundle。
       AssetBundle依赖查找工具
      动因
      总揽
      原理
      实现
      控件
      AssetBundleName
       特例
      Builtin资源
      默认材质
      Sprite Atlas
      Shader打包

动因
      我们的项目使用Unity5.3.3版本。
      在Unity5中,AssetBundle的打包和依赖管理已经简单了很多,我们只需要

AssetBundle依赖查找工具

      AssetBundle依赖查找工具
      给资源设置一个AssetBundleName,Unity就会自动完成依赖处理。
      但是,有一个很大的问题是,Unity并没有提供一个编辑器窗口来查看每个AssetBundle中到底打包了什么样的资源,以及,是否有资源被打包到了多个AssetBundle中,即发生重复打包,产生了资源冗余
      为了解决上面提到的问题,我们开发了一个编辑器插件,来查看AssetBundle的详细信息

总揽
      拥有两个tab
      AssetBundle列表:

AssetBundle依赖查找工具

重复打包的资源的列表:

AssetBundle依赖查找工具

直接资源就是指选择了AssetBundleName的资源

原理
      A GUIDE TO ASSETBUNDLES ANDRESOURCES
      在这个系列文章中,Unity解释了很多AssetBundle和Resources的底层技术细节,其中,提到
      In Unity 5, Object dependencies are tracked via the AssetDatabase API
      By combining the AssetDatabase and AssetImporter APIs, it is possible to write an Editor script that ensures that all of an AssetBundle's direct or indirect dependencies are assigned to AssetBundles, or that no two AssetBundles share dependencies that have not been assigned to an AssetBundle.
在我们的的插件中,正式使用了AssetDatabase和AssetImporter来分析整个依赖关系,进而保证分析的结果和实际打包出的AssetBundle保持一致

实现

AssetBundle依赖查找工具

控件
     本插件实现了ListViewDrawer和RefTreeDrawer来简化UI的绘制和管理,实现数据驱动
public static void DrawFoldoutList(
ref bool foldout,
GUIContent title, List dataList,
Action drawFunc,
Dictionary menuItems=null,
Action selectFunc=null)
{
}
AssetBundleName
     只有在指定了AssetBundleName后,AssetDatabase才能正确的分析整个依赖关系,但是,要手动设置AssetBundleName显然不是一个好的解决方案

AssetBundle依赖查找工具
   
     我们使用Json文件来给出命名规则,使用Unity编辑器脚本来解析这个规则并自动设置AssetBundleName
{
    "total_dir":[
        {"dir":"Assets/Resources/UI/Texture", "prefix":"ui", "suffix":""},
        {"dir":"Assets/Resources/UI/Prefab", "prefix":"ui", "suffix":""},
        {"dir":"Assets/Resources/UI/Atlas", "prefix":"ui",  "name":"atlas", "suffix":""},
        {"dir":"Assets/Resources/UI/Atlas/Common/Icon", "prefix":"ui_atlas", "suffix":""},
        {"dir":"Assets/Resources/SkillData", "prefix":"", "name":"config", "suffix":""},
        {"dir":"Assets/Resources/Table", "prefix":"", "name":"config", "suffix":""},
        {"dir":"Assets/Resources/Effects", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/CmtInfo", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/Skill", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/Animation", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/Actors", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/DynamicShader", "prefix":"", "suffix":""},
        {"dir":"Assets/StaticShaders", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/ScenePrefab", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/Map", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources/Modles", "prefix":"", "suffix":""},
        {"dir":"Assets/Resources", "prefix":"", "name":"other", "suffix":""},
    ],
    "child_files":[
        {"dir":"Assets/Scene", "prefix":"", "type":"t:Scene", "suffix":"", "loadmode":"dynamic"},
    ],
    "child_directory":[ 
    ], 
    "special_assets":[
    ],
    "static_dir":[
        {"dir":"Assets/Scripts"}
    ],
}
total_dir: 整个目录打包成AssetBundle
child_files: 目录下的指定文件被打包成独立的AssetBundle
child_directory: 子目录打包成独立的AssetBundle
special_assets: 特殊的文件,直接指定

特例
      Builtin资源
      需要注意的是,使用AssetDatabase的API,并不能获取到对Builtin资源的依赖关系,但是Builtin资源还是会被打包进AssetBundle的

默认材质
      一种典型的Builtin资源是默认材质:

AssetBundle依赖查找工具

       如果我们打开了modelImport.importMaterials=false这个导入选项,Unity会给模型指定一个默认的材质,这个材质并不能在我们的工程目录下看到
       在这样的情况下,如果我们对每个模型指定一个AssetBundleName,那么在打包AssetBundle时,每个包都会打包一个Default-Diffuse.mat和Default-Diffuse.mat依赖的Standard Shader,这两个依赖已经要占用100K的大小了,影响还是很大的
      幸运的是,Unity的AssetPostprocessor提供了OnAssignMaterialModel接口,供我们自定义默认的模型材质
Material OnAssignMaterialModel(Material mat, Renderer renderer)
        {
            if (mat.name != "Default-Diffuse" && mat.shader.name != "Standard")
            {
                return null;
            } 
            var newMat = AssetDatabase.LoadAssetAtPath("Assets/DefaultMaterials/Default-Diffuse.mat");
            return newMat;
        }
     通过指定自定义的默认材质,并且把这个默认材质单独打包,Unity就可以自动建立AssetBundle的依赖关系,完美解决这个问题

Sprite Atlas
      在UGui中,Atlas是被Unity自动拼合的,这很方便,但带来一个很大的问题就是,这个自动拼合的Atlas会被重复打包的问题
      在Unity 5.2.2p4, 5.3和更新的版本中,Unity自动做了点工作
Any automatically-generated sprite atlas will be assigned to the AssetBundle containing the Sprite Objects from which the sprite atlas was generated. If the sprite Objects are assigned to multiple AssetBundles, then the sprite atlas will not be assigned to an AssetBundle and will be duplicated. If the Sprite Objects are not assigned to an AssetBundle, then the sprite atlas will also not be assigned to an AssetBundle.
     但是,还是很难完全避免重复打包的问题,还是要小心处理,甚至使用TexturePacker来手动打包Atlas,放弃Unity提供的这一便利

Shader打包
     在日常的开发中,我们通常会使用
Shader.Find("Transparent/Diffuse")
     来直接查找到Shader来使用,但是,如果Shader被打包到AssetBundle中,这个函数会报错的
     Unity提供了一个解决方案是AlwaysIncluded Shaders:

AssetBundle依赖查找工具

      当Shader被添加在Always Included Shaders中时,依赖这个Shader的资源在打包时,并不会包含真正的Shader代码,而是包含了一个引用
      在检查插件中,我们也要注意处理这个情况

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引