Unity5新的AssetBundle系统使用心得

来源:互联网 发布:域名备案代办 编辑:程序博客网 时间:2024/04/28 21:50
  Unity的AssetBundle系统是对资源管理的一个扩展,动态更新,网页游戏,资源下载都是基于AssetBundle系统的。但是不得不说,这个系统非常恶心,坑很深。至于有多深,请看这篇文章: http://www.cnblogs.com/ybgame/p/3973177.html

        原先的AssetBundle系统需要自己写一大坨导出的代码(BuildPipeline),这个新手会无从下手,老手也经常会被坑到。想正确处理好资源的依赖关系从而保证资源完整而又不会产生重复资源,确实不是一件非常容易的事情。       

        Unity5新的AssetBundle系统大大简化了这一操作。Unity打包的时候会自动处理依赖关系,并生成一个.manifest文件,这个文件描述了assetbundle包大小、crc验证、包之间的依赖关系等等,是一个文本文件。加载资源的时候Unity会自动处理好其依赖包的加载。(参见最后的补充,Unity并没有如此智能,新的assetbundle简化很多,但是也有一些新的坑)

        打包代码简化为一个函数(其实也没什么必要了,因为流程固定了,做成内嵌的菜单选项也没什么影响)

查看文本打印
  1. BuildPipeline.BuildAssetBundles(outputPath);  

       执行这个函数,它会自动打包工程目录下的所有的assetbundle,函数足够智能,它只会打包有修改的资源。

       如何添加一个AssetBundle呢?

       很简单,在资源属性窗口底部有一个选项,这个地方设置AssetBundle的名字。它会修改资源对应的.meta文件,记录这个名字。 AssetBundle的名字固定为小写。另外,每个AssetBundle都可以设置一个Variant,其实就是一个后缀,实际AssetBundle的名字会添加这个后缀。如果有不同分辨率的同名资源,可以使用这个来做区分。

      



      我手头的模型资源非常多,所以我又写了个脚本自动遍历prefab的meta文件,添加AssetBundle名字。有一个需要注意的地方就是.meta文件貌似权限问题,无法直接写入,需要删除原文件,然后使用新的文件替换。。

查看文本打印
  1. # -*- coding: utf-8 -*-  
  2. import os, sys, shutil;  
  3.   
  4. EXT_LIST = ['.prefab.meta''.png.meta''.jpg.meta'];  
  5.   
  6. def doWork(path):  
  7.     for root, dirs, files in os.walk(path):  
  8.         for file in files:  
  9.             for ext in EXT_LIST:  
  10.                 if file.endswith(ext):  
  11.                     fullPath = os.path.join(root, file)  
  12.                     fullPath = fullPath.replace('\\', '/')  
  13.                     prefabName = fullPath.replace(path, '');  
  14.                     prefabName = prefabName[:prefabName.find('.')] + '.data';  
  15.                       
  16.                     fileData = [];  
  17.                     fp = open(fullPath, 'r');  
  18.                     for line in fp:  
  19.                         if line.find('assetBundleName:') != -1:  
  20.                             fileData.append('  assetBundleName: ' + prefabName.lower() + '\n');  
  21.                         else:  
  22.                             fileData.append(line);  
  23.                     fp.close();  
  24.                     # os.remove(fullPath);  
  25.                     # return;  
  26.                     fpw = open(fullPath + '.tmp''w');  
  27.                     fpw.writelines(fileData);  
  28.                     fpw.close();  
  29.                     os.remove(fullPath)  
  30.                     shutil.copy(fullPath + '.tmp', fullPath);  
  31.                     os.remove(fullPath + '.tmp')  
  32.                     break;  
  33.   
  34.   
  35. doWork(r'Assets/Resources/Prefab/')  
  36. os.system('PAUSE')  

c#编辑器扩展(与python代码功能一样,喜欢哪个用哪个)

查看文本打印
  1. public class ExportAssetBundles : Editor  
  2. {  
  3.     // 设置assetbundle的名字(修改meta文件)  
  4.     [MenuItem("Tools/SetAssetBundleName")]  
  5.     static void OnSetAssetBundleName()  
  6.     {  
  7.   
  8.         UnityEngine.Object obj = Selection.activeObject;  
  9.         string path = AssetDatabase.GetAssetPath(Selection.activeObject);  
  10.   
  11.         string[] extList = new string[] { ".prefab.meta"".png.meta"".jpg.meta" , ".tga.meta" };  
  12.         EditorUtil.Walk(path, extList, DoSetAssetBundleName);  
  13.   
  14.         //刷新编辑器  
  15.         AssetDatabase.Refresh();  
  16.         Debug.Log("AssetBundleName修改完毕");  
  17.     }  
  18.   
  19.     static void DoSetAssetBundleName(string path)  
  20.     {  
  21.         path = path.Replace("\\", "/");  
  22.         int index = path.IndexOf(EditorConfig.PREFAB_PATH);  
  23.         string relativePath = path.Substring(path.IndexOf(EditorConfig.PREFAB_PATH) + EditorConfig.PREFAB_PATH.Length);  
  24.         string prefabName = relativePath.Substring(0, relativePath.IndexOf('.')) + EditorConfig.ASSETBUNDLE;  
  25.         StreamReader fs = new StreamReader(path);  
  26.         List<string> ret = new List<string>();  
  27.         string line;  
  28.         while((line = fs.ReadLine()) != null) {  
  29.             line = line.Replace("\n""");  
  30.             if (line.IndexOf("assetBundleName:") != -1) {  
  31.                 line = "  assetBundleName: " + prefabName.ToLower();  
  32.   
  33.             }  
  34.             ret.Add(line);  
  35.         }  
  36.         fs.Close();  
  37.   
  38.         File.Delete(path);  
  39.   
  40.         StreamWriter writer = new StreamWriter(path + ".tmp");  
  41.         foreach (var each in ret) {  
  42.             writer.WriteLine(each);  
  43.         }  
  44.         writer.Close();  
  45.   
  46.         File.Copy(path + ".tmp", path);  
  47.         File.Delete(path + ".tmp");  
  48.     }  
  49.   
  50.     [MenuItem("Tools/CreateAssetBundle")]  
  51.     static void OnCreateAssetBundle()  
  52.     {  
  53.         BuildPipeline.BuildAssetBundles(EditorConfig.OUTPUT_PATH);  
  54.   
  55.         //刷新编辑器  
  56.         AssetDatabase.Refresh();  
  57.         Debug.Log("AssetBundle打包完毕");  
  58.     }  
  59. }  


补充:

        之前我简单的认为Unity既然已经做了资源依赖链,那么加载资源的时候理所应当的会自动处理好依赖关系。结果是我想的太简单了。我们必须自己手动加载依赖的assetbundle。

        我们直接调用打包函数,在打包目录会生成一个与打包目录同名的文件,比如我们打包到AssetsBundle/windows目录下,则此目录下会生成一个windows文件,以及一个windows.manifest文件。

        这个windows文件就包含了当前assetbundle的列表和依赖关系。我们需要在游戏开始的时候手动加载这个assetbundle(即windows),获取一个AssetBundleManifest对象。这个对象有GetAllAsssetBundles()函数还有一个GetAllDependencies函数。

        我们可以使用GetAllDependencies传入一个assetbundle的名字来获取其依赖的assetbundle,然后分别加载这些assetbundle。注意传入的文件名是我们生成assetbundle设置的名字,而不是全路径。可以参考windows.manifest文件查看assetbundle名字形式。

        新的问题:

       由于我的资源量比较大,所以我拆分了多个生成工程。而生成assetbundle的时候指向的是一个路径,最终合并后就是一个完整的游戏资源包。我们在游戏运行加载资源的时候需要windows这个manifest文件,而它又是Unity自己生成的,并且只保存了当前项目生成的assetbundle信息,无法自动合并。从这点来说它无法满足我的需求。

       我们之所以需要这个manifest文件,是因为我们要从中获取依赖关系。所以我在项目中自己维护资源列表。它可以根据生成的manifest自动生成、合并资源列表(一个json格式的文本文件)。我们在游戏开始时解析这个json文件获取到资源列表。这个资源列表同时也可以作为自动更新的文件列表。

      实现方式很简单,assetbundle打包完毕后,加载manifest文件(注意这里有个坑,我会在另一篇文章里面讲解如何在Editor下加载AssetBundle),获取所有资源信息,与当前已存在的资源列表进行比对,合并。多个项目之间只要维护好一份统一的资源列表就可以了。


补充2:

       1、似乎没有必要给所有的模型创建prefab来使用,我们可以直接把fbx打进AssetBundle,然后实例化这个fbx就可以得到正确的GameObject。这个在我的地图文件里面非常必要,因为地图文件很小很碎很多,如果非要创建prefab的话,可能prefab都要占几十兆的空间。而在使用过程中,理论上我不会对这种模型做特殊的修改,仅仅是当做资源来使用。  其实人物等模型也是如此,如果仅仅当做资源来使用的话,是没有必要创建prefab的。不过如果要绑定一些脚本(理论上也可以通过代码来搞定),设置物理参数就需要一个prefab来保存这些信息了。

       2、不可以给一个文件夹设置assetbundle名字,会报错。  把文件夹下面的所有文件设置成一样的名字,就可以把这些资源打包到一个assetbundle里面。

       3、所有可能共用的资源(比如公共纹理,尤其是shader)一定要设置assetbundle的名字,这样这些会单独打包,并且其他使用这些资源的assetbundle会自动依赖这些包。如果shader不设置打包的话,则每个assetbundle里面都有一份shader,这样就浪费了很多空间,并且当修改shader内容的时候,所有的包都需要重新制作。

       4、加载完assetbundle,然后使用LoadAsset加载资源。传入的名字要么是以Assets/开头的全路径,要么只有一个文件名。其他形式的名字无法正确匹配到对应资源。如果只有一个文件名,要保证这个assetbundle内不包含同名,同类型的文件,否则会返回第一个查找到的资源。  名字大小写无关

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 两岁宝宝入睡难怎么办 吃激素掉发严重怎么办 20多岁掉头发怎么办 最近头发老掉怎么办20 20多岁最近严重脱发怎么办 头痒头皮屑多掉头发怎么办 有头屑头痒掉头发怎么办 老是头痒掉头发怎么办 头屑头痒掉头发怎么办 头痒头屑多掉头发怎么办 洗头发时总是掉发怎么办 染头发染到脸上洗不掉怎么办 总爱掉头发怎么办20岁 头发开叉长的慢怎么办 洗头时掉很多头发怎么办 出门前头发很油怎么办 关于头发头顶头发少怎么办 每天掉100根头发怎么办 小孩嘴巴烂了该怎么办 小孩吃烧烤嘴巴肿好大该怎么办 脸特别瘦的人怎么办 怀孕宝宝太懒了怎么办 奶油胶手机壳脏了怎么办 为什么电脑桌面壁纸变成黑色怎么办 仓鼠妈妈老忘了宝宝怎么办 木工三排多轴钻床气缸坏了怎么办? 水溶笔洗不掉怎么办 针管笔没有墨了怎么办 二岁小儿不爱吃饭怎么办 狗狗一直挠痒痒怎么办 手被铅笔扎了怎么办 小孩智力轻度低下该怎么办 发侮辱人的信息怎么办 宋民国表情我能怎么办 苹果手机微信弄成听筒模式怎么办 我的小可爱丢了怎么办 法斗犬老是皮肤病看也看不好怎么办 项链水晶不亮了怎么办 美甲画画手抖怎么办 微信中的表情符号不全怎么办 球球大作战找不到团战服务器怎么办