资源加载模块
资源加载模块的主要作用是
-
方便统一管理各种资源加载卸载方式(Resources、AB 包、UnityWebRequest、Editor)
-
方便异步加载资源(Resources、AB 包、UnityWebRequest)
资源加载模块的基本原理
将各资源加载模式模块化,比如 Resources 资源管理器、AB 包资源管理器等等,让他们分别管理不同加载方式加载的资源,最终再将他们整合在一起,方便我们在加载资源时根据自己的需求进行选择性使用
Resources资源加载模块
cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// Resources资源加载模块管理器
/// </summary>
public class ResourcesMgr : BaseManager<ResourcesMgr>
{
private ResourcesMgr() { }
/// <summary>
/// 同步加载资源方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="path">资源路径</param>
/// <returns></returns>
public T Load<T>(string path) where T : UnityEngine.Object
{
return Resources.Load<T>(path);
}
/// <summary>
/// 异步加载资源方法
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
/// <param name="path">资源路径</param>
/// <param name="callBack">加载结束后的回调函数</param>
public void LoadAsync<T>(string path,UnityAction<T> callBack) where T: UnityEngine.Object
{
//通过协同程序异步加载资源
MonoMgr.Instance.StartCoroutine(ReallyLoadAsync<T>(path,callBack));
}
private IEnumerator ReallyLoadAsync<T>(string path, UnityAction<T> callBack) where T : UnityEngine.Object
{
//异步加载资源
ResourceRequest resourceRequest = Resources.LoadAsync<T>(path);
//等待资源加载结束后才会执行后面的代码
yield return resourceRequest;
//资源加载结束
callBack?.Invoke(resourceRequest.asset as T);
}
/// <summary>
/// 异步加载资源方法
/// </summary>
/// <param name="path">资源路径</param>
/// <param name="type">资源类型</param>
/// <param name="callBack">加载结束后的回调函数</param>
public void LoadAsync(string path,Type type, UnityAction<object> callBack)
{
//通过协同程序异步加载资源
MonoMgr.Instance.StartCoroutine(ReallyLoadAsync(path,type,callBack));
}
private IEnumerator ReallyLoadAsync(string path, Type type, UnityAction<object> callBack)
{
//异步加载资源
ResourceRequest resourceRequest = Resources.LoadAsync(path,type);
//等待资源加载结束后才会执行后面的代码
yield return resourceRequest;
//资源加载结束
callBack?.Invoke(resourceRequest.asset);
}
/// <summary>
/// 指定卸载资源
/// </summary>
/// <param name="assetToUnload"></param>
public void UnloadAsset(UnityEngine.Object assetToUnload)
{
Resources.UnloadAsset(assetToUnload);
}
/// <summary>
/// 异步卸载对应没有使用的Resources相关的资源
/// </summary>
/// <param name="callBack">回调函数</param>
public void UnloadUnusedAssets(UnityAction callBack)
{
MonoMgr.Instance.StartCoroutine(ReallyUnloadUnusedAssets(callBack));
}
private IEnumerator ReallyUnloadUnusedAssets(UnityAction callBack)
{
AsyncOperation asyncOperation = Resources.UnloadUnusedAssets();
yield return asyncOperation;
//卸载完毕后通知外部
callBack?.Invoke();
}
}
Resources资源加载模块优化
cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public abstract class ResInfoBase { }
/// <summary>
/// 资源信息对象 主要用于存储资源信息 异步加载委托信息 异步加载协程信息
/// </summary>
/// <typeparam name="T"></typeparam>
public class ResInfo<T> : ResInfoBase
{
//资源
public T asset;
//委托 主要用于异步加载结束后 传递资源到外部的委托
public UnityAction<T> callback;
//用于存储异步加载时 开启的协同程序
public Coroutine coroutine;
//判断是否需要移除
public bool isDel = false;
}
/// <summary>
/// Resources资源加载模块管理器
/// </summary>
public class ResourcesMgr : BaseManager<ResourcesMgr>
{
//用于存储加载过的资源或者加载中的资源容器
private Dictionary<string, ResInfoBase> resDic = new Dictionary<string, ResInfoBase>();
private ResourcesMgr() { }
/// <summary>
/// 同步加载资源方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="path">资源路径</param>
/// <returns></returns>
public T Load<T>(string path) where T : UnityEngine.Object
{
ResInfo<T> info;
string resName = path + "_" + typeof(T).Name;
if (!resDic.ContainsKey(resName))
{
//直接同步加载 并且记录资源信息到字典中 方便下次直接取出来用
T res = Resources.Load<T>(path);
info = new ResInfo<T>();
info.asset = res;
resDic.Add(resName, info);
return res;
}
else
{
//取出字典中的记录
info = resDic[resName] as ResInfo<T>;
//异步加载资源没有加载完成
if (info.asset == null)
{ //停止异步加载 直接采用同步的方式加载成功 记录并使用
MonoMgr.Instance.StopCoroutine(info.coroutine);
//直接采用同步的方式加载成功
T res = Resources.Load<T>(path);
//记录
info.asset = res;
//将异步加载结束的委托去执行
info.callback?.Invoke(res);
//加载完毕后清空
info.callback = null;
info.coroutine = null;
//返回加载成功的对象
return res;
}
else
{
return info.asset;
}
}
}
/// <summary>
/// 异步加载资源方法
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
/// <param name="path">资源路径</param>
/// <param name="callBack">加载结束后的回调函数</param>
public void LoadAsync<T>(string path,UnityAction<T> callBack) where T: UnityEngine.Object
{
ResInfo<T> info;
//资源的唯一ID
string resName = path + "_" + typeof(T).Name;
if (!resDic.ContainsKey(resName))
{
//声明一个 资源信息对象
info = new ResInfo<T>();
//将资源记录添加到字典中
resDic.Add(resName, info);
//记录传入的委托函数 一会加载完成后使用
info.callback += callBack;
//通过协同程序异步加载资源
info.coroutine = MonoMgr.Instance.StartCoroutine(ReallyLoadAsync<T>(path));
}
else
{
//从字典中取出资源信息
info = resDic[resName] as ResInfo<T>;
//资源还没加载完
if (info.asset == null)
info.callback += callBack;
else
callBack?.Invoke(info.asset);
}
}
private IEnumerator ReallyLoadAsync<T>(string path) where T : UnityEngine.Object
{
//异步加载资源
ResourceRequest resourceRequest = Resources.LoadAsync<T>(path);
//等待资源加载结束后才会执行后面的代码
yield return resourceRequest;
//资源加载结束
string resName = path + "_" + typeof(T).Name;
if (resDic.ContainsKey(resName))
{
ResInfo<T> info = (resDic[resName] as ResInfo<T>);
//取出资源信息 并记录加载完成的资源
info.asset = resourceRequest.asset as T;
if (info.isDel)
UnloadAsset<T>(path);
else
{
//将加载完成的资源传递出去
info.callback?.Invoke(info.asset);
//加载完毕后清空
info.callback = null;
info.coroutine = null;
}
}
}
/// <summary>
/// 异步加载资源方法
/// </summary>
/// <param name="path">资源路径</param>
/// <param name="type">资源类型</param>
/// <param name="callBack">加载结束后的回调函数</param>
[Obsolete("注意:建议使用泛型方式加载,如果必须得用Type的方式,请不要用泛型方法和type方法加载同类型同名资源")]
public void LoadAsync(string path,Type type, UnityAction<UnityEngine.Object> callBack)
{
ResInfo<UnityEngine.Object> info;
//资源的唯一ID
string resName = path + "_" + type.Name;
if (!resDic.ContainsKey(resName))
{
//声明一个 资源信息对象
info = new ResInfo<UnityEngine.Object>();
//将资源记录添加到字典中
resDic.Add(resName, info);
//记录传入的委托函数 一会加载完成后使用
info.callback += callBack;
//通过协同程序异步加载资源
info.coroutine = MonoMgr.Instance.StartCoroutine(ReallyLoadAsync(path,type));
}
else
{
//从字典中取出资源信息
info = resDic[resName] as ResInfo<UnityEngine.Object>;
//资源还没加载完
if (info.asset == null)
info.callback += callBack;
else
callBack?.Invoke(info.asset);
}
}
private IEnumerator ReallyLoadAsync(string path, Type type)
{
//异步加载资源
ResourceRequest resourceRequest = Resources.LoadAsync(path,type);
//等待资源加载结束后才会执行后面的代码
yield return resourceRequest;
//资源加载结束
string resName = path + "_" + type.Name;
if (resDic.ContainsKey(resName))
{
ResInfo<UnityEngine.Object> info = (resDic[resName] as ResInfo<UnityEngine.Object>);
//取出资源信息 并记录加载完成的资源
info.asset = resourceRequest.asset;
if (info.isDel)
UnloadAsset(path,type);
else
{
//将加载完成的资源传递出去
info.callback?.Invoke(info.asset);
//加载完毕后清空
info.callback = null;
info.coroutine = null;
}
}
}
/// <summary>
/// 指定卸载资源
/// </summary>
/// <param name="assetToUnload"></param>
public void UnloadAsset<T>(string path)
{
//资源的唯一ID
string resName = path + "_" + typeof(T).Name;
//判断是否存在对应资源
if (resDic.ContainsKey(resName))
{
ResInfo<T> resInfo = resDic[resName] as ResInfo<T>;
//资源已经加载结束
if (resInfo.asset != null)
{
resDic.Remove(resName);
Resources.UnloadAsset(resInfo.asset as UnityEngine.Object);
}
else//资源在加载中
{
resInfo.isDel = true;
}
}
}
public void UnloadAsset(string path,Type type)
{
//资源的唯一ID
string resName = path + "_" + type.Name;
//判断是否存在对应资源
if (resDic.ContainsKey(resName))
{
ResInfo<UnityEngine.Object> resInfo = resDic[resName] as ResInfo<UnityEngine.Object>;
//资源已经加载结束
if (resInfo.asset != null)
{
resDic.Remove(resName);
Resources.UnloadAsset(resInfo.asset as UnityEngine.Object);
}
else//资源在加载中
{
resInfo.isDel = true;
}
}
}
/// <summary>
/// 异步卸载对应没有使用的Resources相关的资源
/// </summary>
/// <param name="callBack">回调函数</param>
public void UnloadUnusedAssets(UnityAction callBack)
{
MonoMgr.Instance.StartCoroutine(ReallyUnloadUnusedAssets(callBack));
}
private IEnumerator ReallyUnloadUnusedAssets(UnityAction callBack)
{
AsyncOperation asyncOperation = Resources.UnloadUnusedAssets();
yield return asyncOperation;
//卸载完毕后通知外部
callBack?.Invoke();
}
}
Resources资源加载模块加入引用计数
cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public abstract class ResInfoBase
{
//引用计数
public int refCount;
}
/// <summary>
/// 资源信息对象 主要用于存储资源信息 异步加载委托信息 异步加载协程信息
/// </summary>
/// <typeparam name="T"></typeparam>
public class ResInfo<T> : ResInfoBase
{
//资源
public T asset;
//委托 主要用于异步加载结束后 传递资源到外部的委托
public UnityAction<T> callback;
//用于存储异步加载时 开启的协同程序
public Coroutine coroutine;
//判断是否需要移除
public bool isDel = false;
public void AddRefCount() { ++refCount; }
public void SubRefCount() {
--refCount;
if (refCount < 0)
Debug.LogError("引用计数小于0了,请检查使用和卸载是否配对");
}
}
/// <summary>
/// Resources资源加载模块管理器
/// </summary>
public class ResourcesMgr : BaseManager<ResourcesMgr>
{
//用于存储加载过的资源或者加载中的资源容器
private Dictionary<string, ResInfoBase> resDic = new Dictionary<string, ResInfoBase>();
private ResourcesMgr() { }
/// <summary>
/// 同步加载资源方法
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="path">资源路径</param>
/// <returns></returns>
public T Load<T>(string path) where T : UnityEngine.Object
{
ResInfo<T> info;
string resName = path + "_" + typeof(T).Name;
if (!resDic.ContainsKey(resName))
{
//直接同步加载 并且记录资源信息到字典中 方便下次直接取出来用
T res = Resources.Load<T>(path);
info = new ResInfo<T>();
info.asset = res;
info.AddRefCount();
resDic.Add(resName, info);
return res;
}
else
{
//取出字典中的记录
info = resDic[resName] as ResInfo<T>;
info.AddRefCount();
//异步加载资源没有加载完成
if (info.asset == null)
{ //停止异步加载 直接采用同步的方式加载成功 记录并使用
MonoMgr.Instance.StopCoroutine(info.coroutine);
//直接采用同步的方式加载成功
T res = Resources.Load<T>(path);
//记录
info.asset = res;
//将异步加载结束的委托去执行
info.callback?.Invoke(res);
//加载完毕后清空
info.callback = null;
info.coroutine = null;
//返回加载成功的对象
return res;
}
else
{
return info.asset;
}
}
}
/// <summary>
/// 异步加载资源方法
/// </summary>
/// <typeparam name="T">资源类型</typeparam>
/// <param name="path">资源路径</param>
/// <param name="callBack">加载结束后的回调函数</param>
public void LoadAsync<T>(string path,UnityAction<T> callBack) where T: UnityEngine.Object
{
ResInfo<T> info;
//资源的唯一ID
string resName = path + "_" + typeof(T).Name;
if (!resDic.ContainsKey(resName))
{
//声明一个 资源信息对象
info = new ResInfo<T>();
info.AddRefCount();
//将资源记录添加到字典中
resDic.Add(resName, info);
//记录传入的委托函数 一会加载完成后使用
info.callback += callBack;
//通过协同程序异步加载资源
info.coroutine = MonoMgr.Instance.StartCoroutine(ReallyLoadAsync<T>(path));
}
else
{
//从字典中取出资源信息
info = resDic[resName] as ResInfo<T>;
info.AddRefCount();
//资源还没加载完
if (info.asset == null)
info.callback += callBack;
else
callBack?.Invoke(info.asset);
}
}
private IEnumerator ReallyLoadAsync<T>(string path) where T : UnityEngine.Object
{
//异步加载资源
ResourceRequest resourceRequest = Resources.LoadAsync<T>(path);
//等待资源加载结束后才会执行后面的代码
yield return resourceRequest;
//资源加载结束
string resName = path + "_" + typeof(T).Name;
if (resDic.ContainsKey(resName))
{
ResInfo<T> info = (resDic[resName] as ResInfo<T>);
//取出资源信息 并记录加载完成的资源
info.asset = resourceRequest.asset as T;
if (info.refCount == 0)
UnloadAsset<T>(path,info.isDel,false);
else
{
//将加载完成的资源传递出去
info.callback?.Invoke(info.asset);
//加载完毕后清空
info.callback = null;
info.coroutine = null;
}
}
}
/// <summary>
/// 异步加载资源方法
/// </summary>
/// <param name="path">资源路径</param>
/// <param name="type">资源类型</param>
/// <param name="callBack">加载结束后的回调函数</param>
[Obsolete("注意:建议使用泛型方式加载,如果必须得用Type的方式,请不要用泛型方法和type方法加载同类型同名资源")]
public void LoadAsync(string path,Type type, UnityAction<UnityEngine.Object> callBack)
{
ResInfo<UnityEngine.Object> info;
//资源的唯一ID
string resName = path + "_" + type.Name;
if (!resDic.ContainsKey(resName))
{
//声明一个 资源信息对象
info = new ResInfo<UnityEngine.Object>();
info.AddRefCount();
//将资源记录添加到字典中
resDic.Add(resName, info);
//记录传入的委托函数 一会加载完成后使用
info.callback += callBack;
//通过协同程序异步加载资源
info.coroutine = MonoMgr.Instance.StartCoroutine(ReallyLoadAsync(path,type));
}
else
{
//从字典中取出资源信息
info = resDic[resName] as ResInfo<UnityEngine.Object>;
info.AddRefCount();
//资源还没加载完
if (info.asset == null)
info.callback += callBack;
else
callBack?.Invoke(info.asset);
}
}
private IEnumerator ReallyLoadAsync(string path, Type type)
{
//异步加载资源
ResourceRequest resourceRequest = Resources.LoadAsync(path,type);
//等待资源加载结束后才会执行后面的代码
yield return resourceRequest;
//资源加载结束
string resName = path + "_" + type.Name;
if (resDic.ContainsKey(resName))
{
ResInfo<UnityEngine.Object> info = (resDic[resName] as ResInfo<UnityEngine.Object>);
//取出资源信息 并记录加载完成的资源
info.asset = resourceRequest.asset;
if (info.refCount == 0)
UnloadAsset(path,type,info.isDel,false);
else
{
//将加载完成的资源传递出去
info.callback?.Invoke(info.asset);
//加载完毕后清空
info.callback = null;
info.coroutine = null;
}
}
}
/// <summary>
/// 指定卸载资源
/// </summary>
/// <param name="assetToUnload"></param>
public void UnloadAsset<T>(string path,bool isDel = false,bool isSub = true, UnityAction<T> callback = null)
{
//资源的唯一ID
string resName = path + "_" + typeof(T).Name;
//判断是否存在对应资源
if (resDic.ContainsKey(resName))
{
ResInfo<T> resInfo = resDic[resName] as ResInfo<T>;
if(isSub)
resInfo.SubRefCount();
resInfo.isDel = isDel;
//资源已经加载结束
if (resInfo.asset != null && resInfo.refCount == 0 && resInfo.isDel)
{
resDic.Remove(resName);
Resources.UnloadAsset(resInfo.asset as UnityEngine.Object);
}
else if(resInfo.asset == null)//资源在加载中
{
if(callback != null)
resInfo.callback -= callback;
}
}
}
public void UnloadAsset(string path, Type type, bool isDel = false, bool isSub = true, UnityAction<UnityEngine.Object> callback = null)
{
//资源的唯一ID
string resName = path + "_" + type.Name;
//判断是否存在对应资源
if (resDic.ContainsKey(resName))
{
ResInfo<UnityEngine.Object> resInfo = resDic[resName] as ResInfo<UnityEngine.Object>;
if(isSub)
resInfo.SubRefCount();
resInfo.isDel = isDel;
//资源已经加载结束
if (resInfo.asset != null && resInfo.refCount == 0 && resInfo.isDel)
{
resDic.Remove(resName);
Resources.UnloadAsset(resInfo.asset as UnityEngine.Object);
}
else if(resInfo.asset == null)//资源在加载中
{
if (callback != null)
resInfo.callback -= callback;
}
}
}
/// <summary>
/// 异步卸载对应没有使用的Resources相关的资源
/// </summary>
/// <param name="callBack">回调函数</param>
public void UnloadUnusedAssets(UnityAction callBack)
{
MonoMgr.Instance.StartCoroutine(ReallyUnloadUnusedAssets(callBack));
}
private IEnumerator ReallyUnloadUnusedAssets(UnityAction callBack)
{
//就是在真正移除不使用资源之前 应该把我们自己记录的那些引用计数为0 并且没有被移除记录的资源移除掉
List<string> list = new List<string>();
foreach (string path in resDic.Keys)
{
if (resDic[path].refCount == 0)
list.Add(path);
}
foreach (string path in list)
{
resDic.Remove(path);
}
AsyncOperation asyncOperation = Resources.UnloadUnusedAssets();
yield return asyncOperation;
//卸载完毕后通知外部
callBack?.Invoke();
}
/// <summary>
/// 获取某个资源的引用计数
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="path"></param>
/// <returns></returns>
public int GetRefCount<T>(string path)
{
string resName = path + "_" + typeof(T).Name;
if (resDic.ContainsKey(resName))
return (resDic[resName] as ResInfo<T>).refCount;
else
return 0;
}
/// <summary>
/// 清空字典
/// </summary>
/// <param name="callBack"></param>
public void ClearDic(UnityAction callBack)
{
MonoMgr.Instance.StartCoroutine(ReallyClearDic(callBack));
}
private IEnumerator ReallyClearDic(UnityAction callBack)
{
resDic.Clear();
AsyncOperation asyncOperation = Resources.UnloadUnusedAssets();
yield return asyncOperation;
//卸载完毕后通知外部
callBack?.Invoke();
}
}
Editor资源加载模块
Editor 资源加载的主要作用,在进行项目开发时,最终发布的项目一般为了达到以下两个目的
1.减小包体大小
2.热更新
最终会通过 AssetBundle 资源加载作为主要加载方式
那么也就是说最终我们发布项目时
几乎所有的的游戏资源都会在 AB 包中的
AB 包最终会放置在 StreamingAssets 中 或者 远程资源服务器中
但是在开发期间,如果进行频繁的资源打包(资源放入 AssetBundle 包),会大大降低我们的开发效率。
因此我们在开发功能时,可以不通过 AB 包加载资源,而是在最终要测试或发布时才把资源整合进 AB 包。但是为了方便资源管理,我们也不能将资源放在 Resources 中来进行加载,因为最终 Resources 中的资源会被打包出去。那么此时 Editor 资源加载的作用就体现出来了,我们可以将资源放进不会被打包的 Editor 文件夹中,开发时通过 Editor 资源加载,测试和发布时使用 AssetBundle 资源加载。
cs
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
/// <summary>
/// 编辑器资源管理
/// 只有开发时能使用该管理器加载资源
/// </summary>
public class EditorResMgr : BaseManager<EditorResMgr>
{
//用于放置需要打包进AB包中的资源路径
private string rootPath = "Assets/Editor/ArtRes/";
private EditorResMgr() { }
/// <summary>
/// 加载单个资源
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="path"></param>
/// <returns></returns>
public T LoadEditorRes<T>(string path) where T : UnityEngine.Object
{
#if UNITY_EDITOR
string suffixName = "";
if (typeof(T) == typeof(GameObject))
suffixName = ".prefab";
else if (typeof(T) == typeof(Material))
suffixName = ".mat";
return AssetDatabase.LoadAssetAtPath<T>(rootPath + path + suffixName);
#else
return null;
#endif
}
/// <summary>
/// 加载图集相关资源的方法
/// </summary>
/// <param name="path"></param>
/// <param name="spriteName"></param>
/// <returns></returns>
public Sprite LoadSprite(string path, string spriteName)
{
#if UNITY_EDITOR
//加载图集中的所有子资源
Object[] sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath(rootPath + path);
foreach (var item in sprites)
{
if (item.name == spriteName)
return item as Sprite;
}
return null;
#else
return null;
#endif
}
/// <summary>
/// 获取整个图集
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public Dictionary<string, Sprite> LoadSprites(string path)
{
#if UNITY_EDITOR
Dictionary<string, Sprite> spDic = new Dictionary<string, Sprite>();
//加载图集中的所有子资源
Object[] sprites = AssetDatabase.LoadAllAssetRepresentationsAtPath(rootPath + path);
foreach (var item in sprites)
{
spDic.Add(item.name, item as Sprite);
}
return spDic;
#else
return null;
#endif
}
}
AssetBundle资源管理模块
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class ABMgr : SingletonAutoMono<ABMgr>
{
//主包
private AssetBundle mainAB = null;
//主包依赖获取配置文件
private AssetBundleManifest manifest = null;
//选择存储 AB包的容器
//AB包不能够重复加载 否则会报错
//字典知识 用来存储 AB包对象
private Dictionary<string, AssetBundle> abDic = new Dictionary<string, AssetBundle>();
/// <summary>
/// 获取AB包加载路径
/// </summary>
private string PathUrl
{
get
{
return Application.streamingAssetsPath + "/";
}
}
/// <summary>
/// 主包名 根据平台不同 报名不同
/// </summary>
private string MainName
{
get
{
#if UNITY_IOS
return "IOS";
#elif UNITY_ANDROID
return "Android";
#else
return "PC";
#endif
}
}
/// <summary>
/// 加载主包 和 配置文件
/// 因为加载所有包是 都得判断 通过它才能得到依赖信息
/// 所以写一个方法
/// </summary>
private void LoadMainAB()
{
if( mainAB == null )
{
mainAB = AssetBundle.LoadFromFile( PathUrl + MainName);
manifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
}
}
/// <summary>
/// 泛型异步加载资源
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="abName"></param>
/// <param name="resName"></param>
/// <param name="callBack"></param>
public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callBack, bool isSync = false) where T:Object
{
StartCoroutine(ReallyLoadResAsync<T>(abName, resName, callBack,isSync));
}
//协程函数
private IEnumerator ReallyLoadResAsync<T>(string abName, string resName, UnityAction<T> callBack, bool isSync) where T : Object
{
//加载主包
LoadMainAB();
//获取依赖包
string[] strs = manifest.GetAllDependencies(abName);
for (int i = 0; i < strs.Length; i++)
{
if (!abDic.ContainsKey(strs[i]))
{
if (isSync) //同步加载
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
abDic.Add(strs[i], ab);
}
else //异步加载
{
//一开始异步加载就记录 如果值为null说明正在加载
abDic.Add(strs[i], null);
AssetBundleCreateRequest rep = AssetBundle.LoadFromFileAsync(PathUrl + strs[i]);
yield return rep;
//不为空加载结束
abDic[strs[i]] = rep.assetBundle;
}
}
else//证明字典中已经记录了一个AB包信息
{
//如果字典中记录的是null 那就证明正在加载
//我们需要等待加载结束 直接使用
while (abDic[strs[i]] == null)
{
//只有发现正在加载中 就不停的等待一帧
yield return 0;
}
}
}
//加载目标包
if (!abDic.ContainsKey(abName))
{
if (isSync) //同步加载
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
abDic.Add(abName, ab);
}
else
{
//一开始异步加载就记录 如果值为null说明正在加载
abDic.Add(abName, null);
AssetBundleCreateRequest rep = AssetBundle.LoadFromFileAsync(PathUrl + abName);
yield return rep;
abDic[abName] = rep.assetBundle;
}
}
else
{
//如果字典中记录的是null 那就证明正在加载
//我们需要等待加载结束 直接使用
while (abDic[abName] == null)
{
//只有发现正在加载中 就不停的等待一帧
yield return 0;
}
}
if (isSync) //同步加载
{
//即使是同步加载也需要通过回调函数传结果给外部
T res = abDic[abName].LoadAsset<T>(resName);
callBack(res);
}
else
{
//异步加载包中资源
AssetBundleRequest abq = abDic[abName].LoadAssetAsync<T>(resName);
yield return abq;
callBack(abq.asset as T);
}
}
/// <summary>
/// Type异步加载资源
/// </summary>
/// <param name="abName"></param>
/// <param name="resName"></param>
/// <param name="type"></param>
/// <param name="callBack"></param>
public void LoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack, bool isSync = false)
{
StartCoroutine(ReallyLoadResAsync(abName, resName, type, callBack, isSync));
}
private IEnumerator ReallyLoadResAsync(string abName, string resName, System.Type type, UnityAction<Object> callBack, bool isSync)
{
//加载主包
LoadMainAB();
//获取依赖包
string[] strs = manifest.GetAllDependencies(abName);
for (int i = 0; i < strs.Length; i++)
{
if (!abDic.ContainsKey(strs[i]))
{
if (isSync) //同步加载
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
abDic.Add(strs[i], ab);
}
else //异步加载
{
//一开始异步加载就记录 如果值为null说明正在加载
abDic.Add(strs[i], null);
AssetBundleCreateRequest rep = AssetBundle.LoadFromFileAsync(PathUrl + strs[i]);
yield return rep;
//不为空加载结束
abDic[strs[i]] = rep.assetBundle;
}
}
else//证明字典中已经记录了一个AB包信息
{
//如果字典中记录的是null 那就证明正在加载
//我们需要等待加载结束 直接使用
while (abDic[strs[i]] == null)
{
//只有发现正在加载中 就不停的等待一帧
yield return 0;
}
}
}
//加载目标包
if (!abDic.ContainsKey(abName))
{
if (isSync) //同步加载
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
abDic.Add(abName, ab);
}
else
{
//一开始异步加载就记录 如果值为null说明正在加载
abDic.Add(abName, null);
AssetBundleCreateRequest rep = AssetBundle.LoadFromFileAsync(PathUrl + abName);
yield return rep;
abDic[abName] = rep.assetBundle;
}
}
else
{
//如果字典中记录的是null 那就证明正在加载
//我们需要等待加载结束 直接使用
while (abDic[abName] == null)
{
//只有发现正在加载中 就不停的等待一帧
yield return 0;
}
}
if (isSync)
{
Object res = abDic[abName].LoadAsset(resName, type);
callBack(res);
}
else
{
//异步加载包中资源
AssetBundleRequest abq = abDic[abName].LoadAssetAsync(resName, type);
yield return abq;
callBack(abq.asset);
}
}
/// <summary>
/// 名字 异步加载 指定资源
/// </summary>
/// <param name="abName"></param>
/// <param name="resName"></param>
/// <param name="callBack"></param>
public void LoadResAsync(string abName, string resName, UnityAction<Object> callBack, bool isSync = false)
{
StartCoroutine(ReallyLoadResAsync(abName, resName, callBack, isSync));
}
private IEnumerator ReallyLoadResAsync(string abName, string resName, UnityAction<Object> callBack, bool isSync)
{
//加载主包
LoadMainAB();
//获取依赖包
string[] strs = manifest.GetAllDependencies(abName);
for (int i = 0; i < strs.Length; i++)
{
if (!abDic.ContainsKey(strs[i]))
{
if (isSync) //同步加载
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + strs[i]);
abDic.Add(strs[i], ab);
}
else //异步加载
{
//一开始异步加载就记录 如果值为null说明正在加载
abDic.Add(strs[i], null);
AssetBundleCreateRequest rep = AssetBundle.LoadFromFileAsync(PathUrl + strs[i]);
yield return rep;
//不为空加载结束
abDic[strs[i]] = rep.assetBundle;
}
}
else//证明字典中已经记录了一个AB包信息
{
//如果字典中记录的是null 那就证明正在加载
//我们需要等待加载结束 直接使用
while (abDic[strs[i]] == null)
{
//只有发现正在加载中 就不停的等待一帧
yield return 0;
}
}
}
//加载目标包
if (!abDic.ContainsKey(abName))
{
if (isSync) //同步加载
{
AssetBundle ab = AssetBundle.LoadFromFile(PathUrl + abName);
abDic.Add(abName, ab);
}
else
{
//一开始异步加载就记录 如果值为null说明正在加载
abDic.Add(abName, null);
AssetBundleCreateRequest rep = AssetBundle.LoadFromFileAsync(PathUrl + abName);
yield return rep;
abDic[abName] = rep.assetBundle;
}
}
else
{
//如果字典中记录的是null 那就证明正在加载
//我们需要等待加载结束 直接使用
while (abDic[abName] == null)
{
//只有发现正在加载中 就不停的等待一帧
yield return 0;
}
}
if (isSync) //同步加载
{
Object res = abDic[abName].LoadAsset(resName);
callBack(res);
}
else
{
//异步加载包中资源
AssetBundleRequest abq = abDic[abName].LoadAssetAsync(resName);
yield return abq;
callBack(abq.asset);
}
}
//卸载AB包的方法
public void UnLoadAB(string name,UnityAction<bool> callBackResult)
{
if( abDic.ContainsKey(name) )
{
if (abDic[name] == null)
{
//代表正在异步加载,没有卸载成功
callBackResult(false);
return;
}
abDic[name].Unload(false);
abDic.Remove(name);
//卸载成功
callBackResult(true);
}
}
//清空AB包的方法
public void ClearAB()
{
//清理之前 停止协同程序
StopAllCoroutines();
AssetBundle.UnloadAllAssetBundles(false);
abDic.Clear();
//卸载主包
mainAB = null;
}
}
UnityWebRequest类资源加载模块
WWW 和 UnityWebRequest 都是 Unity 提供给我们的简单的访问网页的类
我们可以利用它们来进行数据的上传和下载
除此之外,它们都支持 file:// 本地文件传输协议,我们可以利用该协议来异步加载本地文件
特别是在 Android 平台时,我们无法直接通过 C# 中的 File 公共类加载 StreamingAssets 文件夹中的内容,我们需要使用 WWW 或 UnityWebRequest 类来加载
cs
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
public class UnityWebRequestResMgr : BaseManager<UnityWebRequestResMgr>
{
private UnityWebRequestResMgr() { }
/// <summary>
/// 利用UnityWebRequest去加载资源
/// </summary>
/// <typeparam name="T">资源类型只能是 string byte[] Texture AssetBundle</typeparam>
/// <param name="path">路径需要自己加上协议 http:// ftp:// file://</param>
/// <param name="callback">加载成功回调函数</param>
/// <param name="failCallBack">加载失败回调函数</param>
public void LoadRes<T>(string path, UnityAction<T> callback, UnityAction failCallBack) where T : class
{
MonoMgr.Instance.StartCoroutine(ReallyLoadRes<T>(path, callback, failCallBack));
}
private IEnumerator ReallyLoadRes<T>(string path, UnityAction<T> callback, UnityAction failCallBack) where T : class
{
Type type = typeof(T);
//用于加载的对象
UnityWebRequest req = null;
if(type == typeof(string) || type == typeof(byte[]))
req = UnityWebRequest.Get(path);
if(type == typeof(Texture))
req = UnityWebRequestTexture.GetTexture(path);
if (type == typeof(AssetBundle))
req = UnityWebRequestAssetBundle.GetAssetBundle(path);
else
{
failCallBack?.Invoke();
yield break;
}
yield return req.SendWebRequest();
//如果加载成功
if (req.result == UnityWebRequest.Result.Success)
{
if (type == typeof(string))
callback?.Invoke(req.downloadHandler.text as T);
else if (type == typeof(byte[]))
callback?.Invoke(req.downloadHandler.data as T);
else if (type == typeof(Texture))
callback?.Invoke(DownloadHandlerTexture.GetContent(req) as T);
else if (type == typeof(AssetBundle))
callback?.Invoke(DownloadHandlerAssetBundle.GetContent(req) as T);
}
else
failCallBack?.Invoke();
//释放对象
req.Dispose();
}
}
整合EditorResMgr和ABMgr
cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// 用于进行加载AB包相关资源的整合,在开发中通过EditorResMgr去加载对应资源
/// </summary>
public class ABResMgr : BaseManager<ABResMgr>
{
private bool isDebug = true;
private ABResMgr() { }
public void LoadResAsync<T>(string abName, string resName, UnityAction<T> callBack, bool isSync = false) where T : Object
{
#if UNITY_EDITOR
if (isDebug)
{
T res = EditorResMgr.Instance.LoadEditorRes<T>($"{abName}/{resName}");
callBack?.Invoke(res);
}
else
{
ABMgr.Instance.LoadResAsync<T>(abName, resName, callBack, isSync);
}
#else
ABMgr.Instance.LoadResAsync<T>(abName, resName, callBack, isSync);
#endif
}
}