Unity中的AssetBundle详解
AssetBundle的概念
AssetBundle又称AB包,是Unity提供的一种用于存储资源的资源压缩包。
Unity中的AssetBundle系统是对资源管理的一种扩展,通过将资源分布在不同的AB包中可以最大程度地减少运行时的内存压力,可以动态地加载和卸载AB包,继而有选择地加载内容。
AssetBundle的优势
-
AB包存储位置自定义,继而可放入可读可写的路径下便于实现热更新 -
AB包自定义压缩方式,可以选择不压缩或选择LZMA和LZ4等压缩方式,减小包的大小,更快的进行网络传输。 -
资源可分布在不同的AB包中,最大程度减少运行时的内存压力, 可做到即用即加载,有选择的加载需要的内容。 -
AB包支持后期进行动态更新,显著减小初始安装包的大小,非核心资源以AB包形式上传服务器,后期运行时动态加载,提高用户体验。
AssetBundle和Resources的比较
AssetBundle | | Resources | |
---|
资源可分布在多个包中 | | 所有资源打包成一个大包 | | 存储位置自定义灵活 | | 必须存放在Resources目录下 | | 压缩方式灵活(LZMA,LZ4) | | 资源全部会压缩成二进制 | | 支持后期进行动态更新 | | 打包后资源只读无法动态更改 | | | | | |
AssetBundle的特性
-
AB包可以存储绝大部分Unity资源但无法直接存储C#脚本,所以代码的热更新需要使用Lua或者存储编译后的DLL文件。 -
AB包不能重复进行加载,当AB包已经加载进内存后必须卸载后才能重新加载。 -
多个资源分布在不同的AB包可能会出现一个预制体的贴图等部分资源不在同一个包下,直接加载会出现部分资源丢失的情况,即AB包之间是存在依赖关系的,在加载当前AB包时需要一并加载其所依赖的包。 -
打包完成后,会自动生成一个主包(主包名称随平台不同而不同),主包的manifest下会存储有版本号、校验码(CRC)、所有其它包的相关信息(名称、依赖关系)
AssetBundle全打包流程
本次主要介绍Unity官方提供的AB包管理插件AssetBundle Browser 进行打包
1、AssetBundleBrowser插件的获取
-
Unity 2019版本可以直接在Windows —> PackageManager里面找到此插件并直接安装 -
2020版本之后或其它版本可能在1方法中找不到此插件,可以通过去github搜索下载压缩包,下载地址https://github.com/Unity-Technologies/AssetBundles-Browser
将下载后的安装包解压到Unity工程的Packages文件夹下 (一定要解压)
2、AssetBundleBrowser面板的使用
-
正确获取到并安装完插件后,通过 Windows ----> AssetBundle Browser下打开AB包管理面板 一共有三个面板 -
Configure面板 :能查看当前AB包及其内部资源的基本情况(大小,资源,依赖情况等) -
Build面板:负责AssetBundle打包的相关设置 按Build即可进行打包 三种压缩方式
- NoCompression:不压缩,解压快,包较大,不建议使用。
- LZMA: 压缩最小,解压慢,用一个资源要解压包下所有资源。
- LZ4: 压缩稍大,解压快,用什么解压什么,内存占用低,更建议使用。
一般需要进行更改的设置即为图中勾选的相关选项设置。 -
Inspect面板:主要用来查看已经打包后的AB包文件的一些详细情况(大小,资源路径等)
3、设置资源所属的AssetBundle包
在需要打包的资源的Inspector面板下方即可选择其应放在哪个AB包下,也可通过New新建AB包将资源放入,放入后再次Build打包即可将此资源打入相应的AB包中。
AssetBundle管理器
? 利用AssetBundleBrowser可以轻松实现AB包的打包工作,更重要的是如何将AB包中的资源加载出来并使用它们,Unity已经提供了一组API能够实现AB包的加载和资源的加载。
AB包管理器的主要需求:加载指定AB包下的指定资源,基本步骤如下:
- 加载资源所在AB包及其的所有依赖包(根据主包的manifest信息找依赖包名称)
- 从AB包中加载指定的资源(根据名称,类型)
- 不再使用时卸载已经加载了的AB包
主要相关API
AssetBundle ab = AssetBundle.LoadFromFile(path);
T obj = ab.LoadAsset<T>(ResourceName);
Object obj = ab.LoadAsset(ResourceName);
Object obj = ab.LoadAsset(ResourceName,Type);
T obj = ab.LoadAssetAsync<T>(resName);
Object obj = ab.LoadAssetAsync(resName);
Object obj = ab.LoadAssetAsync(resName,Type);
ab.UnLoad(false);
AssetBundle.UnloadAllAssetBundles(false);
下面是AB包管理器的详细代码和注释,里面涉及到的单例模式内容后面会简单进行介绍。
ABManager.cs
using System;
using System.Net.Mime;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Common
{
public class ABManager : MonoSingleton<ABManager>
{
private Dictionary<string, AssetBundle> abCache;
private AssetBundle mainAB = null;
private AssetBundleManifest mainManifest = null;
private string basePath { get
{
#if UNITY_EDITOR || UNITY_STANDALONE
return Application.dataPath + "/StreamingAssets/";
#elif UNITY_IPHONE
return Application.dataPath + "/Raw/";
#elif UNITY_ANDROID
return Application.dataPath + "!/assets/";
#endif
}
}
private string mainABName
{
get
{
#if UNITY_EDITOR || UNITY_STANDALONE
return "StandaloneWindows";
#elif UNITY_IPHONE
return "IOS";
#elif UNITY_ANDROID
return "Android";
#endif
}
}
protected override void Init()
{
base.Init();
abCache = new Dictionary<string, AssetBundle>();
}
private AssetBundle LoadABPackage(string abName)
{
AssetBundle ab;
if (mainAB == null)
{
mainAB = AssetBundle.LoadFromFile(basePath + mainABName);
mainManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
string[] dependencies = mainManifest.GetAllDependencies(abName);
for (int i = 0; i < dependencies.Length; i++)
{
if (!abCache.ContainsKey(dependencies[i]))
{
ab = AssetBundle.LoadFromFile(basePath + dependencies[i]);
abCache.Add(dependencies[i], ab);
}
}
if (abCache.ContainsKey(abName)) return abCache[abName];
else
{
ab = AssetBundle.LoadFromFile(basePath + abName);
abCache.Add(abName, ab);
return ab;
}
}
#region 同步加载的三个重载
public T LoadResource<T>(string abName,string resName)where T:Object
{
AssetBundle ab = LoadABPackage(abName);
return ab.LoadAsset<T>(resName);
}
public Object LoadResource(string abName,string resName)
{
AssetBundle ab = LoadABPackage(abName);
return ab.LoadAsset(resName);
}
public Object LoadResource(string abName, string resName,System.Type type)
{
AssetBundle ab = LoadABPackage(abName);
return ab.LoadAsset(resName,type);
}
#endregion
public void LoadResourceAsync(string abName,string resName, System.Action<Object> finishLoadObjectHandler)
{
AssetBundle ab = LoadABPackage(abName);
StartCoroutine(LoadRes(ab,resName,finishLoadObjectHandler));
}
private IEnumerator LoadRes(AssetBundle ab,string resName, System.Action<Object> finishLoadObjectHandler)
{
if (ab == null) yield break;
AssetBundleRequest abr = ab.LoadAssetAsync(resName);
yield return abr;
finishLoadObjectHandler(abr.asset);
}
public void LoadResourceAsync(string abName, string resName,System.Type type, System.Action<Object> finishLoadObjectHandler)
{
AssetBundle ab = LoadABPackage(abName);
StartCoroutine(LoadRes(ab, resName,type, finishLoadObjectHandler));
}
private IEnumerator LoadRes(AssetBundle ab, string resName,System.Type type, System.Action<Object> finishLoadObjectHandler)
{
if (ab == null) yield break;
AssetBundleRequest abr = ab.LoadAssetAsync(resName,type);
yield return abr;
finishLoadObjectHandler(abr.asset);
}
public void LoadResourceAsync<T>(string abName, string resName, System.Action<Object> finishLoadObjectHandler)where T:Object
{
AssetBundle ab = LoadABPackage(abName);
StartCoroutine(LoadRes<T>(ab, resName, finishLoadObjectHandler));
}
private IEnumerator LoadRes<T>(AssetBundle ab, string resName, System.Action<Object> finishLoadObjectHandler)where T:Object
{
if (ab == null) yield break;
AssetBundleRequest abr = ab.LoadAssetAsync<T>(resName);
yield return abr;
finishLoadObjectHandler(abr.asset as T);
}
public void UnLoad(string abName)
{
if(abCache.ContainsKey(abName))
{
abCache[abName].Unload(false);
abCache.Remove(abName);
}
}
public void UnLoadAll()
{
AssetBundle.UnloadAllAssetBundles(false);
abCache.Clear();
mainAB = null;
mainManifest = null;
}
}
}
上面管理器继承的脚本单例类如下
MonoSingleton.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Common
{
public class MonoSingleton<T> : MonoBehaviour where T:MonoSingleton<T>
{
private static T instance;
public static T Instance
{
get
{
if (instance != null) return instance;
instance = FindObjectOfType<T>();
if (instance == null)
{
new GameObject("Singleton of "+typeof(T)).AddComponent<T>();
}
else instance.Init();
return instance;
}
}
private void Awake()
{
instance = this as T;
Init();
}
protected virtual void Init()
{
}
}
}
除了上面的脚本单例类 单例模式还有另外两种无需继承MonoBehaviour的纯C#单例模式 有兴趣的读者可自行了解 单例模式下的饿汉和懒汉模式。
|