AssetBundle入门
一、什么是AssetBundle
AssetBundle是一个压缩包包含模型、贴图、预制体、声音、甚至整个场景,可以在游戏运行的时候被加载。
二、AssetBundle有什么作用
为了通过网络高效传输,可以根据用例要求(LZMA和LZ4)使用内置算法的选择来压缩AssetBundle。减小初始安装包的大小,加载针对最终用户平台优化的资源以及减轻运行时内存压力。
说白了,就是减小Unity发布到平台包的大小。
三、AssetBundle中有什么?
AssetBundle是一个压缩包包含模型、贴图、预制体、声音、场景等资源,但实际上这些文件被分为了两类文件来存储:serialized file (序列化文件)和 resource files(资源文件)。
预制体、场景等资源是属于序列化文件,图片、声音属于二进制资源是属于资源文件。
序列化的文件包含预制体、场景等资源,这些资产被分解打散,最后统一被写进一个单独的文件(只有一个)。资源文件会单独存储保存,以使我们能够从磁盘上的另一个线程有效地加载它们。
如下图:
我们可以通过代码从一个特定的压缩包加载出来的对象。这个对象包含了所有我们当初添加到这个压缩包里面的内容,我们可以通过这个对象加载出来使用。
四、AssetBundle使用流程
1,指定资源的AssetBundle属性
(xxxa/xxx)这里xxxa会生成目录,名字为xxx
2,构建AssetBundle包
3,上传AB包
4,加载AB包和包里面的资源
五、AssetBundle使用相关API
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); AssetBundle ab = AssetBundle.LoadFromFile("AssetBundles/scene/wall.unity3d"); GameObject wallPrefab = ab.LoadAsset<GameObject>("CubeWall"); Instantiate(wallPrefab);
六、AssetBundle分组策略
1,逻辑实体分组
a,一个UI界面或者所有UI界面一个包(这个界面里面的贴图和布局信息一个包)
b,一个角色或者所有角色一个包(这个角色里面的模型和动画一个包)
c,所有的场景所共享的部分一个包(包括贴图和模型)
2,按照类型分组
所有声音资源打成一个包,所有shader打成一个包,所有模型打成一个包,所有材质打成一个包
3,按照使用分组
把在某一时间内使用的所有资源打成一个包。可以按照关卡分,一个关卡所需要的所有资源包括角色、贴图、声音等打成一个包。也可以按照场景分,一个场景所需要的资源一个包
总结
1,把经常更新的资源放在一个单独的包里面,跟不经常更新的包分离
2,把需要同时加载的资源放在一个包里面
3,可以把其他包共享的资源放在一个单独的包里面
4,把一些需要同时加载的小资源打包成一个包
5,如果对于一个同一个资源有两个版本,可以考虑通过后缀来区分 v1 v2 v3 unity3dv1 unity3dv2
七、关于BuildAssetBundleOptions参数
BuildAssetBundleOptions.None:使用LZMA算法压缩,压缩的包更小,但是加载时间更长。使用之前需要整体解压。一旦被解压,这个包会使用LZ4重新压缩。使用资源的时候不需要整体解压。在下载的时候可以使用LZMA算法,一旦它被下载了之后,它会使用LZ4算法保存到本地上。
BuildAssetBundleOptions.UncompressedAssetBundle:不压缩,包大,加载快
BuildAssetBundleOptions.ChunkBasedCompression:使用LZ4压缩,压缩率没有LZMA高,但是我们可以加载指定资源而不用解压全部。
注意使用LZ4压缩,可以获得可以跟不压缩想媲美的加载速度,而且比不压缩文件要小。
八、AB包的依赖项
如果打包的资源对象中其中一个或多个对象包含对另一个包中对象的的的引用,则AssetBundle依赖于包含引用对象的AssetBundle 。若打包资源内未包含的引用,则不会发生依赖关系。
如果AssetBundle包含依赖项,则在加载要实例化的对象之前,先加载包含那些依赖项的捆绑包,这一点很重要。Unity不会尝试自动加载依赖项。
可以通过加载Manifests文件可以处理资源的依赖。
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath); AssetBundleManifest manifest = assetBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); string[] dependencies = manifest.GetAllDependencies("assetBundle"); //Pass the name of the bundle you want the dependencies for. foreach(string dependency in dependencies) { AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency)); }
获取到所有依赖包的目录,加载所有的依赖包之后实例化对象资源,避免不可预知的错误。
九、四种加载AB包的方式
1,AssetBundle.LoadFromMemoryAsync 从内存中异步加载
传入相对路径,也可以选择传入CRC值。如果捆绑包是LZMA压缩的,则它将在加载时解压缩AssetBundle。LZ4压缩束以其压缩状态加载。
using UnityEngine; using System.Collections; using System.IO; public class Example : MonoBehaviour { IEnumerator LoadFromMemoryAsync(string path) { AssetBundleCreateRequest createRequest = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); yield return createRequest; AssetBundle bundle = createRequest.assetBundle; var prefab = bundle.LoadAsset<GameObject>("MyObject"); Instantiate(prefab); } }
2,AssetBundle.LoadFromFile 从本地硬盘中加载
从本地存储加载未压缩的捆绑包时,此API效率很高。如果未压缩或未压缩块(LZ4),则LoadFromFile将直接从磁盘加载。使用此方法加载完全压缩(LZMA)捆绑软件将首先解压缩捆绑软件,然后再将其加载到内存中。
public class LoadFromFileExample extends MonoBehaviour { function Start() { var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(Application.streamingAssetsPath, "myassetBundle")); if (myLoadedAssetBundle == null) { Debug.Log("Failed to load AssetBundle!"); return; } var prefab = myLoadedAssetBundle.LoadAsset.<GameObject>("MyObject"); Instantiate(prefab); } }
注意:在具有Unity 5.3或更旧版本的Android设备上,尝试从Streaming Assets路径加载AssetBundle时,此API将失败。这是因为该路径的内容被压缩在的.jar文件中。
3,WWW.LoadFromCacheOrDownload
不推荐使用(使用UnityWebRequest)
该API可用于从远程服务器下载AssetBundles或加载本地AssetBundles。它是UnityWebRequest API的较旧且不太理想的版本。
using UnityEngine; using System.Collections; public class LoadFromCacheOrDownloadExample : MonoBehaviour { IEnumerator Start () { while (!Caching.ready) yield return null; var www = WWW.LoadFromCacheOrDownload("http://myserver.com/myassetBundle", 5); yield return www; if(!string.IsNullOrEmpty(www.error)) { Debug.Log(www.error); yield return; } var myLoadedAssetBundle = www.assetBundle; var asset = myLoadedAssetBundle.mainAsset; } }
4,UnityWebRequest
使用UnityWebRequest的优点在于,它允许开发人员以更灵活的方式处理下载的数据,并有可能消除不必要的内存使用。
IEnumerator InstantiateObject() { string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName; UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0); yield return request.Send(); AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request); GameObject cube = bundle.LoadAsset<GameObject>("Cube"); GameObject sprite = bundle.LoadAsset<GameObject>("Sprite"); Instantiate(cube); Instantiate(sprite); }
十、从AssetBundles加载资产
T objectFromBundle = bundleObject.LoadAsset<T>(assetName);
GameObject gameObject = loadedAssetBundle.LoadAsset<GameObject>(assetName);
加载所有资源:
Unity.Object[] objectArray = loadedAssetBundle.LoadAllAssets();
异步加载所有:
AssetBundleRequest request = loadedAssetBundleObject.LoadAssetAsync<GameObject>(assetName); yield return request; var loadedAsset = request.asset;
AssetBundleRequest request = loadedAssetBundle.LoadAllAssetsAsync(); yield return request; var loadedAssets = request.allAssets;