★AssetBundle:
准备:1.在Project视图中建一个Editor的文件夹,进行预编译,在其内建一个脚本用来做预编译操作;
2.创建几个预设体实现预编译后带来的功能;
3.创建一个文件夹用来存储生成的包;
4.引用命名空间using UnityEditor;
实例学习AssetBundle:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class CreateAssetBundle : Editor
{
/// <summary>
/// 第一种打包方式:选择的多个物体都分别生成一个单独的包
/// </summary>
[MenuItem("AB/BuildAll")]
static void BuildAllAB()
{
//打包方式单一
//具有相同名字的资源,会打到一个Bundle当中
//.mainfest是bundle的配置文件
//每一个bundle都有这样一个配置文件,通过它可以查看每个bundle中的版本、hash、资源和依赖等信息
//还有一个全局的总的配置文件(与打包的输出目录重名),通过总的配置文件,可以查看一共有多少个bundle包和每个bundle的信息
/*
* 1.资源没变的bundle,不会触发重新打包,及时bundle删除了,也不会重新打包,但是配置文件丢失,会触发重新打包
* 2.可以强制进行重新打包
* BuildPipeline.BuildAssetBundles("Assets/ABS",BuildAssetBundleOptions.ForceRebuildAssetBundle ,BuildTarget.StandaloneWindows);
*/
/*
* 1.不压缩,资源最大,但访问速度读取最快
* BuildPipeline.BuildAssetBundles("Assets/ABS",BuildAssetBundleOptions.UncompressedAssetBundle ,BuildTarget.StandaloneWindows);
* 2.默认采用LZMA形式进行压缩,压缩比较大,访问速度较慢
* BuildPipeline.BuildAssetBundles("Assets/ABS",BuildAssetBundleOptions.None ,BuildTarget.StandaloneWindows);
* 3.采用LZ4进行压缩,基于Chunk算法,压缩比较小,访问速度较快
* BuildPipeline.BuildAssetBundles("Assets/ABS",BuildAssetBundleOptions.ChunkBasedCompression ,BuildTarget.StandaloneWindows);
*/
BuildPipeline.BuildAssetBundles("Assets/ABS",BuildAssetBundleOptions.None ,BuildTarget.StandaloneWindows);
AssetDatabase.Refresh();
Debug.Log(111);
}
/// <summary>
/// 第二种打包方式:选择的多个物体都生成于一个包内
/// </summary>
[MenuItem("AB/BuildABSecelt")]
static void BuildABSecelt()
{
AssetBundleBuild[] abbs = new AssetBundleBuild[1];
abbs[0].assetBundleName = "Custom";
Object[] selects = Selection.objects;
string[] selectnames = new string[selects.Length];
for (int i = 0; i < selects.Length; i++)
{
//获取选择到资源的路径
selectnames[i] = AssetDatabase.GetAssetPath(selects[i]);
}
abbs[0].assetNames = selectnames;
//该方法不再依靠Unity编辑器中的AssetBundleName进行打包,而是通过纯代码的方式进行打包
BuildPipeline.BuildAssetBundles("Assets/ABS", abbs, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
//更新Project视图
AssetDatabase.Refresh();
}
/// <summary>
/// 选择某个物体或多个物体,把它(它们)的assetBundleName设置为自己预设体的名称
/// </summary>
[MenuItem("AB/SetABName")]
static void SetABName()
{
Object[] selects = Selection.objects;
foreach (var item in selects)
{
string path = AssetDatabase.GetAssetPath(item);
AssetImporter asset = AssetImporter.GetAtPath(path);
asset.assetBundleName = item.name;
//变体:asset.assetBundleVariant
asset.SaveAndReimport();
}
AssetDatabase.Refresh();
}
}
该脚本实现的效果:
图1:
图2:
图3:
★使用AssetBundle的好处:
☆没有打包材质时的所占空间大小:
☆打包材质后所占的空间大小:
☆比较:相同个体和相同材质的情况下:
1.没有打包材质的空间大小:
2.打包材质的空间大小:
总结:若是n个相同的物体单独打包不打包材质,就会打包n个材质
如果打包n个材质并打包材质,就会减少n-1个材质的空间大小
加载AssetBundle:
/************************* * Title: "" * Function: * -1.直接加载 * (1) LoadFromFile,直接从文件加载,这是最快的加载方式,在后台将AB解压后加载AB对象,分为异步和同步; * (2) LoadFromMemory,从内存中加载,从内存中获取AB的二进制数据,然后再去创建AB对象,分为异步和同步;一般用在加密的数据上 * 2.通过WWW类下载: * (1)WWW www=new WWW(url); * (2)WWW.LoadFromCachorDownload(); * 实际使用环境的分析: (1) 随游戏一同发布的AssetBundle(一般位于StreamingAssets文件夹中): 在打AssetBundle包时,使用LZ4压缩格式进行打包(开启BuildAssetBundleOptions.ChunkBasedCompression即可)。 在运行时需要加载AssetBundle对象时,使用LoadFromFile方法进行加载。 这样做的好处是:即可以将AssetBundle文件压缩,又可以兼顾加载速度,且节约内存。 (2) 作为更新包,需要从服务端下载的AssetBundle: 在打AssetBundle包时,使用默认的LZMA格式压缩。 使用WWW.LoadFromCacheOrDownload 方法下载并缓存AssetBundle包文件。 这样做的好处是:获得了最大的压缩率,在下载过程中可以减少数据传输量。同时,在本地磁盘创建缓存之后, 又可以兼顾之后的加载速度,且节约内存。 (3) 进行加密的AssetBundle: 在打AssetBundle包时,使用LZ4压缩格式进行打包(开启BuildAssetBundleOptions.ChunkBasedCompression即可)。 在运行时需要加载AssetBundle对象时,使用LoadFromMemory方法进行加载。(这也是从内存中使用流数据加载AssetBundle 对象的仅有的使用场景。) (4) 需要自己压缩的AssetBundle: 我们自己也可以使用第三方库或工具对生成的AssetBundle包文件进行压缩,如果需要这样做,则我们最好不要再使用 Unity3D对AssetBundle进行压缩,因此在打包时选择开启BuildAssetBundleOptions.UncompressedAssetBundle。 在运行时需要加载AssetBundle对象时,使用LoadFromFileAsync方法进行异步加载。 * Author: v. * Date: 2018.1.9 * Version: 1.0 * Description: * *************************/ using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class LoadTest : MonoBehaviour { private string AssetBundleSavePath; //存放AssetBundle的目录 private string mURL; //要加载的AssetBundle的绝对目录,包含文件本身 void Awake () { //打包存放的文件夹路径: AssetBundleSavePath = Application.dataPath + "/ABS"; //要加载的AssetBundle的绝对路径(资源对象的路径) mURL = AssetBundleSavePath + "/prefabs/cube1"; } private void Start() { //普通方法: //LoadAssetBundle0101(); //LoadAssetBundle0102(); //www方法:本地连接需要在目录前添加 "file://" /* 协程的调用方法一:*/ //只打包prefab: //StartCoroutine("LoadAssetBundle020101", "file://"+mURL); //StartCoroutine("LoadAssetBundle020102"); //打包prefab、material、texture: //StartCoroutine("LoadAssetBundle0102"); /* 协程的调用方法二:*/ //只打包Prefab //StartCoroutine(LoadAssetBundle020101("file://" + mURL)); //StartCoroutine(LoadAssetBundle020102()); //StartCoroutine(LoadAssetBundle0202("file://" + mURL)); //通过总的AssetBundle的Manifest文件加载: StartCoroutine(LoadManifestFile()); } /// <summary> /// 普通方法1:同步 /// </summary> private void LoadAssetBundle0101() { //加载得到AssetBundle资源对象,可以理解为将AssetBundle资源对象加载到内存当中 AssetBundle ab = AssetBundle.LoadFromFile(mURL); //加载得到的AssetBundle中的资源 //object obj = ab.LoadAsset("Cube1") as GameObject; //加载指定的资源 //GameObject obj = ab.LoadAsset<GameObject>(); //加载所有资源 GameObject[] objs = ab.LoadAllAssets<GameObject>(); print(objs.Length); //克隆资源对象,实现在Game视图的实例化 Instantiate(objs[0]); //打包相同材质的不同prefab,为了更好的看出实例化出的个对象,变换一下位置 Instantiate(objs[1],Vector3.one,Quaternion.identity); //是否卸载AB所有的资源:fasle只卸载当前的资源,true,卸载所有的资源,会导致游戏对象丢失材质 ab.Unload(false); } /// <summary> /// 普通方法1: 异步 /// </summary> IEnumerator LoadAssetBundle0102() { //直接异步加载资源的绝对路径 AssetBundleCreateRequest abcr = AssetBundle.LoadFromFileAsync(mURL); //等待加载完毕: yield return abcr; //ab接收加载abcr的资源 AssetBundle ab = abcr.assetBundle; //1.直接加载ab的资源 //GameObject obj = ab.LoadAsset<GameObject>("Cube1"); //2.异步加载ab内所有的资源 AssetBundleRequest abr = ab.LoadAllAssetsAsync<GameObject>(); //等待加载完毕: yield return abr; //加载资源对象: UnityEngine.Object obj = abr.asset; Instantiate(obj); ab.Unload(false); } /// <summary> /// WWW1的有参方法: /// </summary> /// <param name="url"></param> /// <returns></returns> IEnumerator LoadAssetBundle020101(string url) { //1. 需要销毁www //WWW www = new WWW(mURL); //yield return www; //AssetBundle ab = www.assetBundle; //GameObject obj = ab.LoadAsset<GameObject>("Cube"); //Instantiate(obj); //ab.Unload(false); //www.Dispose(); //2.使用using不需要销毁www using (WWW www = new WWW(url)) { yield return www; //打包prefab、material、texture的时候: //需要下载本地链接的material的资源: 否则会丢失材质 WWW ww = new WWW("file://" + AssetBundleSavePath + "/materials/material_01"); AssetBundle ab = www.assetBundle; AssetBundle abMaterial_01 = ww.assetBundle; //运用直接加载的方法加载texture的资源: AssetBundle abTextures_01 = AssetBundle.LoadFromFile(AssetBundleSavePath + "/textures/textures_01"); //把游戏对象加载到内存,方便克隆: GameObject obj = ab.LoadAsset<GameObject>("Cube"); //obj包含了prefab、material、texture的所有资源,卸载obj就是卸载了所有的资源 Instantiate(obj); ab.Unload(false); ww.Dispose(); } } /// <summary> /// WWW1的无参方法 /// </summary> /// <returns></returns> IEnumerator LoadAssetBundle020102() { //1. //WWW www = new WWW("file://"+mURL); //yield return www; //AssetBundle ab = www.assetBundle; //GameObject obj = ab.LoadAsset<GameObject>("Cube"); //Instantiate(obj); //ab.Unload(false); //www.Dispose(); //2.using using (WWW www = new WWW("file://" + mURL)) { yield return www; AssetBundle ab = www.assetBundle; GameObject obj = ab.LoadAsset<GameObject>("Cube"); Instantiate(obj); ab.Unload(false); } } /// <summary> /// WWW2: /// </summary> /// <param name="url"></param> /// <returns></returns> IEnumerator LoadAssetBundle0202(string url) { //0表示该prefab第一次打包的版本,如果在没有删除打包资源的情况下改变某值则版本会变成1 //继续使用0也能够实例化到Game视图中,但是并不是改变某值后的版本,只是存在于内存中的版本0 //因此想要实现改变某值后的资源展现在实例化对象中,就要把0改为1; using (WWW www = WWW.LoadFromCacheOrDownload(url, 0)) { yield return www; AssetBundle ab = www.assetBundle; GameObject obj = ab.LoadAsset<GameObject>("Cube"); Instantiate(obj); ab.Unload(false); } } IEnumerator LoadManifestFile() { //直接加载总的 AssetBundle 的 Manifest 的资源: AssetBundle ab = AssetBundle.LoadFromFile(AssetBundleSavePath + "/ABS"); //1.加载单个资源,"AssetBundleManifest"不是某个文件,而是规定,无论加载哪个资源都这么写 //AssetBundleManifest abmf = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); //2.加载全部资源:总的AssetBundle资源配置文件只有一个,所以只要加载成功,那么Lenght就是1 AssetBundleManifest[] abms = ab.LoadAllAssets<AssetBundleManifest>(); //测试是否只有一个文件,也同时测试了,是否加载成功 if (abms.Length == 1) { //得到所有的AssetBundle资源的名字 string[] allABName = abms[0].GetAllAssetBundles(); //创建一个Hash128的数组接收所有资源的HashID Hash128[] allHS = new Hash128[allABName.Length]; //遍历所有资源的名称 for (int i = 0; i < allABName.Length; i++) { //将所有资源的HashID添加到allHS数组中 allHS[i] = abms[0].GetAssetBundleHash(allABName[i]); //打印所有的资源名称以及HashID; //也可以不用创建这个数组,那这个只能实现一个功能,仅仅将资源名称和HashID打印出来而已。 //创建数组,可以通过存储在数组内的HashID搜索以及实现你想要实现的功能。 print(allABName[i] + " " + allHS[i]); } ab.Unload(false); } yield return null; } }
实例:减少代码的冗杂,提高代码的重用性:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class LoadTT : MonoBehaviour { string mPathAB; AssetBundleManifest mABMF; private void Awake() { mPathAB = Application.dataPath + "/ABS"; } private void Start() { StartCoroutine("LoadABMF"); } private void Update() { if (Input.GetKeyDown(KeyCode.A)) { StartCoroutine(LoadSource(mABMF, "prefabs/cube1.ab", "Cube1")); } } /// <summary> /// 得到配置文件 /// </summary> /// <returns></returns> IEnumerator LoadABMF() { WWW absWWW = WWW.LoadFromCacheOrDownload ("file://" + mPathAB + "/ABS", 2); yield return absWWW; // 判断absWWW有没有错误 if (!string.IsNullOrEmpty(absWWW.error)) { Debug.Log(absWWW.error); } else { // 总的AB AssetBundle abs = absWWW.assetBundle ; // 总的配置文件 mABMF = abs.LoadAsset<AssetBundleManifest>("AssetBundleManifest"); abs.Unload(false); } } /// <summary> /// /// </summary> /// <param name="manifest">配置文件</param> /// <param name="abName">要加载的AB名字,需要扩展名</param> IEnumerator LoadSource(AssetBundleManifest manifest, string abName, string sourceName) { // 得到所有依赖的AB的名字 string[] dps = manifest.GetAllDependencies(abName); #if UNITY_EDITOR foreach (string item in dps) { print(item); } #endif //AssetBundle[] abDps = new AssetBundle[dps.Length]; List<AssetBundle> abDpsList = new List<AssetBundle>(); for (int i = 0; i < dps.Length; i++) { string urlDpsFile = "file://" + mPathAB + "/" + dps[i]; WWW wwwDps = WWW.LoadFromCacheOrDownload(urlDpsFile , manifest.GetAssetBundleHash(dps[i])); yield return wwwDps; AssetBundle abDps = wwwDps.assetBundle; abDpsList.Add(abDps); } string url = "file://" + mPathAB + "/" + abName; WWW abWWW = WWW.LoadFromCacheOrDownload(url , manifest.GetAssetBundleHash(abName)); yield return abWWW; if (!string.IsNullOrEmpty(abWWW.error )) { print(abWWW.error ); } else { AssetBundle ab = abWWW.assetBundle ; GameObject obj = ab.LoadAsset<GameObject>(sourceName); if (obj != null) { Instantiate(obj); } ab.Unload(false); } // 卸载所有的AB foreach (AssetBundle item in abDpsList) { item.Unload(false); } } }