using System; using System.Collections; using System.Collections.Generic; using System.Threading; using AlicizaX.ObjectPool; using AlicizaX.Runtime; using Cysharp.Threading.Tasks; using UnityEngine; using UnityEngine.SceneManagement; using YooAsset; namespace AlicizaX.Resource.Runtime { /// /// 资源管理器。 /// internal sealed partial class ResourceManager : IResourceManager { #region Propreties /// /// 资源包名称。 /// public string DefaultPackageName { get; set; } /// /// 资源系统运行模式。 /// public EPlayMode PlayMode { get; set; } /// /// 下载文件校验等级。 /// public EFileVerifyLevel VerifyLevel { get; set; } /// /// 设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒) /// public long Milliseconds { get; set; } /// /// 实例化的根节点。 /// public Transform InstanceRoot { get; set; } /// /// Propagates notification that operations should be canceled. /// public CancellationToken CancellationToken { get; private set; } /// /// 默认资源包。 /// internal ResourcePackage DefaultPackage { private set; get; } /// /// 资源包列表。 /// private Dictionary PackageMap { get; } = new Dictionary(); /// /// 资源信息列表。 /// private readonly Dictionary _assetInfoMap = new Dictionary(); /// /// 正在加载的资源列表。 /// private readonly HashSet _assetLoadingList = new HashSet(); /// /// 获取或设置无用资源释放的最小间隔时间,以秒为单位。 /// private float m_MinUnloadUnusedAssetsInterval = 60f; /// /// 获取或设置无用资源释放的最大间隔时间,以秒为单位。 /// private float m_MaxUnloadUnusedAssetsInterval = 300f; /// /// 使用系统释放无用资源策略。 /// private bool m_UseSystemUnloadUnusedAssets = true; /// /// 获取无用资源释放的等待时长,以秒为单位。 /// private float m_LastUnloadUnusedAssetsOperationElapseSeconds = 0f; private bool m_ForceUnloadUnusedAssets = false; private bool m_PreorderUnloadUnusedAssets = false; private bool m_PerformGCCollect = false; private AsyncOperation m_AsyncOperation = null; private UnloadUnusedAssetsOperation m_unloadUnusedAssetsOperation = null; #endregion /// /// 初始化资源管理器的新实例。 /// public ResourceManager() { } public void Initialize() { // 初始化资源系统 YooAssets.Initialize(new ResourceLogger()); YooAssets.SetOperationSystemMaxTimeSlice(Milliseconds); #if UNITY_WECHAT_GAME && !UNITY_EDITOR YooAssets.SetCacheSystemDisableCacheOnWebGL(); #endif // 创建默认的资源包 string packageName = DefaultPackageName; var defaultPackage = YooAssets.TryGetPackage(packageName); if (defaultPackage == null) { defaultPackage = YooAssets.CreatePackage(packageName); YooAssets.SetDefaultPackage(defaultPackage); DefaultPackage = defaultPackage; } CancellationToken = InstanceRoot.gameObject.GetCancellationTokenOnDestroy(); IObjectPoolManager objectPoolModule = SysModuleCenter.GetModule(); SetObjectPoolManager(objectPoolModule); } void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) { m_LastUnloadUnusedAssetsOperationElapseSeconds += Time.unscaledDeltaTime; if (m_AsyncOperation == null && m_unloadUnusedAssetsOperation == null && (m_ForceUnloadUnusedAssets || m_LastUnloadUnusedAssetsOperationElapseSeconds >= m_MaxUnloadUnusedAssetsInterval || m_PreorderUnloadUnusedAssets && m_LastUnloadUnusedAssetsOperationElapseSeconds >= m_MinUnloadUnusedAssetsInterval)) { Log.Info("Unload unused assets..."); m_ForceUnloadUnusedAssets = false; m_PreorderUnloadUnusedAssets = false; m_LastUnloadUnusedAssetsOperationElapseSeconds = 0f; m_AsyncOperation = Resources.UnloadUnusedAssets(); if (m_UseSystemUnloadUnusedAssets) { UnloadUnusedAssets(); } } if (m_AsyncOperation is { isDone: true }) { m_AsyncOperation = null; if (m_PerformGCCollect) { Log.Info("GC.Collect..."); m_PerformGCCollect = false; GC.Collect(); } } if (m_unloadUnusedAssetsOperation is { IsDone: true }) { m_unloadUnusedAssetsOperation = null; Log.Info("Unload UnusedAssets Done..."); } } public UniTask InitPackageAsync(string packageName, string hostServerURL, string fallbackHostServerURL, bool isDefaultPackage = true) { if (PackageMap.ContainsKey(packageName)) { Log.Error($"ResourceSystem has already init package : {packageName}"); return new UniTask(false); } var taskCompletionSource = new UniTaskCompletionSource(); GameFrameworkGuard.NotNull(packageName, nameof(packageName)); GameFrameworkGuard.NotNull(hostServerURL, nameof(hostServerURL)); GameFrameworkGuard.NotNull(fallbackHostServerURL, nameof(fallbackHostServerURL)); // 创建默认的资源包 var resourcePackage = YooAssets.TryGetPackage(packageName); if (resourcePackage == null) { resourcePackage = YooAssets.CreatePackage(packageName); if (isDefaultPackage) { // 设置该资源包为默认的资源包,可以使用YooAssets相关加载接口加载该资源包内容。 YooAssets.SetDefaultPackage(resourcePackage); } } PackageMap[packageName] = resourcePackage; var initializationOperationHandler = CreateInitializationOperationHandler(resourcePackage, hostServerURL, fallbackHostServerURL); initializationOperationHandler.Completed += asyncOperationBase => { if (asyncOperationBase.Error == null && asyncOperationBase.Status == EOperationStatus.Succeed && asyncOperationBase.IsDone) { taskCompletionSource.TrySetResult(true); } else { taskCompletionSource.TrySetException(new Exception(asyncOperationBase.Error)); } }; return taskCompletionSource.Task; } void IModule.Dispose() { PackageMap.Clear(); m_AssetPool = null; _assetLoadingList.Clear(); _assetInfoMap.Clear(); } #region Public Methods #region 加载场景 /// /// 异步加载场景 /// /// 资源路径 /// 场景模式 /// 是否加载完成自动激活 /// public UniTask LoadSceneAsync(string path, LoadSceneMode sceneMode, bool activateOnLoad = true) { var taskCompletionSource = new UniTaskCompletionSource(); var sceneHandle = YooAssets.LoadSceneAsync(path, sceneMode, LocalPhysicsMode.None, !activateOnLoad); sceneHandle.Completed += handle => { taskCompletionSource.TrySetResult(handle); }; return taskCompletionSource.Task; } /// /// 异步加载场景 /// /// 资源路径 /// 场景模式 /// 是否加载完成自动激活 /// public UniTask LoadSceneAsync(AssetInfo assetInfo, LoadSceneMode sceneMode, bool activateOnLoad = true) { var taskCompletionSource = new UniTaskCompletionSource(); var sceneHandle = YooAssets.LoadSceneAsync(assetInfo, sceneMode, LocalPhysicsMode.None, !activateOnLoad); sceneHandle.Completed += handle => { taskCompletionSource.TrySetResult(handle); }; return taskCompletionSource.Task; } #endregion #region 获取资源信息 /// /// 是否需要从远端更新下载。 /// /// 资源的定位地址。 /// 资源包名称。 public bool IsNeedDownloadFromRemote(string location, string packageName = "") { if (string.IsNullOrEmpty(packageName)) { return YooAssets.IsNeedDownloadFromRemote(location); } else { var package = YooAssets.GetPackage(packageName); return package.IsNeedDownloadFromRemote(location); } } /// /// 是否需要从远端更新下载。 /// /// 资源信息。 /// 资源包名称。 public bool IsNeedDownloadFromRemote(AssetInfo assetInfo, string packageName = "") { if (string.IsNullOrEmpty(packageName)) { return YooAssets.IsNeedDownloadFromRemote(assetInfo); } else { var package = YooAssets.GetPackage(packageName); return package.IsNeedDownloadFromRemote(assetInfo); } } /// /// 获取资源信息列表。 /// /// 资源标签。 /// 资源包名称。 /// 资源信息列表。 public AssetInfo[] GetAssetInfos(string tag, string packageName = "") { if (string.IsNullOrEmpty(packageName)) { return YooAssets.GetAssetInfos(tag); } else { var package = YooAssets.GetPackage(packageName); return package.GetAssetInfos(tag); } } /// /// 获取资源信息列表。 /// /// 资源标签列表。 /// 资源包名称。 /// 资源信息列表。 public AssetInfo[] GetAssetInfos(string[] tags, string packageName = "") { if (string.IsNullOrEmpty(packageName)) { return YooAssets.GetAssetInfos(tags); } else { var package = YooAssets.GetPackage(packageName); return package.GetAssetInfos(tags); } } /// /// 获取资源信息。 /// /// 资源的定位地址。 /// 资源包名称。 /// 资源信息。 public AssetInfo GetAssetInfo(string location, string packageName = "") { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } if (string.IsNullOrEmpty(packageName)) { if (_assetInfoMap.TryGetValue(location, out AssetInfo assetInfo)) { return assetInfo; } assetInfo = YooAssets.GetAssetInfo(location); _assetInfoMap[location] = assetInfo; return assetInfo; } else { string key = $"{packageName}/{location}"; if (_assetInfoMap.TryGetValue(key, out AssetInfo assetInfo)) { return assetInfo; } var package = YooAssets.GetPackage(packageName); if (package == null) { throw new GameFrameworkException($"The package does not exist. Package Name :{packageName}"); } assetInfo = package.GetAssetInfo(location); _assetInfoMap[key] = assetInfo; return assetInfo; } } /// /// 检查资源是否存在。 /// /// 资源定位地址。 /// 资源包名称。 /// 检查资源是否存在的结果。 public HasAssetResult HasAsset(string location, string packageName = "") { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } AssetInfo assetInfo = GetAssetInfo(location, packageName); if (!CheckLocationValid(location)) { return HasAssetResult.Valid; } if (assetInfo == null) { return HasAssetResult.NotExist; } if (IsNeedDownloadFromRemote(assetInfo)) { return HasAssetResult.AssetOnline; } return HasAssetResult.AssetOnDisk; } /// /// 检查资源定位地址是否有效。 /// /// 资源的定位地址 /// 资源包名称。 public bool CheckLocationValid(string location, string packageName = "") { if (string.IsNullOrEmpty(packageName)) { return YooAssets.CheckLocationValid(location); } else { var package = YooAssets.GetPackage(packageName); return package.CheckLocationValid(location); } } #endregion #region 资源加载 #region 获取资源句柄 /// /// 获取同步资源句柄。 /// /// 资源定位地址。 /// 指定资源包的名称。不传使用默认资源包 /// 资源类型。 /// 资源句柄。 private AssetHandle GetHandleSync(string location, string packageName = "") where T : UnityEngine.Object { return GetHandleSync(location, typeof(T), packageName); } private AssetHandle GetHandleSync(string location, Type assetType, string packageName = "") { if (string.IsNullOrEmpty(packageName)) { return YooAssets.LoadAssetSync(location, assetType); } var package = YooAssets.GetPackage(packageName); return package.LoadAssetSync(location, assetType); } /// /// 获取异步资源句柄。 /// /// 资源定位地址。 /// 指定资源包的名称。不传使用默认资源包 /// 资源类型。 /// 资源句柄。 private AssetHandle GetHandleAsync(string location, string packageName = "") where T : UnityEngine.Object { return GetHandleAsync(location, typeof(T), packageName); } private AssetHandle GetHandleAsync(string location, Type assetType, string packageName = "") { if (string.IsNullOrEmpty(packageName)) { return YooAssets.LoadAssetAsync(location, assetType); } var package = YooAssets.GetPackage(packageName); return package.LoadAssetAsync(location, assetType); } #endregion /// /// 获取资源定位地址的缓存Key。 /// /// 资源定位地址。 /// 资源包名称。 /// 资源定位地址的缓存Key。 private string GetCacheKey(string location, string packageName = "") { if (string.IsNullOrEmpty(packageName) || packageName.Equals(DefaultPackageName)) { return location; } return $"{packageName}/{location}"; } public T LoadAsset(string location, string packageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } string assetObjectKey = GetCacheKey(location, packageName); AssetObject assetObject = m_AssetPool.Spawn(assetObjectKey); if (assetObject != null) { return assetObject.Target as T; } AssetHandle handle = GetHandleSync(location, packageName: packageName); T ret = handle.AssetObject as T; assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); m_AssetPool.Register(assetObject, true); return ret; } public GameObject LoadGameObject(string location, Transform parent = null, string packageName = "") { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } string assetObjectKey = GetCacheKey(location, packageName); AssetObject assetObject = m_AssetPool.Spawn(assetObjectKey); if (assetObject != null) { return AssetsReference.Instantiate(assetObject.Target as GameObject, parent).gameObject; } AssetHandle handle = GetHandleSync(location, packageName: packageName); GameObject gameObject = AssetsReference.Instantiate(handle.AssetObject as GameObject, parent).gameObject; assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); m_AssetPool.Register(assetObject, true); return gameObject; } /// /// 异步加载资源。 /// /// 资源的定位地址。 /// 回调函数。 /// 指定资源包的名称。不传使用默认资源包 /// 要加载资源的类型。 public async UniTaskVoid LoadAsset(string location, Action callback, string packageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); return; } if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } string assetObjectKey = GetCacheKey(location, packageName); await TryWaitingLoading(assetObjectKey); AssetObject assetObject = m_AssetPool.Spawn(assetObjectKey); if (assetObject != null) { await UniTask.Yield(); callback?.Invoke(assetObject.Target as T); return; } _assetLoadingList.Add(assetObjectKey); AssetHandle handle = GetHandleAsync(location, packageName: packageName); handle.Completed += assetHandle => { _assetLoadingList.Remove(assetObjectKey); if (assetHandle.AssetObject != null) { assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); m_AssetPool.Register(assetObject, true); callback?.Invoke(assetObject.Target as T); } else { callback?.Invoke(null); } }; } public TObject[] LoadSubAssetsSync(string location, string packageName = "") where TObject : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } throw new NotImplementedException(); } public UniTask LoadSubAssetsAsync(string location, string packageName = "") where TObject : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } throw new NotImplementedException(); } public async UniTask LoadAssetAsync(string location, CancellationToken cancellationToken = default, string packageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } string assetObjectKey = GetCacheKey(location, packageName); await TryWaitingLoading(assetObjectKey); AssetObject assetObject = m_AssetPool.Spawn(assetObjectKey); if (assetObject != null) { await UniTask.Yield(); return assetObject.Target as T; } _assetLoadingList.Add(assetObjectKey); AssetHandle handle = GetHandleAsync(location, packageName: packageName); bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); if (cancelOrFailed) { _assetLoadingList.Remove(assetObjectKey); return null; } assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); m_AssetPool.Register(assetObject, true); _assetLoadingList.Remove(assetObjectKey); return handle.AssetObject as T; } public async UniTask LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "") { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } string assetObjectKey = GetCacheKey(location, packageName); await TryWaitingLoading(assetObjectKey); AssetObject assetObject = m_AssetPool.Spawn(assetObjectKey); if (assetObject != null) { await UniTask.Yield(); return AssetsReference.Instantiate(assetObject.Target as GameObject, parent).gameObject; } _assetLoadingList.Add(assetObjectKey); AssetHandle handle = GetHandleAsync(location, packageName: packageName); bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); if (cancelOrFailed) { _assetLoadingList.Remove(assetObjectKey); return null; } GameObject gameObject = AssetsReference.Instantiate(handle.AssetObject as GameObject, parent).gameObject; assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); m_AssetPool.Register(assetObject, true); _assetLoadingList.Remove(assetObjectKey); return gameObject; } #endregion /// /// 异步加载资源。 /// /// 资源的定位地址。 /// 要加载资源的类型。 /// 加载资源的优先级。 /// 加载资源回调函数集。 /// 用户自定义数据。 /// 指定资源包的名称。不传使用默认资源包。 public async void LoadAssetAsync(string location, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "") { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } if (loadAssetCallbacks == null) { throw new GameFrameworkException("Load asset callbacks is invalid."); } string assetObjectKey = GetCacheKey(location, packageName); await TryWaitingLoading(assetObjectKey); float duration = Time.time; AssetObject assetObject = m_AssetPool.Spawn(assetObjectKey); if (assetObject != null) { await UniTask.Yield(); loadAssetCallbacks.LoadAssetSuccessCallback(location, assetObject.Target, Time.time - duration, userData); return; } _assetLoadingList.Add(assetObjectKey); AssetInfo assetInfo = GetAssetInfo(location, packageName); if (!string.IsNullOrEmpty(assetInfo.Error)) { _assetLoadingList.Remove(assetObjectKey); string errorMessage = Utility.Text.Format("Can not load asset '{0}' because :'{1}'.", location, assetInfo.Error); if (loadAssetCallbacks.LoadAssetFailureCallback != null) { loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotExist, errorMessage, userData); return; } throw new GameFrameworkException(errorMessage); } AssetHandle handle = GetHandleAsync(location, assetType, packageName: packageName); if (loadAssetCallbacks.LoadAssetUpdateCallback != null) { InvokeProgress(location, handle, loadAssetCallbacks.LoadAssetUpdateCallback, userData).Forget(); } await handle.ToUniTask(); if (handle.AssetObject == null || handle.Status == EOperationStatus.Failed) { _assetLoadingList.Remove(assetObjectKey); string errorMessage = Utility.Text.Format("Can not load asset '{0}'.", location); if (loadAssetCallbacks.LoadAssetFailureCallback != null) { loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotReady, errorMessage, userData); return; } throw new GameFrameworkException(errorMessage); } else { assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); m_AssetPool.Register(assetObject, true); _assetLoadingList.Remove(assetObjectKey); if (loadAssetCallbacks.LoadAssetSuccessCallback != null) { duration = Time.time - duration; loadAssetCallbacks.LoadAssetSuccessCallback(location, handle.AssetObject, duration, userData); } } } /// /// 异步加载资源。 /// /// 资源的定位地址。 /// 加载资源的优先级。 /// 加载资源回调函数集。 /// 用户自定义数据。 /// 指定资源包的名称。不传使用默认资源包。 public async void LoadAssetAsync(string location, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "") { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } if (loadAssetCallbacks == null) { throw new GameFrameworkException("Load asset callbacks is invalid."); } string assetObjectKey = GetCacheKey(location, packageName); await TryWaitingLoading(assetObjectKey); float duration = Time.time; AssetObject assetObject = m_AssetPool.Spawn(assetObjectKey); if (assetObject != null) { await UniTask.Yield(); loadAssetCallbacks.LoadAssetSuccessCallback(location, assetObject.Target, Time.time - duration, userData); return; } _assetLoadingList.Add(assetObjectKey); AssetInfo assetInfo = GetAssetInfo(location, packageName); if (!string.IsNullOrEmpty(assetInfo.Error)) { _assetLoadingList.Remove(assetObjectKey); string errorMessage = Utility.Text.Format("Can not load asset '{0}' because :'{1}'.", location, assetInfo.Error); if (loadAssetCallbacks.LoadAssetFailureCallback != null) { loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotExist, errorMessage, userData); return; } throw new GameFrameworkException(errorMessage); } AssetHandle handle = GetHandleAsync(location, assetInfo.AssetType, packageName: packageName); if (loadAssetCallbacks.LoadAssetUpdateCallback != null) { InvokeProgress(location, handle, loadAssetCallbacks.LoadAssetUpdateCallback, userData).Forget(); } await handle.ToUniTask(); if (handle.AssetObject == null || handle.Status == EOperationStatus.Failed) { _assetLoadingList.Remove(assetObjectKey); string errorMessage = Utility.Text.Format("Can not load asset '{0}'.", location); if (loadAssetCallbacks.LoadAssetFailureCallback != null) { loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotReady, errorMessage, userData); return; } throw new GameFrameworkException(errorMessage); } else { assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); m_AssetPool.Register(assetObject, true); _assetLoadingList.Remove(assetObjectKey); if (loadAssetCallbacks.LoadAssetSuccessCallback != null) { duration = Time.time - duration; loadAssetCallbacks.LoadAssetSuccessCallback(location, handle.AssetObject, duration, userData); } } } private async UniTaskVoid InvokeProgress(string location, AssetHandle assetHandle, LoadAssetUpdateCallback loadAssetUpdateCallback, object userData) { if (string.IsNullOrEmpty(location)) { throw new GameFrameworkException("Asset name is invalid."); } if (loadAssetUpdateCallback != null) { while (assetHandle is { IsValid: true, IsDone: false }) { await UniTask.Yield(); loadAssetUpdateCallback.Invoke(location, assetHandle.Progress, userData); } } } private readonly TimeoutController _timeoutController = new TimeoutController(); private async UniTask TryWaitingLoading(string assetObjectKey) { if (_assetLoadingList.Contains(assetObjectKey)) { try { await UniTask.WaitUntil( () => !_assetLoadingList.Contains(assetObjectKey), cancellationToken: CancellationToken) #if UNITY_EDITOR .AttachExternalCancellation(_timeoutController.Timeout(TimeSpan.FromSeconds(60))); _timeoutController.Reset(); #else ; #endif } catch (OperationCanceledException ex) { if (_timeoutController.IsTimeout()) { Log.Error($"LoadAssetAsync Waiting {assetObjectKey} timeout. reason:{ex.Message}"); } } } } public AssetHandle LoadAssetGetOperation(string location, string packageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(packageName)) { return YooAssets.LoadAssetSync(location); } var package = YooAssets.GetPackage(packageName); return package.LoadAssetSync(location); } public AssetHandle LoadAssetAsyncHandle(string location, string packageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(packageName)) { return YooAssets.LoadAssetAsync(location); } var package = YooAssets.GetPackage(packageName); return package.LoadAssetAsync(location); } #endregion #region 资源回收 private void UnloadUnusedAssets() { m_AssetPool.ReleaseAllUnused(); foreach (var package in PackageMap.Values) { if (package is { InitializeStatus: EOperationStatus.Succeed }) { m_unloadUnusedAssetsOperation = package.UnloadUnusedAssetsAsync(); } } } public void ForceUnloadAllAssets() { #if UNITY_WEBGL Log.Warning($"WebGL not support invoke {nameof(ForceUnloadAllAssets)}"); return; #else foreach (var package in PackageMap.Values) { if (package is { InitializeStatus: EOperationStatus.Succeed }) { package.UnloadAllAssetsAsync(); } } #endif } /// /// 强制执行释放未被使用的资源。 /// /// 是否使用垃圾回收。 public void ForceUnloadUnusedAssets(bool performGCCollect) { m_ForceUnloadUnusedAssets = true; if (performGCCollect) { m_PerformGCCollect = true; } } /// /// 预订执行释放未被使用的资源。 /// /// 是否使用垃圾回收。 public void UnloadUnusedAssets(bool performGCCollect) { m_PreorderUnloadUnusedAssets = true; if (performGCCollect) { m_PerformGCCollect = true; } } #endregion public int Priority { get => 2; } } }