960 lines
33 KiB
C#
960 lines
33 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.IO;
|
||
using UnityEngine;
|
||
using UnityEngine.Events;
|
||
using UnityEngine.SceneManagement;
|
||
using Object = UnityEngine.Object;
|
||
using UnitySceneManager = UnityEngine.SceneManagement.SceneManager;
|
||
|
||
namespace AssetManagement
|
||
{
|
||
/// <summary>
|
||
/// 管理资源池和资源抽取回调的情况
|
||
/// </summary>
|
||
public class AssetManager
|
||
{
|
||
private readonly List<BundleCallbackData> _bundleCallbacks = new List<BundleCallbackData>();
|
||
private readonly Dictionary<string, BundleHandle> _bundleHandles = new Dictionary<string, BundleHandle>();
|
||
private readonly List<SceneHandle> _sceneHandles = new List<SceneHandle>();
|
||
public BundleManager bundleManager { get; private set; }
|
||
|
||
public static AssetManager instance { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 构造新的实例
|
||
/// </summary>
|
||
public static void CreateInstance()
|
||
{
|
||
if (instance != null)
|
||
Debug.LogError(
|
||
"Process Error: AssetManager already has an instance, while trying to create a new one!");
|
||
else
|
||
instance = new AssetManager();
|
||
}
|
||
|
||
// /// <summary>
|
||
// /// 移除旧的实例
|
||
// /// </summary>
|
||
// public static void Clear()
|
||
// {
|
||
// if (instance != null)
|
||
// {
|
||
// instance.ClearAllAssets();
|
||
// instance = null;
|
||
// }
|
||
// }
|
||
|
||
// // 移除非场景等资源,自动走BundleManager予以卸载
|
||
// public void ClearCommonAssets()
|
||
// {
|
||
// _bundleCallbacks.Clear();
|
||
// foreach (var bundle in _bundleHandles.Values)
|
||
// {
|
||
// bundle.Dispose();
|
||
// bundleManager.UnloadBundle(bundle.bundleName);
|
||
// }
|
||
//
|
||
// _bundleHandles.Clear();
|
||
// }
|
||
|
||
// private void ClearAllAssets()
|
||
// {
|
||
// try
|
||
// {
|
||
// _bundleCallbacks.Clear();
|
||
// foreach (var bundle in _bundleHandles.Values)
|
||
// bundle.Dispose();
|
||
// // foreach (var bundle in _spriteHandles.Values)
|
||
// // bundle.Dispose();
|
||
// foreach (var scene in _sceneHandles)
|
||
// bundleManager.UnloadBundle(scene.bundleName);
|
||
// }
|
||
// catch (Exception e)
|
||
// {
|
||
// Debug.LogError(e);
|
||
// }
|
||
//
|
||
// try
|
||
// {
|
||
// AssetBundle.UnloadAllAssetBundles(true);
|
||
// }
|
||
// catch (Exception e)
|
||
// {
|
||
// Debug.LogError(e);
|
||
// }
|
||
// }
|
||
|
||
public bool IsLoadingScene()
|
||
{
|
||
var result = false;
|
||
if (_sceneHandles != null)
|
||
foreach (var handle in _sceneHandles)
|
||
if (handle.state == SceneHandle.SceneState.notLoad)
|
||
{
|
||
result = true;
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
public SceneHandle GetCurrentSceneHandle()
|
||
{
|
||
SceneHandle result;
|
||
if (_sceneHandles == null || _sceneHandles.Count == 0)
|
||
result = null;
|
||
else
|
||
result = _sceneHandles[0];
|
||
return result;
|
||
}
|
||
|
||
public void Init(BundleManager bundleManager)
|
||
{
|
||
this.bundleManager = bundleManager;
|
||
this.bundleManager.onComplete += OnBundleLoaded;
|
||
}
|
||
|
||
private void OnBundleLoaded(BundleRecord record)
|
||
{
|
||
BundleHandle handle;
|
||
if (_bundleHandles.TryGetValue(record.name, out handle))
|
||
{
|
||
handle.OnAssetBundleLoad(record);
|
||
// 如果加载完成时,已经没有资源需求
|
||
CheckBundleUnload(handle);
|
||
}
|
||
|
||
// // 注:不可能有其他流程不依赖加载Sprite Bundle,所以主动卸载应该是安全的
|
||
// // 如果项目里面还有作妖,再处理这个流程
|
||
// SpriteBundleHandle spriteHandle;
|
||
// if (_spriteHandles.TryGetValue(record.name, out spriteHandle))
|
||
// {
|
||
// spriteHandle.OnAssetBundleLoad(record.assetBundle);
|
||
// if (spriteHandle.callbackList.Count == 0 && spriteHandle.inUseSprites.Count == 0)
|
||
// {
|
||
// spriteHandle.Dispose();
|
||
// _spriteHandles.Remove(record.name);
|
||
// _bundleManager.UnloadBundle(record.name);
|
||
// }
|
||
// }
|
||
for (var i = _bundleCallbacks.Count - 1; i >= 0; i--)
|
||
{
|
||
var bundle = _bundleCallbacks[i];
|
||
if (bundle.bundleName == record.name)
|
||
{
|
||
_bundleCallbacks.RemoveAt(i);
|
||
bundle.CallBack(record);
|
||
}
|
||
}
|
||
|
||
foreach (var scene in _sceneHandles)
|
||
if (scene.bundleName == record.name)
|
||
scene.OnAssetBundleLoad();
|
||
}
|
||
|
||
public SceneHandle LoadScene(string bundleName, string sceneName, LoadSceneMode mode,
|
||
SceneLoadCallback onSceneLoad = null, bool allowReload = false)
|
||
{
|
||
// 注:SceneBundle应该是单一场景,包含多个场景时的情况尚未测试。
|
||
var handle = _sceneHandles.Find(a => a.bundleName == bundleName && a.sceneName == sceneName);
|
||
if (handle != null)
|
||
{
|
||
if (handle.mode != mode)
|
||
{
|
||
Debug.LogError(string.Format(
|
||
"Loading or loaded scene cannot switch loading mode! Cannot switch from {0} to {1}!",
|
||
handle.mode, mode));
|
||
}
|
||
else
|
||
{
|
||
if (handle.state == SceneHandle.SceneState.notLoad)
|
||
{
|
||
if (onSceneLoad != null)
|
||
handle.AddCallback(onSceneLoad);
|
||
}
|
||
else if (allowReload)
|
||
{
|
||
if (onSceneLoad != null)
|
||
handle.AddCallback(onSceneLoad);
|
||
handle.ReloadScene();
|
||
}
|
||
else
|
||
{
|
||
if (onSceneLoad != null)
|
||
handle.CallBackOne(onSceneLoad);
|
||
}
|
||
|
||
// 清理单一场景可能覆盖的叠加场景
|
||
// 注:Reload叠加场景本身也是个不正常的操作
|
||
if (mode == LoadSceneMode.Single)
|
||
for (var i = _sceneHandles.Count - 1; i >= 0; i--)
|
||
{
|
||
var scene = _sceneHandles[i];
|
||
if (scene != handle)
|
||
{
|
||
scene.Dispose();
|
||
bundleManager.UnloadBundle(scene.bundleName);
|
||
_sceneHandles.RemoveAt(i);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
BundleRecord record;
|
||
var loaded = bundleManager.TryLoadBundle(bundleName, out record);
|
||
// 如果是单一场景,立刻清空当前已经加载的场景Bundle
|
||
// 场景本身由新场景执行覆盖
|
||
if (mode == LoadSceneMode.Single)
|
||
{
|
||
foreach (var scene in _sceneHandles)
|
||
{
|
||
scene.Dispose();
|
||
bundleManager.UnloadBundle(scene.bundleName);
|
||
}
|
||
|
||
_sceneHandles.Clear();
|
||
}
|
||
handle = new SceneHandle(bundleName, sceneName, record, mode);
|
||
_sceneHandles.Add(handle);
|
||
// 允许不加回调加载场景,场景本身记录Bundle足矣
|
||
if (onSceneLoad != null)
|
||
handle.AddCallback(onSceneLoad);
|
||
if (loaded)
|
||
handle.OnAssetBundleLoad();
|
||
}
|
||
|
||
return handle;
|
||
}
|
||
|
||
// 就做个这个接口清了算了。
|
||
public void UnloadAllScene()
|
||
{
|
||
foreach (var sceneHandle in _sceneHandles)
|
||
{
|
||
if (sceneHandle.mode != LoadSceneMode.Single)
|
||
sceneHandle.UnloadScene();
|
||
bundleManager.UnloadBundle(sceneHandle.bundleName);
|
||
}
|
||
|
||
_sceneHandles.Clear();
|
||
}
|
||
|
||
// /// <summary>
|
||
// /// 主动卸载一个叠加场景,Single应该直接走LoadScene覆盖
|
||
// /// </summary>
|
||
// public void UnloadScene(string bundleName, string sceneName)
|
||
// {
|
||
// var i = _sceneHandles.FindIndex(a => a.bundleName == bundleName && a.sceneName == sceneName);
|
||
// if (i >= 0)
|
||
// {
|
||
// var handle = _sceneHandles[i];
|
||
// handle.UnloadScene();
|
||
// bundleManager.UnloadBundle(handle.bundleName);
|
||
// _sceneHandles.RemoveAt(i);
|
||
// }
|
||
// }
|
||
|
||
/// <summary>
|
||
/// 异步处理资源回调接口
|
||
/// </summary>
|
||
/// <param name="name">资源名称</param>
|
||
/// <param name="token">回调接收者,用于监视回调前是否接收者析构</param>
|
||
/// <param name="callback">回调函数</param>
|
||
/// <typeparam name="T">资源类型</typeparam>
|
||
public void LoadAsset<T>(string name, Component token, AssetLoadCallback<T> callback) where T : Object
|
||
{
|
||
#if UNITY_EDITOR
|
||
ExtensionCheck(name);
|
||
#endif
|
||
var bundleName = name.ToLower().ToUrl();
|
||
var assetName = Path.GetFileNameWithoutExtension(name);
|
||
LoadAsset(bundleName, assetName, token, callback);
|
||
}
|
||
|
||
public T LoadAssetSync<T>(string name) where T : Object
|
||
{
|
||
#if UNITY_EDITOR
|
||
ExtensionCheck(name);
|
||
#endif
|
||
var bundleName = name.ToLower().ToUrl();
|
||
var assetName = Path.GetFileNameWithoutExtension(name);
|
||
return LoadAssetSync<T>(bundleName, assetName);
|
||
}
|
||
#if UNITY_EDITOR
|
||
private static void ExtensionCheck(string name)
|
||
{
|
||
if (Path.HasExtension(name))
|
||
Debug.LogError(string.Format("The path {0} has extension! You should be constant with Resources.Load!",
|
||
name));
|
||
}
|
||
#endif
|
||
|
||
/// <summary>
|
||
/// 异步处理资源回调接口
|
||
/// </summary>
|
||
/// <param name="bundleName">资源包名</param>
|
||
/// <param name="assetName">资源名</param>
|
||
/// <param name="token">回调接收者,用于监视回调前是否接收者析构</param>
|
||
/// <param name="callback">回调函数</param>
|
||
/// <typeparam name="T">资源类型</typeparam>
|
||
public void LoadAsset<T>(string bundleName, string assetName, Component token, AssetLoadCallback<T> callback)
|
||
where T : Object
|
||
{
|
||
var handle = LoadBundle(bundleName);
|
||
handle.AddLoading(assetName, token, callback);
|
||
}
|
||
|
||
// public void Preload(string bundleName, Component token, BundleLoadCallback callback)
|
||
// {
|
||
// BundleHandle handle;
|
||
// if (_bundleHandles.TryGetValue(bundleName, out handle))
|
||
// {
|
||
// handle.preload = true;
|
||
// if (handle.isBundleLoaded)
|
||
// {
|
||
// if (callback != null)
|
||
// callback.Invoke(handle.bundleName);
|
||
// }
|
||
// else if (callback != null)
|
||
// _bundleCallbacks.Add(new BundleCallbackData(bundleName, token, callback));
|
||
// }
|
||
// else
|
||
// {
|
||
// if (callback != null)
|
||
// _bundleCallbacks.Add(new BundleCallbackData(bundleName, token, callback));
|
||
// handle = LoadBundle(bundleName);
|
||
// handle.preload = true;
|
||
// }
|
||
// }
|
||
//
|
||
// public BundleHandle PreloadSync(string bundleName)
|
||
// {
|
||
// var handle = LoadBundleSync(bundleName);
|
||
// if (handle != null)
|
||
// handle.preload = true;
|
||
// return handle;
|
||
// }
|
||
|
||
// public void RemovePreload(string bundleName)
|
||
// {
|
||
// BundleHandle handle;
|
||
// if (_bundleHandles.TryGetValue(bundleName, out handle))
|
||
// {
|
||
// handle.preload = false;
|
||
// CheckBundleUnload(handle);
|
||
// }
|
||
// for (var i = _bundleCallbacks.Count - 1; i >= 0; i--)
|
||
// if (_bundleCallbacks[i].bundleName == bundleName)
|
||
// _bundleCallbacks.RemoveAt(i);
|
||
// }
|
||
|
||
private BundleHandle LoadBundleSync(string bundleName)
|
||
{
|
||
BundleHandle handle;
|
||
var loadBundle = true;
|
||
if (_bundleHandles.TryGetValue(bundleName, out handle))
|
||
if (handle.isBundleLoaded)
|
||
loadBundle = false;
|
||
// 注:无需触发OnBundleLoaded,原有Bundle会在LoadBundleSync触发
|
||
if (loadBundle)
|
||
{
|
||
var record = bundleManager.LoadBundleSync(bundleName);
|
||
if (record == null)
|
||
{
|
||
Debug.LogError("Cannot handle the situation of an empty Bundle Record!");
|
||
}
|
||
else
|
||
{
|
||
if (handle == null)
|
||
{
|
||
handle = new BundleHandle(bundleName);
|
||
_bundleHandles[bundleName] = handle;
|
||
handle.isBundleLoaded = true;
|
||
handle.bundle = record.assetBundle;
|
||
}
|
||
}
|
||
}
|
||
|
||
return handle;
|
||
}
|
||
|
||
private BundleHandle LoadBundle(string bundleName)
|
||
{
|
||
BundleHandle handle;
|
||
if (!_bundleHandles.TryGetValue(bundleName, out handle))
|
||
{
|
||
handle = new BundleHandle(bundleName);
|
||
BundleRecord record;
|
||
if (bundleManager.TryLoadBundle(bundleName, out record))
|
||
handle.OnAssetBundleLoad(record);
|
||
_bundleHandles.Add(bundleName, handle);
|
||
}
|
||
|
||
return handle;
|
||
}
|
||
|
||
public T LoadAssetSync<T>(string bundleName, string assetName) where T : Object
|
||
{
|
||
T result = null;
|
||
var handle = LoadBundleSync(bundleName);
|
||
if (handle != null && handle.bundle)
|
||
result = handle.LoadSync<T>(assetName);
|
||
return result;
|
||
}
|
||
|
||
public void RemoveAsset(string bundle, string assetName)
|
||
{
|
||
BundleHandle handle;
|
||
if (_bundleHandles.TryGetValue(bundle, out handle))
|
||
{
|
||
handle.RemoveAsset(assetName);
|
||
CheckBundleUnload(handle);
|
||
}
|
||
}
|
||
|
||
private void CheckBundleUnload(BundleHandle handle)
|
||
{
|
||
if (handle.loadedList.Count == 0 &&
|
||
handle.loadingList.Count == 0)
|
||
{
|
||
handle.Dispose();
|
||
_bundleHandles.Remove(handle.bundleName);
|
||
bundleManager.UnloadBundle(handle.bundleName);
|
||
}
|
||
}
|
||
|
||
// 特殊处理整个AssetBundle抛弃的流程
|
||
public void UnloadBundle(string bundle)
|
||
{
|
||
BundleHandle handle;
|
||
if (_bundleHandles.TryGetValue(bundle, out handle))
|
||
{
|
||
handle.Dispose();
|
||
_bundleHandles.Remove(bundle);
|
||
}
|
||
|
||
bundleManager.UnloadBundle(bundle);
|
||
}
|
||
|
||
public void CancelCallback<T>(string bundle, string assetName, Component token, AssetLoadCallback<T> callback)
|
||
where T : Object
|
||
{
|
||
BundleHandle handle;
|
||
if (_bundleHandles.TryGetValue(bundle, out handle))
|
||
handle.RemoveLoading(assetName, token, callback);
|
||
}
|
||
}
|
||
|
||
public delegate void AssetLoadCallback<in T>(string bundleName, string assetName, T asset) where T : Object;
|
||
|
||
public delegate void BundleLoadCallback(string handle);
|
||
|
||
public delegate void SceneLoadCallback(bool success, string name);
|
||
|
||
public class BundleHandle
|
||
{
|
||
public readonly string bundleName;
|
||
public readonly List<AssetHandle> loadedList = new List<AssetHandle>();
|
||
public readonly List<AssetLoadHandle> loadingList = new List<AssetLoadHandle>();
|
||
public AssetBundle bundle;
|
||
|
||
public bool isBundleLoaded;
|
||
// // 是否预加载或者挂住的资源包,不执行释放
|
||
// public bool preload;
|
||
|
||
public BundleHandle(string bundleName)
|
||
{
|
||
this.bundleName = bundleName;
|
||
}
|
||
|
||
public T LoadSync<T>(string assetName) where T : Object
|
||
{
|
||
T result = null;
|
||
var load = true;
|
||
var type = typeof(T);
|
||
foreach (var loaded in loadedList)
|
||
if (loaded.type == type && loaded.assetName == assetName)
|
||
{
|
||
result = loaded.asset as T;
|
||
load = false;
|
||
break;
|
||
}
|
||
|
||
if (load)
|
||
if (isBundleLoaded && bundle)
|
||
{
|
||
result = bundle.LoadAsset<T>(assetName);
|
||
var loadingHandle = loadingList.Find(a => a.assetName == assetName && a.type == type);
|
||
// 触发异步加载完成事件,应该没有所谓的先后顺序,除非原项目做了其他的妖,就需要提出到Manager层延后触发
|
||
if (loadingHandle != null)
|
||
OnAssetLoadComplete(loadingHandle, result);
|
||
// 没有异步加载等待时,就自行建立加载成功记录
|
||
else
|
||
loadedList.Add(new AssetHandle(assetName, result, type));
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
public void AddLoading<T>(string assetName, Component token, AssetLoadCallback<T> callback) where T : Object
|
||
{
|
||
var type = typeof(T);
|
||
var loading = true;
|
||
if (isBundleLoaded)
|
||
{
|
||
if (bundle)
|
||
{
|
||
var handle = loadedList.Find(a => a.type == type && a.assetName == assetName);
|
||
// 资源已经加载完成的情况
|
||
if (handle != null)
|
||
{
|
||
loading = false;
|
||
var item = handle.asset as T;
|
||
try
|
||
{
|
||
callback.Invoke(bundleName, assetName, item);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
}
|
||
}
|
||
// 加载到空Bundle的情况,直接返回空值
|
||
else
|
||
{
|
||
loading = false;
|
||
callback.Invoke(bundleName, assetName, null);
|
||
}
|
||
}
|
||
|
||
if (loading)
|
||
{
|
||
var handle = loadingList.Find(a => a.type == type && a.assetName == assetName);
|
||
if (handle == null)
|
||
{
|
||
handle = new AssetLoadHandle(type, bundleName, assetName);
|
||
handle.OnComplete += OnAssetLoadComplete;
|
||
// 注:异步回调至少等一帧,因此可以先加载等回调
|
||
if (bundle)
|
||
handle.Load(bundle);
|
||
loadingList.Add(handle);
|
||
}
|
||
|
||
handle.AddCallback(token, callback);
|
||
}
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
if (loadingList != null)
|
||
foreach (var loading in loadingList)
|
||
loading.Dispose();
|
||
}
|
||
|
||
public void RemoveLoading<T>(string assetName, Component token, AssetLoadCallback<T> callback) where T : Object
|
||
{
|
||
var type = typeof(T);
|
||
foreach (var handle in loadingList)
|
||
if (handle.type == type && handle.assetName == assetName)
|
||
handle.RemoveCallback(token, callback);
|
||
}
|
||
|
||
// 注:删除回调不会出现泄漏 - 资源一旦从Bundle提取就唯一,并且不能主动析构
|
||
// Bundle本身会因为AssetManager层没有引用而卸载
|
||
public void RemoveAsset(string assetName)
|
||
{
|
||
for (var i = loadedList.Count - 1; i >= 0; i--)
|
||
{
|
||
var handle = loadedList[i];
|
||
//if (handle.type == type && handle.assetName == assetName)
|
||
if (handle.assetName == assetName)
|
||
loadedList.RemoveAt(i);
|
||
}
|
||
|
||
for (var i = loadingList.Count - 1; i >= 0; i--)
|
||
{
|
||
var handle = loadingList[i];
|
||
//if (handle.type == type && handle.assetName == assetName)
|
||
if (handle.assetName == assetName)
|
||
loadingList.RemoveAt(i);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 开始加载AssetBundle,返回是否有必要保留bundle
|
||
/// </summary>
|
||
public void OnAssetBundleLoad(BundleRecord assetBundle)
|
||
{
|
||
if (isBundleLoaded)
|
||
{
|
||
Debug.LogError(string.Format(
|
||
"Unexpected process, BundleHandle {0} has already hold a bundle, but get a new one!", bundleName));
|
||
}
|
||
else
|
||
{
|
||
isBundleLoaded = true;
|
||
bundle = assetBundle.assetBundle;
|
||
var callback = false;
|
||
if (bundle)
|
||
{
|
||
// 特殊处理场景资源包 - 同样回调空值,并且使用占位资源
|
||
if (bundle.isStreamedSceneAssetBundle)
|
||
{
|
||
callback = true;
|
||
Debug.LogError(
|
||
"You should never load a scene bundle via Bundle Handle! Use LoadScene interface instead!");
|
||
}
|
||
else
|
||
{
|
||
foreach (var loading in loadingList)
|
||
loading.Load(bundle);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
callback = true;
|
||
}
|
||
|
||
if (callback)
|
||
{
|
||
foreach (var loading in loadingList)
|
||
loading.Callback(null);
|
||
loadingList.Clear();
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OnAssetLoadComplete(AssetLoadHandle loadHandle, Object asset)
|
||
{
|
||
loadingList.Remove(loadHandle);
|
||
var handle = new AssetHandle(loadHandle.assetName, asset, loadHandle.type);
|
||
loadedList.Add(handle);
|
||
loadHandle.Callback(asset);
|
||
}
|
||
}
|
||
|
||
public abstract class AssetCallbackData
|
||
{
|
||
public readonly string assetName;
|
||
public readonly string bundleName;
|
||
public readonly Component token;
|
||
|
||
public AssetCallbackData(string bundleName, string assetName, Component token)
|
||
{
|
||
this.bundleName = bundleName;
|
||
this.assetName = assetName;
|
||
this.token = token;
|
||
}
|
||
|
||
public abstract void CallBack(Object asset);
|
||
}
|
||
|
||
public class AssetCallbackData<T> : AssetCallbackData where T : Object
|
||
{
|
||
public readonly AssetLoadCallback<T> callBack;
|
||
|
||
public AssetCallbackData(string bundleName, string assetName, Component token, AssetLoadCallback<T> callback) :
|
||
base(bundleName, assetName,
|
||
token)
|
||
{
|
||
callBack = callback;
|
||
}
|
||
|
||
public override void CallBack(Object asset)
|
||
{
|
||
var item = asset as T;
|
||
try
|
||
{
|
||
callBack.Invoke(bundleName, assetName, item);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
}
|
||
}
|
||
|
||
public class AssetHandle
|
||
{
|
||
public Object asset;
|
||
public string assetName;
|
||
public Type type;
|
||
|
||
public AssetHandle(string assetName, Object asset, Type type)
|
||
{
|
||
this.assetName = assetName;
|
||
this.asset = asset;
|
||
this.type = type;
|
||
}
|
||
}
|
||
|
||
public class BundleCallbackData
|
||
{
|
||
private readonly BundleLoadCallback _callback;
|
||
private readonly Component _token;
|
||
public readonly string bundleName;
|
||
|
||
public BundleCallbackData(string bundleName, Component token, BundleLoadCallback callback)
|
||
{
|
||
this.bundleName = bundleName;
|
||
_token = token;
|
||
_callback = callback;
|
||
}
|
||
|
||
public void CallBack(BundleRecord bundleHandle)
|
||
{
|
||
if (_token && _callback != null)
|
||
_callback.Invoke(bundleHandle.name);
|
||
}
|
||
}
|
||
|
||
public class AssetLoadHandle
|
||
{
|
||
private readonly List<AssetCallbackData> _callbacks = new List<AssetCallbackData>();
|
||
public readonly string assetName;
|
||
public readonly string bundleName;
|
||
public readonly Type type;
|
||
private AssetBundleRequest _assetBundleRequest;
|
||
|
||
public AssetLoadHandle(Type type, string bundleName, string assetName)
|
||
{
|
||
this.type = type;
|
||
this.bundleName = bundleName;
|
||
this.assetName = assetName;
|
||
}
|
||
|
||
public event UnityAction<AssetLoadHandle, Object> OnComplete;
|
||
|
||
public void Load(AssetBundle assetBundle)
|
||
{
|
||
_assetBundleRequest = assetBundle.LoadAssetAsync(assetName, type);
|
||
_assetBundleRequest.completed += OnAssetLoad;
|
||
}
|
||
|
||
public void AddCallback<T>(Component token, AssetLoadCallback<T> callback) where T : Object
|
||
{
|
||
_callbacks.Add(new AssetCallbackData<T>(bundleName, assetName, token, callback));
|
||
}
|
||
|
||
public void RemoveCallback<T>(Component token, AssetLoadCallback<T> callback) where T : Object
|
||
{
|
||
for (var i = 0; i < _callbacks.Count; i++)
|
||
if (_callbacks[i].token == token)
|
||
{
|
||
var callbackFunc = _callbacks[i] as AssetLoadCallback<T>;
|
||
if (callbackFunc == callback)
|
||
{
|
||
_callbacks.RemoveAt(i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
if (_assetBundleRequest != null)
|
||
{
|
||
_assetBundleRequest.completed -= OnAssetLoad;
|
||
_assetBundleRequest = null;
|
||
}
|
||
}
|
||
|
||
public void Callback(Object asset)
|
||
{
|
||
foreach (var callback in _callbacks)
|
||
if (callback.token)
|
||
callback.CallBack(asset);
|
||
else
|
||
Debug.LogError(string.Format("A listener to asset {0} didn't remove callback before destroyed!",
|
||
assetName));
|
||
}
|
||
|
||
private void OnAssetLoad(AsyncOperation asyncOperation)
|
||
{
|
||
if (asyncOperation == _assetBundleRequest)
|
||
{
|
||
if (OnComplete != null)
|
||
OnComplete.Invoke(this, _assetBundleRequest.asset);
|
||
}
|
||
else
|
||
{
|
||
Debug.LogError("AssetLoadHandle didn't receive expected AssetBundleRequest from callback!");
|
||
}
|
||
}
|
||
}
|
||
|
||
public class SceneHandle
|
||
{
|
||
public enum SceneState
|
||
{
|
||
notLoad,
|
||
loaded,
|
||
Error
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载资源包在整个流程的占比
|
||
/// </summary>
|
||
public const float bundlePercentage = 0.3f;
|
||
|
||
public readonly string bundleName;
|
||
public readonly List<SceneLoadCallback> callbacks = new List<SceneLoadCallback>();
|
||
public readonly LoadSceneMode mode;
|
||
public readonly string sceneName;
|
||
private AsyncOperation _asyncOperation;
|
||
public BundleRecord record;
|
||
|
||
public SceneHandle(string bundleName,
|
||
string sceneName,
|
||
BundleRecord bundleRecord,
|
||
LoadSceneMode mode)
|
||
{
|
||
this.bundleName = bundleName;
|
||
this.sceneName = sceneName;
|
||
record = bundleRecord;
|
||
this.mode = mode;
|
||
}
|
||
|
||
public SceneState state { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 开始加载AssetBundle,返回是否有必要保留bundle
|
||
/// </summary>
|
||
public void OnAssetBundleLoad()
|
||
{
|
||
if (record == null || !record.assetBundle || record.state == BundleState.Error)
|
||
{
|
||
state = SceneState.Error;
|
||
Debug.LogError(string.Format("Scene Bundle {0} for scene {1} is not properly loaded!", bundleName,
|
||
sceneName));
|
||
Callback();
|
||
}
|
||
else if (_asyncOperation == null)
|
||
{
|
||
_asyncOperation = UnitySceneManager.LoadSceneAsync(sceneName, mode);
|
||
_asyncOperation.completed += OnSceneLoadComplete;
|
||
}
|
||
}
|
||
|
||
public float GetLoadingProgress()
|
||
{
|
||
if (state == SceneState.loaded || state == SceneState.Error)
|
||
return 1f;
|
||
if (record == null)
|
||
return 0f;
|
||
if (_asyncOperation == null)
|
||
return bundlePercentage * record.GetTotalProgress();
|
||
return bundlePercentage + (1f - bundlePercentage) * _asyncOperation.progress;
|
||
}
|
||
|
||
public void AddCallback(SceneLoadCallback callback)
|
||
{
|
||
if (state == SceneState.notLoad)
|
||
{
|
||
callbacks.Add(callback);
|
||
}
|
||
else
|
||
{
|
||
var success = state == SceneState.loaded;
|
||
try
|
||
{
|
||
callback.Invoke(success, sceneName);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void RemoveCallback(SceneLoadCallback callback)
|
||
{
|
||
for (var i = callbacks.Count - 1; i >= 0; i--)
|
||
if (callbacks[i] == callback)
|
||
callbacks.RemoveAt(i);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 卸载一个叠加场景,主要场景使用肯定出问题
|
||
/// </summary>
|
||
public void UnloadScene()
|
||
{
|
||
if (mode != LoadSceneMode.Additive)
|
||
{
|
||
Debug.LogError("Only additive scenes can be unloaded! Major ones shall only be replaced instead!");
|
||
}
|
||
else
|
||
{
|
||
var scene = UnitySceneManager.GetSceneByName(sceneName);
|
||
if (scene.isLoaded)
|
||
UnitySceneManager.UnloadSceneAsync(scene);
|
||
}
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
callbacks.Clear();
|
||
record = null;
|
||
if (_asyncOperation != null)
|
||
_asyncOperation.completed -= OnSceneLoadComplete;
|
||
state = SceneState.notLoad;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 特殊接口,负责重新加载当前场景
|
||
/// </summary>
|
||
public void ReloadScene()
|
||
{
|
||
// state == SceneState.notLoad 尚未完成加载,就继续等待加载完成,不用刷新
|
||
switch (state)
|
||
{
|
||
case SceneState.Error:
|
||
Debug.LogError("Try to reload a scene that has error during previous loading!");
|
||
break;
|
||
case SceneState.loaded:
|
||
OnAssetBundleLoad();
|
||
break;
|
||
}
|
||
}
|
||
|
||
private void OnSceneLoadComplete(AsyncOperation asyncOperation)
|
||
{
|
||
if (_asyncOperation != asyncOperation)
|
||
{
|
||
state = SceneState.Error;
|
||
Debug.LogError("Unexpected process - the AsyncOperation is not what SceneHandle is waiting for!");
|
||
Callback();
|
||
}
|
||
else
|
||
{
|
||
state = SceneState.loaded;
|
||
Callback();
|
||
}
|
||
|
||
_asyncOperation = null;
|
||
}
|
||
|
||
private void Callback()
|
||
{
|
||
foreach (var callback in callbacks)
|
||
CallBackOne(callback);
|
||
callbacks.Clear();
|
||
}
|
||
|
||
public void CallBackOne(SceneLoadCallback callback)
|
||
{
|
||
var success = state == SceneState.loaded;
|
||
try
|
||
{
|
||
callback.Invoke(success, sceneName);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
}
|
||
}
|
||
} |