AssetBundle_介绍 (基于unity 2017 版本 --- 如要知晓最新资料建议去看官方文档)
(最近看到的一篇AB不错的文章:https://blog.csdn.net/Mars___Z/article/details/90199004。简明扼要)
一: AssetBundle的定义和作用
用处?
1,AssetBundle是一个压缩包包含模型、贴图、预制体、声音、甚至是整个场景、可以在游戏运行时被加载。
2,AssetBundle自身保存着相互依赖的关系;---(AB包中后缀为manifest的文件夹中可被文本形式打开)
3,压缩包可以使用LZMA和LZ4压缩算法,减少包大小,更快的进行网络传输;
4,把一些可以下载的内容放在AssetBundle里面,可以减少安装包大小。---(便于游戏体验和更便于实时加载问题)
二: 什么是AssetBundle
可以归为两点
1,他是一个存在于硬盘上的文件。可以称之为压缩包,这个压缩包可以被认为是一个文件夹,里面包含了多个文件,
这些文件可以分为两类:Serializad file(序列化文件或流文件)和Resource file (源文件)。
a) Serializad file 资源是被打碎放在一个对象中,最后统一写进一个单独的文件(只有一个 类似Instance);
b) Resource file 某些二进制文件资源(如:图片 、声音等)被单独保存,方便快速的加载。
2,他是一个AssetBundle对象,我们可以通过代码从一个特定的压缩包加载出来的对象,这个对象包含了所有我们当初添加到这个文件压缩包里面的内容,我们可以通过这个对象加载出来使用。
三: AssetBundle 概念图
资源打包流程
资源使用流程
四 :AssetBundle 使用流程(简称AB)---“重要”
(基本设置)指定资源的AssetBundle属性 (xxxa/xxx)这里xxxa会生成目录,名字为xxx ;
如图:
选择一个需要打包的资源。
关于AB打包的面板和设置方法(图片内有说明)
//-----------------------------------------------------------------------------------------------------
A_使用编译器扩展---直接Build所有以及设置好要打包成AssetBundle的资料
当设置好后可以使用这个编译器扩展进行一次性全部打包模式
using UnityEngine; using UnityEditor; using System.IO; public class CreateAssetBundles : MonoBehaviour { [MenuItem("AssetBundles/Build AssetBundles")] //特性 static void BuildAssetBundle() { string dir = "AssetBundles"; //相对路径 if(!Directory.Exists(dir)) //判断是否存在 { Directory.CreateDirectory(dir); } BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); } }
//-----------------------------------------------------------------------------------------------------
B_AssetBundle包_结构。
当设置好面板属性后,直接使用编译器(AssetBundle/Build AssetBundle)按键进行生成文件。创建后的样式
---本地项目文件夹下创建AB包文件夹
文件夹下的文件格式-----采取的是归类方式 (即 scene/map 这种形式)
最后AssetBundle的文件格式 ,因后缀没添加则只有文件名(注意manifest后缀的文件为管理文件,用于管理记录,为打包后自动生成的)
//-----------------------------------------------------------------------------------------------------
C_加载AB包和包里面的资源。----(需要注意的是带Async的API即是异步的方法不带则是同步方式)
首先介绍一下主要加载AB包的四种方式(即四种类型的API) :
1, 从文件中加载 AssetBundle.LoadFromFile 。
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //这里需要注意的是,如果你想加载的资源有依赖的资源 如贴图等资源,则必须先加载依赖资源后加载你所想加载的资源。 10 //即A依赖B 则必须先加载B到内存中,然后再加载A 并实例化---这里注意依赖项一般可以不用实例化只需要在内存中即可。 11 AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 AssetBundle ab = AssetBundle.LoadFromFile("AssetBundles/scene/map_001"); 14 15 16 17 18 //这里需要注意的是 单独加载和加载所有压缩包内的对象处理方式 19 20 //加载单独的 21 GameObject panelPrefab = ab.LoadAsset<GameObject>("Plane"); //注意名字不能写错了 22 23 Instantiate(panelPrefab);//实例化资源 24 25 ////加载所有 26 //Object[] panelPrefab = ab.LoadAllAssets(); 27 28 //foreach (var obj in panelPrefab) 29 //{ 30 // Instantiate(obj);//实例化资源 31 //} 32 33 yield return null; 34 } 35 }
2、从内存中加载 AssetBundle.LoadFromMemoryAsync(注意是异步,去掉Asyns既是同步方法)
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //这里需要注意的是,如果你想加载的资源有依赖的资源 如贴图等资源,则必须先加载依赖资源后加载你所想加载的资源。 10 //即A依赖B 则必须先加载B到内存中,然后再加载A 并实例化---这里注意依赖项一般可以不用实例化只需要在内存中即可。 11 AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 14 string path = "AssetBundles/scene/map_01"; 15 16 //LoadFromMemoryAsync 和 LoadFromMemory --内存读取 17 AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); //异步的方式 18 yield return request; //异步的方式 必须等待完成后才继续执行 19 AssetBundle ab = request.assetBundle; 20 21 //AssetBundle ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));//同步的方式 22 23 //使用资源 24 GameObject gameObj = ab.LoadAsset<GameObject>("map_001"); 25 Instantiate(gameObj); 26 27 yield return null; 28 } 29 }
3、第三种加载AB包的方式WWW(万维网) --WWW(万维网)对象方法可以从本地读取,也可以从服务器读取。
---需要注意的是这个方法在2017以后基本被UnityWebRequest(这个方法代替的是WWW远程读取)代替。做了一定程度的封装。
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //这里需要注意的是,如果你想加载的资源有依赖的资源 如贴图等资源,则必须先加载依赖资源后加载你所想加载的资源。 10 //即A依赖B 则必须先加载B到内存中,然后再加载A 并实例化---这里注意依赖项一般可以不用实例化只需要在内存中即可。 11 //AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 //注意!!! 14 //www.LoadFromCacheOrDownload 这个方法是先下载存到cache的缓存空间内 然后在进行提取。 15 //如果第二次也进行下载一样的 则会在这个缓存空间内提取之前已经下载的数据 16 //开始下载前必须先判断是否准备好了,否则一直循环下去,等待准备好 17 while (!Caching.ready) //检查cache 缓存是否准备好了 18 yield return null; 19 20 //完整的路径---本地下载 21 //string allPath = @"E:/03_TestDemo/01_UnityDemo/ShowMap_ABPackageTest_001/AssetBundles/scene/map_01"; 22 //WWW www = WWW.LoadFromCacheOrDownload(allPath, 1); 23 24 //远程服务器下载 25 WWW www = WWW.LoadFromCacheOrDownload(@"http://localhost/AssetBundles/scene/map_01", 1); //从远程服务器上下载AB包 26 yield return www; 27 28 //因万维网是非常复杂的,经常容易出现非常意外的错误,所以必须加入一个判断。 29 if(!string.IsNullOrEmpty(www.error)) //当什么都没有时 30 { 31 Debug.Log(www.error); 32 yield break; //跳出协程 33 } 34 35 AssetBundle ab = www.assetBundle; 36 37 //使用资源 38 GameObject gameObj = ab.LoadAsset<GameObject>("map_001"); 39 Instantiate(gameObj); 40 41 yield return null; //上述下载时没有加载相应的贴图材质。。。注意!!! 42 } 43 }
4、第四种Unity封装的UnityWebRequest对象---(专用于替代WWW的远程加载方式)
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class LoadFromFileExample : MonoBehaviour 6 { 7 private IEnumerator Start() 8 { 9 //这里需要注意的是,如果你想加载的资源有依赖的资源 如贴图等资源,则必须先加载依赖资源后加载你所想加载的资源。 10 //即A依赖B 则必须先加载B到内存中,然后再加载A 并实例化---这里注意依赖项一般可以不用实例化只需要在内存中即可。 11 //AssetBundle abShare = AssetBundle.LoadFromFile("AssetBundles/materials/mapmaterials"); 12 13 //第四种远程加载方式---(专门用于替换WWW的远程加载方式) 14 //string uri = @"E:/03_TestDemo/01_UnityDemo/ShowMap_ABPackageTest_001/AssetBundles/scene/map_001"; 15 string uri = @"http://localhost/AssetBundles/scene/map_001"; 16 UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri); 17 yield return request.SendWebRequest(); //注意是异步方式所以必须等待他完成后再执行 18 19 //两种获取ab对象的方式 20 //AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request); //第一种方法 21 AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; //第二种方法 22 23 //使用资源 24 GameObject gameObj = ab.LoadAsset<GameObject>("map_001"); 25 Instantiate(gameObj); 26 27 yield return null; //注意上述的远程加载方式是没有加载其依赖的资源 28 } 29 }
5_加载<AB包依赖项> ---(根据AB包文件夹下的 "AssetBundle文件以及AssetBundle.manifest文件")
1 using System.Collections; 2 using UnityEngine; 3 using UnityEngine.Networking; 4 5 public class Load : MonoBehaviour 6 { 7 //第一种是服务端加载AB资源包 和依赖包 8 private IEnumerator Start() 9 { 10 //从一个服务器下载一个AB包的管理文件AssetBundles 和 AssetBundles.manifest 11 string uri = @"http://localhost/AssetBundles/AssetBundles"; 12 UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(uri); 13 yield return request.SendWebRequest(); 14 15 //AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle; //第一种方法 16 AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);//第二种方法 17 AssetBundleManifest manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); 18 19 string[] str = manifest.GetAllAssetBundles(); 20 21 foreach(string s in str ) 22 { 23 24 UnityWebRequest uwr = UnityWebRequestAssetBundle.GetAssetBundle(@"http://localhost/AssetBundles/" + s); 25 yield return uwr.SendWebRequest(); 26 AssetBundle TmpAB = DownloadHandlerAssetBundle.GetContent(uwr);//第二种方法 27 Object[] obj = TmpAB.LoadAllAssets(); 28 29 foreach (Object o in obj) 30 { 31 if(o is GameObject) 32 { 33 Instantiate(o); 34 } 35 36 print(obj.Length); 37 } 38 39 } 40 41 42 43 yield return null; 44 } 45 46 47 48 //第二种本地加载AB资源包 和依赖包 49 #region LoadAB.manifest 50 //private void Start() 51 //{ 52 // //加载得到Manifest文件 53 // AssetBundle ab = AssetBundle.LoadFromFile("Assets /AssetBundle/AssetBundle"); 54 // AssetBundleManifest manifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); 55 56 // //从Manifest文件中得到所有的AB包的路径(包括依赖项) 57 // string[] str = manifest.GetAllAssetBundles(); 58 // foreach (string s in str) 59 // { 60 // AssetBundle assetBundle = AssetBundle.LoadFromFile("Assets/AssetBundle/" + s); 61 // Object[] o = assetBundle.LoadAllAssets(); 62 // foreach (Object temp in o) 63 // { 64 // if (temp is GameObject) 65 // { 66 // Instantiate(temp); 67 // } 68 // } 69 // } 70 //} 71 #endregion 72 }
小结:上述方法直接在场景中见一个空物体挂载其脚本即可(注意前提是AB包必须已经按相应方式创建好了)。
//=============================================================================================================
五:AssetBundle 打包时的分组(仅供参考)
1、逻辑实体分组:
a、一个UI界面或者所有UI界面一个包(这个界面里面所有的贴图和布局信息一个包);
b、一个角色或者所有角色一个包(这个角色里面的模型和动画一个包);
c、所有场景所共有的部分一个包(包括贴图和模型);
2、按照类型分组
所有的声音资源打成一个包,所有shader打成一个包,所有模型打成一个包,所有材质打成一个包。
3、按使用分组
把在某一时间内使用的所有资源打成一个包,可以按照关卡分,一个关卡所需要的所有资源包,包括角色、
贴图、声音等打成一个包,也可以按照场景分,一个场景所需要的资源一个包。
小结:
1、把经常更新的资源放在一个单独的包里、跟不经常更新的包分离。
2、把需要同时加载的资源放在一个包里面。
3、可以把其他包共享的资源放在一个单独的包里面。(做依赖,这样可以减少很多比较资源占用)
4、把一些需要同时加载的小资源打包成一个包。
5、如果对一个同一个资源有两个版本,可以考虑通过后缀来区分(如:v1 v2 v3 )(如unityAB_V1 、unityAV_V2);
//=============================================================================================================
六 : 依赖打包简介
小结:如图所示,其主要的特点是把模型预制体所使用的材质贴图进行整合,这样可以避免占用过多的内存资源,
在加载时也可以避免包过大使用的时间太长,同时避免了资源的重复加载。在需要加载时也可以通过依赖
关系进行逆向加载。
//=============================================================================================================
七: Build AssetBundle方法参数详解(BuildPipline.BuildAssetBundle)
1、Build的路径(只要是在硬盘上都可以的)
2、BuildAssetBundleOptions(枚举类型)
a)、BuildAssetBundleOptions.None:使用LZMA算法压缩,压缩的包更小,但加载时间更长,
使用之前需要整体解压 。一旦被解压,这个包会使用LZ4重新压缩。使用资源的时候不需要整体解压
。在下载的时候可以使用LZMA算法。一旦它被下载了之后,它会使用LZ4算法保存到本地上。
b)、BuildAssetBundleOption.UncompressedAssetBundle (不压缩,包大,加载速度快)。
c)、BuildAssetBundleOption.ChunkBasedCompression:(使用LZ4算法,压缩率没有LZMA高,
但我们可以加载指定资源的不用解压全部);
注意:使用LZ4算法,可以获得可以跟不压缩相媲美的加载速度,而且比不压缩的文件要小。
3、BuildTarget:(选择Build出来的AB包需要的平台)
图解:
//=============================================================================================================
八:关于AssetBundle的卸载
首先为什么要把AB 包卸载了?其实也很简单,内存永远是有限的,当你转换一个场景或者关卡时,之前不需要的AB包所占用的内存是需要把它
释放掉的,这样才能让内存永远保持着一个健康的容量。
卸载主要有两个方面:
1、 减少内存使用,可以保证一个良好且健康的运行内存。
2、 有可能导致资源丢失问题。
所以一般什么时候卸载资源呢?
AssetBundle.Unload(true)卸载所有资源,即使有资源使用着(1,在关卡切换或场景切换时 2、资源没被用的时候调用);
AssetBundle.Unload(false)卸载所有没有被使用的资源。(这个适用时间比较多 ,可自行把控)。
个别资源怎么卸载,通过Resources.UnloadUnusedAssets();
当场景切换时,unity会自行执行(Resources.UnloadUnusedAssets()这个方法);
//=============================================================================================================
九:关于AssetBundle的文件效验(每个AB包中有一个manifest文件中就有一个CRC)
CRC、DM5、SHA1
相同点:
CRC、DM5、SHA1都是通过对数据进行计算,来生成一个效验值,该效验值用来效验数据的完整性。
不同点:
1、算法的不同。CRC采用多项式除法,MD5和SHA1使用的替换,轮转等方法。
2、效验值的长度不同。CRC效验值的长度基本跟其多项式有关系,一般为16位或32位,MD5是16个字节(128位);SHA1是20个字节(160位);
3、效验值的称呼不同。CRC一般叫做CRC值;MD5和SHA1一般叫哈希值(Hash)或散列值。
4、安全性不同。这里的安全性是指效验的能力,即数据的错误能通过效验位检测出来,CRC的安全性跟多项式有很大关系,相对于MD5和SHA1要弱很多;MD5的安全性很高,不过大概在04年的时候被山东大学的王小云破解了;SHA1安全性最高。
(算法的过程一般是不可逆的,破解即是逆向根据效验值推导出算法过程列表)
5、效率不同。CRC的计算效率很高;MD5和SHA1比较慢。
6、用途不同。CRC一般用作通信数据的效验,MD5和SHA1用于安全(Security)领域。比如文件效验、数字签名等。