AssetBundle应用
按照步骤
命名
使用AssetBundle最先注意到的便是资源的命名。
最普通的命名方式,便是:
如图上标识的地方直接手动输入,命名。
但如果资源量较大时,这种方式会很枯燥、繁琐,而且自己想对应的名字,也是件很头疼的事情。所以,我便使用了代码的方式,实现了批量的资源命名。
using System.IO;
using UnityEditor;
public class AutoName : AssetPostprocessor
{
/// <summary>
/// 该函数在所有资源被动的时候会自动调用,对资源进行处理------UnityEditor名字空间中
/// 为导入的和移动的自动加上assetBundle的名字
/// </summary>
/// <param name="importedAssets">导入的资源</param>
/// <param name="deletedAssets">删除的资源</param>
/// <param name="movedAssets">移动的资源</param>
/// <param name="movedFromAssetPaths">从资源路径中的移动</param>
static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
{
foreach (var str in importedAssets)
{
if (!str.EndsWith(".cs"))
{
AssetImporter importer = AssetImporter.GetAtPath(str);//AssetImporter资源导入
if (str.StartsWith("Assets/Editor/AssetBundle/"))
{
//如果在指定的文件夹下,名字是将路径去除扩展名
if ("" != Path.GetDirectoryName(str.Remove(0, 26)))
{//不是第一层,将“路径的目录信息”加上“文件名(不包括扩展名)”
importer.assetBundleName = Path.GetDirectoryName(str.Remove(0, 26)) + '\\' + Path.GetFileNameWithoutExtension(str);
}
else
{//如果是第一层,则直接获取路径中的文件名(不包括扩展名)
importer.assetBundleName = Path.GetFileNameWithoutExtension(str);
}
importer.assetBundleVariant = "unity3d";//设置扩展名
}
else
{
//如果不是在指定的文件夹下,名字和扩展名皆为空
importer.assetBundleName = "";
if (importer.assetBundleVariant != "")
importer.assetBundleVariant = "";
}
}
}
foreach (var str in movedAssets)
{
if (!str.EndsWith(".cs"))
{
AssetImporter importer = AssetImporter.GetAtPath(str);
if (str.StartsWith("Assets/Editor/AssetBundle/"))
{
if ("" != Path.GetDirectoryName(str.Remove(0, 26)))
{
importer.assetBundleName = Path.GetDirectoryName(str.Remove(0, 26)) + '\\' + Path.GetFileNameWithoutExtension(str);
}
else
{
importer.assetBundleName = Path.GetFileNameWithoutExtension(str);
}
importer.assetBundleVariant = "unity3d";
}
else
{
importer.assetBundleName = "";
if (importer.assetBundleVariant != "")
importer.assetBundleVariant = "";
}
}
}
}
}
我这里规定了,一定的路径下(Assets/Editor/AssetBundle/,及项目的资源文件夹Assets下的Editor文件夹下的AssetBundle文件夹中的资源,因为Editor文件夹下的资源,在打包时是不会被打包进项目包的),除了csharp脚本外的资源,根据一定的规则,按照路径来进行命名。具体的规则和说明,在代码中都有较为详细的注视,这里就不做过多的介绍。
打包资源
将所需的资源全部命名完之后,便可以开始进行打包。
先要编辑打包所需的代码:
using System.IO;
using UnityEditor;
using UnityEngine;
public class BuildAssetBundle : Editor
{
[MenuItem("BuildAssetbundle/BuildAllResource")]
static void Build()
{
//先删除之前保存的AssetBundle文件及文件夹
DirectoryInfo directory = new DirectoryInfo(Application.dataPath + "/StreamingAssets/AssetBundle");
if (directory.Exists)
directory.Delete(true);
//然后从新创建一个空的文件夹
Directory.CreateDirectory(Application.dataPath + "/StreamingAssets/AssetBundle");
//打包组成依赖关系
AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath + "/AssetBundle",// /../是在当前目录下返回上一级目录,Application.dataPath是当前项目的Asset文件夹
BuildAssetBundleOptions.UncompressedAssetBundle,//非压缩形式
BuildTarget.Android);//BuildTarget.StandaloneWindows64
}
}
首先“[MenuItem(“BuildAssetbundle/BuildAllResource”)]”是为了在Unity的工具栏创建按钮,如图:
然后再代码的最后BuildTarget这个参数为枚举,是用于选择打的包应用的平台。例如,BuildTarget.Android为安卓,BuildTarget.StandaloneWindows64为WIN64等。其余的在代码中有相应的注释,便不再赘述。
之后点击创建的按钮,Unity便开始读条,打包资源。根据我的设置路径,会将打出包来的ab资源,放在StreamingAssets文件夹下的AssetBundle文件夹中,因为StreamingAssets文件夹下的资源数据,会完整的打入Unity项目包中,会比较方便。
加载AssetBundle
再之后,便是在项目中加载AssetBundle的资源了。
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace ZM_Code
{
/// <summary>
/// Assetbundle加载资源类
/// 代码中的注释是用WWW类进行加载的代码
/// </summary>
public class AssetBundleLoadAsset
{
private AssetBundleLoadAsset()
{
m_abmainfpath = m_prefixpath + "Assetbundle";
}
private static AssetBundleLoadAsset m_instance;
public static AssetBundleLoadAsset GetInstance()
{
if (null == m_instance)
m_instance = new AssetBundleLoadAsset();
return m_instance;
}
/// <summary>
/// Assetbundle文件的前缀路径
/// </summary>
#if WINDOWS || UNITY_EDITOR
private string m_prefixpath = Application.streamingAssetsPath + "/Assetbundle/";
#elif ANDROID
private string m_prefixpath = "jar:file://" + Application.dataPath + "!/assets/Assetbundle/";
#endif
/// <summary>
/// Assetbundle依赖的总文件路径
/// </summary>
private string m_abmainfpath;
/// <summary>
/// Assetbundle总依赖的对象
/// </summary>
private AssetBundleManifest m_ABmainf = null;
/// <summary>
/// AssetBundle文件的对象的字典
/// </summary>
private Dictionary<string, Object> m_dicObj = new Dictionary<string, Object>();
/***********************第一种方式:将资源资源一次性全部加载***********************/
/// <summary>
/// 用于方法一:将“依赖总文件”中所有的依赖全部加载到对象池中
/// </summary>
public void Init()
{
AppMain.Instance.StartCoroutine(LoadAllAssetBundle());
}
private float m_progress = 0;
/// <summary>
/// 加载进度
/// </summary>
public float progress
{
get { return m_progress; }
}
private IEnumerator LoadAllAssetBundle()
{
AssetBundle mainBundle = AssetBundle.LoadFromFile(m_abmainfpath);
yield return mainBundle;
m_ABmainf = mainBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
mainBundle.Unload(false);
string[] depends = m_ABmainf.GetAllAssetBundles();
AssetBundle[] dependsAssetbundle = new AssetBundle[depends.Length];
yield return null;
for (int index = 0; index < depends.Length; index++)
{
m_progress = (float)index / (depends.Length * 2);
#region 使用WWW.LoadFromCacheOrDownload
//WWW dewww = WWW.LoadFromCacheOrDownload(m_prefixpath + depends[index], m_ABmainf.GetAssetBundleHash(depends[index]));
//yield return dewww;
//if (!string.IsNullOrEmpty(dewww.error))
//{
// Debug.LogError(dewww.error);
// continue;
//}
//dependsAssetbundle[index] = dewww.assetBundle;
#endregion
dependsAssetbundle[index] = AssetBundle.LoadFromFile(m_prefixpath + depends[index]);
yield return null;
//dewww = null;
}
yield return null;
for (int i = 0; i < depends.Length; i++)
{
m_progress = (i + depends.Length) / (float)(depends.Length * 2);
if (!m_dicObj.ContainsKey(depends[i]))
{//异步加载
AssetBundleRequest abr = dependsAssetbundle[i].LoadAssetAsync(Path.GetFileNameWithoutExtension(depends[i]));
yield return abr;
m_dicObj[depends[i]] = abr.asset;//读取的必须是不带路径的文件名
//Debug.Log(m_dicObj[depends[i]]);
}
//dependsAssetbundle[i].Unload(false);
}
m_progress = 1.0f;
yield break;
}
/// <summary>
/// 用于方法一:获取Object类型的对象(已实例化的)(必须要在Init函数后使用)
/// 为了测试,已在函数中加入了防护,可不用在之前掉取Init函数,但要从外部用协程调用对象池自行获取对象
/// </summary>
public T LoadObject<T>(string path) where T : Object
{
if (m_dicObj.ContainsKey(path))
{
return Object.Instantiate(m_dicObj[path]) as T;
}
return null;
}
/// <summary>
/// 用于方法一:获取非Object类型的对象(必须要在Init函数后使用)
/// 为了测试,已在函数中加入了防护,可不用在之前掉取Init函数,但要从外部用协程调用对象池自行获取对象
/// </summary>
public T LoadNotObject<T>(string path) where T : class
{
if (m_dicObj.ContainsKey(path))
{
return m_dicObj[path] as T;
}
return default(T);
}
#region 第二种方式:加载一个想要的之后便释放
private List<string> m_pathlis = new List<string>();
private string[] m_allDepName = null;
/// <summary>
/// 根据传进来的文件路径名加载所需要的资源,并存入对象池
/// </summary>
private IEnumerator LoadAssetBundle(string path)
{
if (null == m_ABmainf)
{//使用WWW类加载“依赖总文件”
#region 使用WWW.LoadFromCacheOrDownload
//WWW mainwww = new WWW(m_abmainfpath);
//yield return mainwww;
//if (!string.IsNullOrEmpty(mainwww.error))
//{
// Debug.LogError(mainwww.error);
// yield break;
//}
//AssetBundle mainBundle = mainwww.assetBundle;
#endregion
AssetBundle mainBundle = AssetBundle.LoadFromFile(m_abmainfpath);
yield return mainBundle;
m_ABmainf = mainBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
//mainwww = null;
mainBundle.Unload(false);
//记录所有文件名
m_allDepName = m_ABmainf.GetAllAssetBundles();
}
string[] depends = m_ABmainf.GetAllDependencies(path);
AssetBundle[] dependsAssetbundle = new AssetBundle[depends.Length];
yield return null;
for (int index = 0; index < depends.Length; index++)
{
#region 使用WWW.LoadFromCacheOrDownload
//WWW dewww = WWW.LoadFromCacheOrDownload(m_prefixpath + depends[index], m_ABmainf.GetAssetBundleHash(depends[index]));
//yield return dewww;
//if (!string.IsNullOrEmpty(dewww.error))
//{
// Debug.LogError(dewww.error);
// m_pathlis.Remove(path);
// yield break;
//}
//dependsAssetbundle[index] = dewww.assetBundle;
#endregion
dependsAssetbundle[index] = AssetBundle.LoadFromFile(m_prefixpath + depends[index]);
yield return null;
//dewww = null;
}
#region 使用WWW.LoadFromCacheOrDownload
//WWW pawww = WWW.LoadFromCacheOrDownload(m_prefixpath + path, m_ABmainf.GetAssetBundleHash(path));
//yield return pawww;
//if (!string.IsNullOrEmpty(pawww.error))
//{
// Debug.LogError(pawww.error);
// m_pathlis.Remove(path);
// yield break;
//}
//AssetBundle ab = pawww.assetBundle;
#endregion
for (int j = 0; j < m_allDepName.Length; j++)
{
if (path == m_allDepName[j])
{//如果所加载的文件存在,则加载
AssetBundle ab = AssetBundle.LoadFromFile(m_prefixpath + path);
//同步加载
m_dicObj[path] = ab.LoadAsset(Path.GetFileNameWithoutExtension(path));//读取的必须是不带路径的文件名
//pawww = null;
ab.Unload(false);
for (int i = 0; i < dependsAssetbundle.Length; i++)
{
dependsAssetbundle[i].Unload(false);
}
yield break;
}
}
yield break;
}
/// <summary>
/// 用于方法二:判断资源是否在对象池中,将对象池传出,等文件加载完毕自行获取
/// 若不在则根据文件路径名加载,但由于协程加载需要一定的时间,所以需要用协程判断
/// while (!m_dic.ContainsKey(path))
/// yield return null;
/// </summary>
public Dictionary<string, Object> AssetBundleToObject(string path)
{
if (!m_dicObj.ContainsKey(path) && !m_pathlis.Contains(path))
{
AppMain.Instance.StartCoroutine(LoadAssetBundle(path));
m_pathlis.Add(path);
}
return m_dicObj;
}
#endregion
}
}
其中,AppMain为一个MonoBehaviour的单例类,用于使用协程的开启方法。
这里我做了两种不同的加载方式WWW.LoadFromCacheOrDownload和AssetBundle.LoadFromFile,我个人更加偏向AssetBundle类自带的加载方式,网文上说WWW.LoadFromCacheOrDownload会有一定的问题。
同时,我用了两种不同的加载步骤,可根据情况使用。个人更偏向使用第一种方法。
来源:CSDN
作者:天富儿
链接:https://blog.csdn.net/f_957995490/article/details/103695687