一:什么是AssetBundle?
AssetBundle就是一个资源捆绑包,就是我们常说的AB包,一个AB中可能包含多个文件(模型、贴图、材质、预制体、声音、场景等)。
二:AssetBundle的优点
1、可以放在任意可读可写的文件夹内,在游戏运行时进行加载资源,从而减少安装包的大小。
2、可以实现资源的热更新。
3、有助于更有效的管理和优化资源。
三:如何使用AssetBundle
1、构建AssetBundle
我们在场景中创建一个球体,然后把他做成预制体。预览这个预制体,我们观察左下角的AssetBundle。
点击下拉框→new→输入路径或者名字(支持目录形式)。
如果有两个资源选择了同一个路径,那么他们会被打成一个包。
我们实现一个手动Bulid的功能脚本
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
public class BulidAssetBundle : MonoBehaviour
{
[MenuItem("Framework/AssetBundleHelper/Build", false, 1)]
private static void BuildAssetBundle()
{
string path = "Assets/AssetBundles";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
BuildPipeline.BuildAssetBundles(path, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows64);
AssetDatabase.Refresh(); //刷新工程目录的缓存
}
}
我们进行第一次构建,会生成以下文件:
其中sphere.ab是我们构建出来的AssetBundle本地,.manifest的文件是这个AssetBundle对应的清单文件。
2、加载AssetBundle
加载AssetBundle有四种方式,分别是从本地文件加载、从流加载、从内存加载以及从远程服务器加载。它们分别对应如下几个API
- AssetBundle.LoadFromFile从本地加载
- AssetBundle.LoadFromMemory从内存中加载
- AssetBundle.LoadFromStream从流中加载
- UnityWebRequestAssetBundle.GetAssetBundle从远程服务器加载(也可以从本地加载)
AssetBundle.LoadFromFile
如果AssetBundle未压缩或采用了数据块(LZ4)压缩方式,LoadFromFile 将直接从磁盘加载AssetBundle。使用此方法加载完全压缩 (LZMA) 的AssetBundle将首先解压缩AssetBundle,然后再将其加载到内存中。
// 同步方式
private static void LoadMethodSync()
{
string path = "Assets/AssetBundles/sphere";
AssetBundle assetBundle = AssetBundle.LoadFromFile(path);
GameObject sphere= assetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
}
// 异步方式
IEnumerator LoadMethodAsync()
{
string path = "Assets/AssetBundles/sphere";
AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(path);
yield return request;
var sphere= request.assetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
}
AssetBundle.LoadFromMemory
该方法传入一个包含AssetBundle数据的字节数组。也可以根据需要传递CRC校验码。如果捆绑包采用的是LZMA压缩方式,将在加载时解压缩AssetBundle。LZ4 压缩包则会以压缩状态加载。当下载的是加密数据并需要从未加密的字节创建AssetBundle时会用到。
// 同步方法
private static void LoadMethodSync()
{
string path = "Assets/AssetBundles/sphere";
AssetBundle assetBundle = AssetBundle.LoadFromMemory(File.ReadAllBytes(path));
Instantiate(sphere);
}
// 异步方法
IEnumerator LoadMethodAsync()
{
string path = "Assets/AssetBundles/sphere";
AssetBundleCreateRequest request = AssetBundle.LoadFromMemoryAsync(File.ReadAllBytes(path));
yield return request;
var sphere= request.assetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
}
AssetBundle.LoadFromStream
从托管Stream加载AssetBundle。如果是LZMA压缩,则将数据解压缩到内存。如果是未压缩或使用块压缩的捆绑包,则直接从Stream读取。需要注意的是,在加载AssetBundle或其中的资源时,不应该释放Stream资源,应该在AssetBundle.Unload之后再释放。相比于从内存加载,从流加载的优势是加载加密资源时,占用的内存较小。
// 同步方法
private static void LoadMethodSync()
{
AssetBundle.UnloadAllAssetBundles(true);
string path = "Assets/AssetBundles/sphere";
FileStream stream = new FileStream(path, FileMode.Open,FileAccess.Read);
AssetBundle assetBundle = AssetBundle.LoadFromStream(stream);
GameObject sphere= assetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
assetBundle.Unload(false);
stream.Close();
}
// 异步方法
IEnumerator LoadMethodAsync()
{
string path = "Assets/AssetBundles/sphere";
FileStream stream = new FileStream(path, FileMode.Open,FileAccess.Read);
AssetBundleCreateRequest request = AssetBundle.LoadFromStreamAsync(stream);
yield return request;
var sphere= request.assetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
request.assetBundle.Unload(false);
stream.Close();
}
UnityWebRequestAssetBundle.GetAssetBundle
该方法会创建经过优化的 UnityWebRequest,以通过 HTTP GET 下载AssetBundle。请求成功后,使用DownloadHandlerAssetBundle
方法将数据流式传输到缓冲区并解压缩。与一次性下载所有数据相比,这种方式节省了很多内存。[如果加载本地数据,需要在路径前加上file://]
IEnumerator LoadMethodAsync()
{
string path = @"[远程资源路径]";
UnityWebRequest request = UnityWebRequestAssetBundle.GetAssetBundle(path);
yield return request.SendWebRequest();
AssetBundle assetBundle = DownloadHandlerAssetBundle.GetContent(request);
GameObject sphere= assetBundle.LoadAsset<GameObject>("Sphere");
Instantiate(sphere);
}
3、加载AssetBundle
string path = "Assets/AssetBundles/sphere";
AssetBundle assetBundle = AssetBundle.LoadFromFile(path);
// 同步加载指定资源
GameObject sphere= assetBundle.LoadAsset<GameObject>("Sphere");
// 异步加载指定资源
AssetBundleRequest request1 = assetBundle.LoadAssetAsync<GameObject>("Sphere");
// 同步加载所有资源
UnityEngine.Object[] objs = assetBundle.LoadAllAssets();
// 异步加载所有资源
AssetBundleRequest request2 = assetBundle.LoadAllAssetsAsync();
4、加载AssetBundle
当你不在使用AssetBundle加载出来的资源时,需要及时将他卸载掉,来释放他占用的内存空间。
核心API:AssetBundle.Unload(bool)
默认传true,表示卸载AssetBundle本身以及从AssetBundle加载出来的全部资源,建议传true,不然会导致资源无法匹配,生成多份资源,造成内存资源的浪费。
四:AssetBundle依赖关系
例如A包是一个材质包,B包是一个贴图包,A包中的材质引用B包中的贴图,如果不加载B包只加载A包,则A包中材质的贴图是丢失的。
五:AssetBundle分组策略
- 逻辑实体分组
一个UI界面或所有UI界面一个包(贴图、布局信息) 一个角色或所有角色一个包(模型、动画) 场景之间共享的资源一个包 - 类型分组
所有音频资源一个包 所有模型资源一个包 所有材质资源一个包 - 并发内容分组
同一时间内需要用到的资源一个包 一个关卡所需的资源一个包 一个场景所需的资源一个包
合理的分组策略能有效处理资源包依赖错乱的问题。
六:工具使用
- Analyze tool: 可以查看资源冗余以及系统如何打包资源,可以通自定义规则去生成额外的报告信息。
检查资源的冗余依赖 |
检查Resources到Addressables的冗余依赖 |
检查Scene到Addressables的冗余依赖 |
Bundle Layout 预览 |
- Event viewer:可以查看运行时的操作事件,例如加载和卸载
- Build layout report:描述Addressables如何将组内的资源打包到Bundles
- Build profile log:构建时的日志,可以在Chrome中查看。
参考: