#AssetBundle作用原理
把资源导出成一种叫做AssetBundle的文件,然后打包后可以在Unity程序运行的时候再加载回来用。
AssetBundle是采取某一种压缩方式压缩成的资源文件。节省存储空间,控制游戏包的大小,实现游戏的热更新。
AssetBundle文件可以分为两类:序列化文件(serialized file)和资源文件(resource files)。
serialized file:资源被打碎放在一个对象中,最后统一被写进一个单独的文件(只有一个)。
resource files:某些二进制资源(图片、声音)被单独保存,方便快速加载。
Ab分组策略:
AssetBundle使用相关API:BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
结合Unity Editor使用如下:
#if UNITY_EDITOR
using UnityEditor;
using System.IO;
public class BulidAssetBundle
{
//编辑器扩展,在菜单栏Assets下生成Build AssetBundles菜单
[MenuItem("Assets/Build AssetBundles")]
//进行资源打包
static void BuildAllAssetBundles()
{
string dir = "AssetBundle"; //使用相对路径保存,文件夹AssetBundle在为工程目录下
if (Directory.Exists(dir) == false) //打包之前要保证文件夹存在,不存在的话会报错
{
Directory.CreateDirectory(dir);
}
//BuildPipeline是UnityEditor中用于打包的类,其中的BuildAssetBundle用于AssetBundle打包
BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.ChunkBasedCompression, BuildTarget.StandaloneWindows64);
//dir:存储的路径
//BuildAssetBundleOptions:表示压缩式的算法
//BuildTarget: 表示用于什么平台
}
/*关于BuildAssetBundleOptions的算法选择*/
//BuildAssetBundleOptions.None:LZMA压缩,压缩包小,但加载时间长。下载之前需要整体解压。下载完成后,包会使用LZ4重新压缩保存于本地,从而在使用资源的时候不需要整体解压。
//BuildAssetBundleOptions.ChunkBasedCompression:LZ4压缩,压缩率没LZMA高,但可以加载指定资源而不用全解压。一般使用LZ4压缩,占存小,却几乎可以跟不压缩的加载速度相媲美。
//BuildAssetBundleOptions.UncompressedAssetBundle:不压缩,包占存大,加载快
}
#endif
Ab包的加载主要有四种方式:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking; //UnityWebRequest
public class LoadAssetBundle : MonoBehaviour
{
IEnumerator Load() //定义一个加载ab包的协程
{
string path = @"AssetBundles/xxx.unity3d";
string url = @"http://localhost/AssetBundles/xxx.unity3d";
/*第一种加载AB的方式,同步内存加载,AssetBundle.LoadFromMemory*/
//卸载加载缓存数据,如果有某个系统来管理加载好的数据就不需要
AssetBundle.UnloadAllAssetBundles(true);
//直接加载AB包
AssetBundle ab = AssetBundle.LoadFromMemory(File.ReadAllBytes(path)); //同步加载二进制文件
//加载资源对象
//Object obj = AssetBundle.LoadAsset(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset("assetname");
GameObject[] objs= ab.LoadAllAssets();
//实例化对象
Instantiate(obj);
foreach(Object o in objs)
{
Instantiate(o);
}
/*第一种加载AB的方式,异步内存加载,AssetBundle.LoadFromMemoryAsync*/
//间接获取请求
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path)); //异步加载二进制文件
//等待请求完成
yield return request;
//卸载加载缓存数据,如果有某个系统来管理加载好的数据就不需要
AssetBundle.UnloadAllAssetBundles(true);
//获取AB对象
AssetBundle ab = request.assetBundle;
//加载资源对象
//Object obj = AssetBundle.LoadAsset(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset("assetname");
GameObject[] objs= ab.LoadAllAssets();
//实例化对象
Instantiate(obj);
foreach(Object o in objs)
{
Instantiate(o);
}
/*第二种加载AB的方式,同步本地文件加载,AssetBundle.LoadFromFile*/
//卸载加载缓存数据,如果有某个系统来管理加载好的数据就不需要
AssetBundle.UnloadAllAssetBundles(true);
//直接加载AB包
AssetBundle ab = AssetBundle.LoadFromFile(path); //同步加载本地文件
//加载资源对象
//Object obj = AssetBundle.LoadAsset(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset("assetname");
GameObject[] objs= ab.LoadAllAssets();
//实例化对象
Instantiate(obj);
foreach(Object o in objs)
{
Instantiate(o);
}
/*第二种加载AB的方式,异步本地文件加载,AssetBundle.LoadFromFileAsync*/
//间接获取请求
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path); //异步加载本地文件
//等待请求完成
yield return request;
//卸载加载缓存数据,如果有某个系统来管理加载好的数据就不需要
AssetBundle.UnloadAllAssetBundles(true);
//获取AB对象
AssetBundle ab = request.assetBundle;
//加载资源对象
//Object obj = AssetBundle.LoadAsset(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset("assetname");
GameObject[] objs= ab.LoadAllAssets();
//实例化对象
Instantiate(obj);
foreach(Object o in objs)
{
Instantiate(o);
}
/*第三种加载AB的方式,服务器和本地都可加载,WWW.LoadFromCacheOrDownload*/
while (Caching.ready == false)
yield return null;
//获取www请求
WWW www = WWW.LoadFromCacheOrDownload(url, 1);
yield return www;
if( !string.IsNullOrEmpty(www.error) )
{
Debug.Log(www.error);
yield break;
}
//卸载加载缓存数据,如果有某个系统来管理加载好的数据就不需要
AssetBundle.UnloadAllAssetBundles(true);
//直接加载AB包
AssetBundle ab = www.assetBundle;
//加载资源对象
//Object obj = AssetBundle.LoadAsset(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset("assetname");
GameObject[] objs= ab.LoadAllAssets();
//实例化对象
Instantiate(obj);
foreach(Object o in objs)
{
Instantiate(o);
}
/*第四种加载AB的方式,服务器加载 ,UnityWebRequest*/
//获取服务器请求
UnityWebRequest request = UnityWebRequest.GetAssetBundle(url);
//等待请求发送完
yield return request.SendWebRequest();
//卸载加载缓存数据,如果有某个系统来管理加载好的数据就不需要
AssetBundle.UnloadAllAssetBundles(true);
//从DownloadHandlerAssetBundle获取request,得到AB对象
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request);
AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
//加载资源对象
//Object obj = AssetBundle.LoadAsset(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset("assetname");
GameObject[] objs= ab.LoadAllAssets();
//实例化对象
Instantiate(obj);
foreach(Object o in objs)
{
Instantiate(o);
}
} //协程
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking; //UnityWebRequest
public class Manifest : MonoBehaviour
{
IEnumerator Manifests() //定义一个处理资源依赖的协程
{
AssetBundle assetBundle = AssetBundle.LoadFromFile(manifestFilePath); //加载Ab资源
AssetBundleManifest manifest = assetBundle.LoadAsset("AssetBundleManifest"); //通过加载到的Ab资源对象获得Manifest资源
string[] assets = manifest.GetAllAssetBundles() //获取Manifests中的所有AssetBundle的名字
//传递所有Manifest中所有AssetBundle的包的名称,通过迭代器加载所有包
foreach(string asset in assets)
{
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, asset) ); //Path.Combine(path1, path2)作用是合并两个路径字符串
}
string[] dependencies = manifest.GetAllDependencies("assetBundle"); //通过获得的Manifest资源对象获得assetBundle包的所有依赖包的名字
//传递当前包需要依赖包的名称,通过迭代器加载依赖的所有包
foreach(string dependency in dependencies)
{
AssetBundle.LoadFromFile(Path.Combine(assetBundlePath, dependency) ); //Path.Combine(path1, path2)作用是合并两个路径字符串
}
}
}
卸载AssetBundle主要是为了减少内存占用,但有可能导致依赖丢失。
关于资源的其他卸载方式:
场景对象(GameObject):这类物件可通过Destroy函数进行销毁;
资源对象(Resources),除了Prefab以外,资源文件还可以通过两种方式来卸载:
1)通过Resources.UnloadAsset卸载指定的资源,CPU开销小;
2)通过Resources.UnloadUnusedAssets一次性卸载所有未被引用的资源,CPU开销大;需要注意被静态变量引用的资源,调用该方法并不会被卸载,在Profiler中能够看到其引用情况。
WWW对象:调用对象的Dispose函数或将其置为null即可;
WebStream:在卸载WWW对象以及对应的AssetBundle对象后,这部分内存即会被引擎自动卸载;
SerializedFile:卸载AssetBundle后,这部分内存会被引擎自动卸载;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking; //UnityWebRequest
public class UnloadAssetBundle : MonoBehaviour
{
IEnumerator Unload() //定义一个卸载ab包的协程
{
//此处根据加载ab包的方式进行相应的卸载
}
}
CRC、MD5、SHA1都是通过对数据进行计算,来生成一个校验值,该校验值用来校验数据的完整性。
Unity在加载Ab包之后,会通过计算生成一个校验值,去和传递过来的校验值进行比对,从而判断数据是否完整。
三种校验算法的不同点: