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