Files
JJBB/Assets/Project/Script/AssetManagement/AssetManager/AssetManager.cs
2024-08-23 15:49:34 +08:00

960 lines
33 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);
}
}
}
}