大家好,又见面了,我是你们的朋友全栈君。
#AssetBundle作用原理 把资源导出成一种叫做AssetBundle的文件,然后打包后可以在Unity程序运行的时候再加载回来用。 AssetBundle是采取某一种压缩方式压缩成的资源文件。节省存储空间,控制游戏包的大小,实现游戏的热更新。
AssetBundle文件分类
AssetBundle文件可以分为两类:序列化文件(serialized file)和资源文件(resource files)。 serialized file:资源被打碎放在一个对象中,最后统一被写进一个单独的文件(只有一个)。 resource files:某些二进制资源(图片、声音)被单独保存,方便快速加载。
AssetBundle分组策略
Ab分组策略:
- 逻辑实体分组 1)一个UI界面 或 所有UI界面一个包(界面里的贴图和布局信息一个包) 2)一个角色 或 所有角色一个包(这个角色里面的模型和动画一个包) 3)所有的场景所共享的部分一个包(包括贴图和模型)
- 按照类型分组 所有声音资源打成一个包,所有shader打成一个包,所有模型打成一个包,所有材质打成一个包
- 按照使用分组 1)把在某一时间需要同时加载的所有资源打成一个包。可以按照关卡或场景划分,一个关卡或场景所需要的所有资源包括角色、贴图、声音等打成一个包 2)把被其他包所共享的资源放在一个单独的包里面 3)把经常更新的资源放在一个单独的包里面,跟不经常更新的包分离
- 版本更新分组 如果对于一个同一个资源有两个版本,可以考虑通过后缀来区分,如v1/v2/v3.unity3d
BuileAssetBundles
AssetBundle使用相关API:BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64); 结合Unity Editor使用如下:
代码语言:javascript复制#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
LoadAssetBundles
Ab包的加载主要有四种方式:
- AssetBundle.LoadFromMemory和AssetBundle.LoadFromMemoryAsync //内存加载
- AssetBundle.LoadFromFile和AssetBundle.LoadFromFileAsync //本地文件加载
- WWW.LoadFromCacheOrDownload //服务器和本地都可加载,不推荐
- UnityWebRequest //服务器加载
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<T>(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset<GameObject>("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<T>(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset<GameObject>("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<T>(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset<GameObject>("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<T>(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset<GameObject>("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<T>(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset<GameObject>("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<T>(assetName);
//Object[] objArray = AssetBundle.LoadAllAssets();
GameObject obj = ab.LoadAsset<GameObject>("assetname");
GameObject[] objs= ab.LoadAllAssets();
//实例化对象
Instantiate(obj);
foreach(Object o in objs)
{
Instantiate(o);
}
} //协程
}
DealDependent by Manifests
通过加载Manifests文件可以处理资源的依赖。
代码语言:javascript复制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>("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)作用是合并两个路径字符串
}
}
}
UnloadAssetBundles
卸载AssetBundle主要是为了减少内存占用,但有可能导致依赖丢失。
- AssetBundle.Unload(true):卸载所有资源,即使有资源被使用着,并且卸载内存中已加载的资源。 PS:一般在关卡切换、场景切换时候调用。
- AssetBundle.Unload(false):卸载所有没用被使用的资源,但会保留内存中已加载的资源。如果重新创建该方法卸载的对象,内存会存相同的资源,造成内存泄漏。
关于资源的其他卸载方式: 场景对象(GameObject):这类物件可通过Destroy函数进行销毁;
资源对象(Resources),除了Prefab以外,资源文件还可以通过两种方式来卸载: 1)通过Resources.UnloadAsset卸载指定的资源,CPU开销小; 2)通过Resources.UnloadUnusedAssets一次性卸载所有未被引用的资源,CPU开销大;需要注意被静态变量引用的资源,调用该方法并不会被卸载,在Profiler中能够看到其引用情况。
WWW对象:调用对象的Dispose函数或将其置为null即可; WebStream:在卸载WWW对象以及对应的AssetBundle对象后,这部分内存即会被引擎自动卸载; SerializedFile:卸载AssetBundle后,这部分内存会被引擎自动卸载;
代码语言:javascript复制using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking; //UnityWebRequest
public class UnloadAssetBundle : MonoBehaviour
{
IEnumerator Unload() //定义一个卸载ab包的协程
{
//此处根据加载ab包的方式进行相应的卸载
}
}
文件校验(CRC、MD5、SHA1)
CRC、MD5、SHA1都是通过对数据进行计算,来生成一个校验值,该校验值用来校验数据的完整性。 Unity在加载Ab包之后,会通过计算生成一个校验值,去和传递过来的校验值进行比对,从而判断数据是否完整。 三种校验算法的不同点:
- 算法不同。CRC采用多项式除法,MD5和SHA1使用的是替换、轮转等方法;
- 校验值的长度不同。CRC校验位的长度跟其多项式有关系,一般为16位或32位,MD5是16个字节(128位),SHA1是20个字节(160位);
- 校验值的称呼不同。CRC一般叫做CRC值;MD5和SHA1一般叫做哈希值或散列值(Hash);
- 安全性不同。这里的安全性是指检错的能力,即数据的错误能通过校验位检测出来。CRC的安全性跟多项式有很大关系,相对于MD5和SHA1要弱很多;MD5的安全性很高,SHA1的安全性最高。
- 效率不同,CRC的计算效率很高;MD5和SHA1比较慢。
- 用途不同。CRC一般用作通信数据的校验;MD5和SHA1用于安全(Security)领域,比如文件校验、数字签名等。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/149607.html原文链接:https://javaforall.cn