Unity中的AssetBundle
Posted 流浪打工人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity中的AssetBundle相关的知识,希望对你有一定的参考价值。
AssetBundle的概念
AssetBundle又称AB包,是Unity提供的一种用于存储资源的资源压缩包,是对Unity 初始Resources的一种扩展;一般使用的策略是把必须的资源和不需要更新的资源放在Resources文件夹下,其他的资源放在AssetBundle下面。
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 进行打包
- AssetBundleBrowser插件的获取 Unity 2019版本可以直接在Windows —>PackageManager里面找到此插件并直接安装
- 2020版本之后或其它版本可能在1方法中找不到此插件,可以通过去github搜索下载压缩包,将下载后的安装包解压到Unity工程的Packages文件夹下 (一定要解压)
AssetBundleBrowser面板的使用
正确获取到并安装完插件后,通过 Windows ----> AssetBundle Browser下打开AB包管理面板 一共有三个面板
Configure面板 :能查看当前AB包及其内部资源的基本情况(大小,资源,依赖情况等)
Build面板:负责AssetBundle打包的相关设置 按Build即可进行打包
三种压缩方式
NoCompression:不压缩,解压快,包较大,不建议使用。
LZMA: 压缩最小,解压慢,用一个资源要解压包下所有资源。
LZ4: 压缩稍大,解压快,用什么解压什么,内存占用低,一般建议使用这种。
一般需要进行更改的设置即为图中勾选的相关选项设置。
Inspect面板:主要用来查看已经打包后的AB包文件的一些详细情况(大小,资源路径等)
3、设置资源所属的AssetBundle包
在需要打包的资源的Inspector面板下方即可选择其应放在哪个AB包下,也可通过New新建AB包将资源放入,放入后再次Build打包即可将此资源打入相应的AB包中。
AssetBundle加载和卸载的代码示例
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ABLoadTest : MonoBehaviour
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
void Start()
abCache = new Dictionary<string, AssetBundle>();
var ab = LoadABTest();
GameObject model = ab.LoadAsset<GameObject>("Cube");
var b = Instantiate<GameObject>(model);
// dosomething
AssetBundle LoadABTest()
AssetBundle ab;
string abName = "3dmodel.first";
if (mainAB == null)
mainAB = AssetBundle.LoadFromFile(basePath + "StandaloneWindows");
mainManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//根据manifest获取所有依赖包的名称 固定API
string[] dependencies = mainManifest.GetAllDependencies(abName);
//循环加载所有依赖包
for (int i = 0; i < dependencies.Length; i++)
//如果不在缓存则加入
if (!abCache.ContainsKey(dependencies[i]))
//根据依赖包名称进行加载
ab = AssetBundle.LoadFromFile(basePath + dependencies[i]);
//注意添加进缓存 防止重复加载AB包
abCache.Add(dependencies[i], ab);
//加载目标包 -- 同理注意缓存问题
if (abCache.ContainsKey(abName))
Debug.Log($"have load abName");
return abCache[abName];
else
ab = AssetBundle.LoadFromFile(basePath + abName);
abCache.Add(abName, ab);
Debug.Log($"new load abName");
return ab;
// Update is called once per frame
void Update()
if(Input.GetKeyDown(KeyCode.A)) // 同步加载
var ab = LoadABTest();
GameObject model = ab.LoadAsset<GameObject>("Cube");
var b = Instantiate<GameObject>(model);
else if (Input.GetKeyDown(KeyCode.S))// 异步加载
var ab = LoadABTest();
StartCoroutine(LoadResAsyncTest(ab));
else if (Input.GetKeyDown(KeyCode.D))// 单个卸载
UnLoad("3dmodel.first");
Debug.Log("have UnLoadAll 3dmodel.first");
else if (Input.GetKeyDown(KeyCode.F))// 全部卸载
UnLoadAll();
Debug.Log("have UnLoadAll");
private IEnumerator LoadResAsyncTest(AssetBundle ab)
if (ab == null) yield return null;
var model1 = ab.LoadAssetAsync<GameObject>("Cube");
yield return model1;
var async_model = Instantiate((GameObject)model1.asset);
// dosomething
//====================AB包的两种卸载方式=================
//单个包卸载
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;
参考资料:Unity中AB包详解(超详细,特性,打包,加载,管理器)
GJM: Unity3D AssetBundle 手记
Object obj = AssetDatabase.LoadMainAssetAtPath("Assets/Test.png"); BuildPipeline.BuildAssetBundle(obj, null, Application.streamingAssetsPath + "/Test.assetbundle", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.StandaloneWindows);
WWW w = new WWW("file://" + Application.streamingAssetsPath + "/Test.assetbundle"); myTexture = w.assetBundle.Load("Test");
Directory.GetFiles("Assets/MyDirs", "*.*", SearchOption.TopDirectoryOnly); Directory.GetDirectories(Application.dataPath + "/Resources/Game", "*.*", SearchOption.AllDirectories);
GetFiles搜集到的资源路径可以被加载,加载之前需要判断一下后缀是否.meta,如果是则不取出该资源,然后将路径转换至Assets开头的相对路径,然后加载资源
string newPath = "Assets" + mypath.Replace(Application.dataPath, ""); newPath = newPath.Replace("\\\\", "/"); Object obj = AssetDatabase.LoadMainAssetAtPath(newPath);
string.Format("file://{0}/{1}", Application.streamingAssetsPath, bundlePath);
string.Format("jar:file://{0}!/assets/{1}", Application.dataPath, bundlePath);
IEnumerator LoadBundle(string url) { WWW www = = new WWW(url); yield return www; if (www.error != null) { Debug.LogError("Load Bundle Faile " + url + " Error Is " + www.error); yield break; } //Do something ... }
//将所有对象加载资源 Object[] objs = bundle.LoadAll(); //加载名为obj的资源 Object obj = bundle.Load("obj"); //异步加载名为resName,类型为type的资源 AssetBundleRequest res = bundle.LoadAsync(resName, type); yield return res; var obj = res.asset;
//需要先实例化 GameObject obj = GameObject.Instantiate(bundle.Load("MyPrefab")) as GameObject;
bundle.Load("MyPrefab", typeof(GameObject))
string path = Application.streamingAssetsPath; BuildPipeline.PushAssetDependencies(); BuildTarget target = BuildTarget.StandaloneWindows; BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/UI_tck_icon_houtui.png"), null, path + "/package1.assetbundle", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, target); BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/New Material.mat"), null, path + "/package2.assetbundle", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, target); BuildPipeline.PushAssetDependencies(); BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Cube.prefab"), null, path + "/package3.assetbundle", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.StandaloneWindows); BuildPipeline.PopAssetDependencies(); BuildPipeline.PushAssetDependencies(); BuildPipeline.BuildAssetBundle(AssetDatabase.LoadMainAssetAtPath("Assets/Cubes.prefab"), null, path + "/package4.assetbundle", BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets | BuildAssetBundleOptions.DeterministicAssetBundle, target); BuildPipeline.PopAssetDependencies(); BuildPipeline.PopAssetDependencies();
[Serializable] public class ResConfigData { public string ResName; //资源名字 public string BundleName; //包名字 public string Path; //资源路径 public int Vesrion; //版本号 } [System.Serializable] public class ResConfig : ScriptableObject { public List<ResConfigData> ConfigDatas = new List<ResConfigData>(); }
ResConfig obj = (ResConfig)AssetDatabase.LoadAssetAtPath(path, typeof(ResConfig)); if (obj == null) { obj = ScriptableObject.CreateInstance<ResConfig>(); AssetDatabase.CreateAsset(obj, path); }
EditorUtility.SetDirty(obj);
以上是关于Unity中的AssetBundle的主要内容,如果未能解决你的问题,请参考以下文章
小松教你手游开发unity实用技能unity ios快捷打包
使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试(续)
如何给你的手游进行快速测试:Unity5和WebGL移植指南
UWA发布 | 2017 Unity手游体检蓝皮书 — ARPG篇