using System; using System.Collections; using System.Collections.Generic; using System.Threading; using AlicizaX.ObjectPool; using AlicizaX; using Cysharp.Threading.Tasks; using UnityEngine; using YooAsset; namespace AlicizaX.Resource.Runtime { /// /// 资源管理器。 /// internal sealed partial class ResourceModule : IResourceModule { /// /// 默认资源包名称。 /// public string DefaultPackageName { get; set; } = "DefaultPackage"; /// /// 资源系统运行模式。 /// public EPlayMode PlayMode { get; set; } = EPlayMode.OfflinePlayMode; public string DecryptionServices { get; set; } /// /// 设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒) /// public long Milliseconds { get; set; } = 30; public int Priority { get => 2; } private string _applicableGameVersion; private int _internalResourceVersion; /// /// 获取当前资源适用的游戏版本号。 /// public string ApplicableGameVersion => _applicableGameVersion; /// /// 获取当前内部资源版本号。 /// public int InternalResourceVersion => _internalResourceVersion; /// /// 当前最新的包裹版本。 /// public string PackageVersion { set; get; } public int DownloadingMaxNum { get; set; } public int FailedTryAgain { get; set; } #region internal /// /// 默认资源包。 /// internal ResourcePackage DefaultPackage { private set; get; } /// /// 资源包列表。 /// private Dictionary PackageMap { get; } = new Dictionary(); /// /// 资源信息列表。 /// private readonly Dictionary _assetInfoMap = new Dictionary(); /// /// 正在加载的资源列表。 /// private readonly HashSet _assetLoadingList = new HashSet(); #endregion public void Initialize() { // 初始化资源系统 YooAssets.Initialize(new ResourceLogger()); YooAssets.SetOperationSystemMaxTimeSlice(Milliseconds); // 创建默认的资源包 string packageName = DefaultPackageName; var defaultPackage = YooAssets.TryGetPackage(packageName); if (defaultPackage == null) { defaultPackage = YooAssets.CreatePackage(packageName); YooAssets.SetDefaultPackage(defaultPackage); } DefaultPackage = defaultPackage; IObjectPoolModule objectPoolModule = ModuleSystem.GetModule(); SetObjectPoolModule(objectPoolModule); } void IModule.Dispose() { PackageMap.Clear(); _assetPool = null; _assetLoadingList.Clear(); _assetInfoMap.Clear(); } public UniTask InitPackageAsync(string packageName = "", string hostServerURL = "", string fallbackHostServerURL = "") { if (string.IsNullOrEmpty(packageName)) { packageName = DefaultPackageName; } if (PackageMap.TryGetValue(packageName, out var resPackage)) { if (resPackage.InitializeStatus is EOperationStatus.Processing or EOperationStatus.Succeed) { Log.Error($"ResourceSystem has already init package : {packageName}"); return new UniTask(false); } else { PackageMap.Remove(packageName); } } 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); } PackageMap[packageName] = resourcePackage; var initializationOperationHandler = CreateInitializationOperationHandler(resourcePackage, hostServerURL, fallbackHostServerURL, DecryptionServices); 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; } /// /// 获取当前资源包版本。 /// /// 指定资源包的名称。不传使用默认资源包 /// 资源包版本。 public string GetPackageVersion(string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(DefaultPackageName) : YooAssets.GetPackage(customPackageName); if (package == null) { return string.Empty; } return package.GetPackageVersion(); } /// /// 异步更新最新包的版本。 /// /// 请求URL是否需要带时间戳。 /// 超时时间。 /// 指定资源包的名称。不传使用默认资源包 /// 请求远端包裹的最新版本操作句柄。 public RequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks = false, int timeout = 60, string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(DefaultPackageName) : YooAssets.GetPackage(customPackageName); return package.RequestPackageVersionAsync(appendTimeTicks, timeout); } /// /// 向网络端请求并更新清单 /// /// 更新的包裹版本 /// 超时时间(默认值:60秒) /// 指定资源包的名称。不传使用默认资源包 public UpdatePackageManifestOperation UpdatePackageManifestAsync(string packageVersion, int timeout = 60, string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(this.DefaultPackageName) : YooAssets.GetPackage(customPackageName); return package.UpdatePackageManifestAsync(packageVersion, timeout); } /// /// 创建资源下载器,用于下载当前资源版本所有的资源包文件。 /// /// 指定资源包的名称。不传使用默认资源包 public ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = "") { ResourcePackage package = null; if (string.IsNullOrEmpty(customPackageName)) { package = YooAssets.GetPackage(this.DefaultPackageName); } else { package = YooAssets.GetPackage(customPackageName); } return package.CreateResourceDownloader(DownloadingMaxNum, FailedTryAgain); } /// /// 清理包裹未使用的缓存文件。 /// /// 文件清理方式。 /// 指定资源包的名称。不传使用默认资源包 public ClearCacheFilesOperation ClearCacheFilesAsync( EFileClearMode clearMode = EFileClearMode.ClearUnusedBundleFiles, string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(DefaultPackageName) : YooAssets.GetPackage(customPackageName); return package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles); } /// /// 清理沙盒路径。 /// /// 指定资源包的名称。不传使用默认资源包 public void ClearAllBundleFiles(string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(DefaultPackageName) : YooAssets.GetPackage(customPackageName); package.ClearCacheFilesAsync(EFileClearMode.ClearAllBundleFiles); } #region 资源回收 public void OnLowMemory() { Log.Warning("Low memory reported..."); _forceUnloadUnusedAssetsAction?.Invoke(true); } private Action _forceUnloadUnusedAssetsAction; /// /// 低内存回调保护。 /// /// 低内存行为。 public void SetForceUnloadUnusedAssetsAction(Action action) { _forceUnloadUnusedAssetsAction = action; } /// /// 资源回收(卸载引用计数为零的资源)。 /// public void UnloadUnusedAssets() { _assetPool.ReleaseAllUnused(); foreach (var package in PackageMap.Values) { if (package is { InitializeStatus: EOperationStatus.Succeed }) { 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) { _forceUnloadUnusedAssetsAction?.Invoke(performGCCollect); } #region Public Methods #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 = _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); _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 = _assetPool.Spawn(assetObjectKey); if (assetObject != null) { return AssetsReference.Instantiate(assetObject.Target as GameObject, parent, this).gameObject; } AssetHandle handle = GetHandleSync(location, packageName: packageName); GameObject gameObject = AssetsReference.Instantiate(handle.AssetObject as GameObject, parent, this).gameObject; assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); _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 = _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); _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 = _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); _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 = _assetPool.Spawn(assetObjectKey); if (assetObject != null) { await UniTask.Yield(); return AssetsReference.Instantiate(assetObject.Target as GameObject, parent, this).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, this).gameObject; assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); _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 = _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); _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 = _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); _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); } } } /// /// 获取同步加载的资源操作句柄。 /// /// 资源定位地址。 /// 资源包名称。 /// 资源类型。 /// 资源操作句柄。 public AssetHandle LoadAssetSyncHandle(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 private readonly TimeoutController _timeoutController = new TimeoutController(); private async UniTask TryWaitingLoading(string assetObjectKey) { if (_assetLoadingList.Contains(assetObjectKey)) { try { await UniTask.WaitUntil(() => !_assetLoadingList.Contains(assetObjectKey)) #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}"); } } } } #endregion #region 设置下载系统参数,自定义下载请求 /// /// 设置下载系统参数,自定义下载请求。 /// /// 自定义下载器的请求委托。 public void SetDownloadSystemUnityWebRequest(UnityWebRequestDelegate downloadSystemUnityWebRequest) { YooAssets.SetDownloadSystemUnityWebRequest(downloadSystemUnityWebRequest); } public UnityEngine.Networking.UnityWebRequest CustomWebRequester(string url) { var request = new UnityEngine.Networking.UnityWebRequest(url, UnityEngine.Networking.UnityWebRequest.kHttpVerbGET); var authorization = GetAuthorization("Admin", "12345"); request.SetRequestHeader("AUTHORIZATION", authorization); return request; } private string GetAuthorization(string userName, string password) { string auth = $"{userName}:{password}"; var bytes = System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth); return $"Basic {Convert.ToBase64String(bytes)}"; } #endregion } }