using System; using System.Collections; using System.Collections.Generic; using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; using YooAsset; #if UNITY_WEBGL && WEIXINMINIGAME && !UNITY_EDITOR using WeChatWASM; #endif namespace TEngine { /// /// 资源管理器。 /// internal sealed partial class ResourceModule : Module, IResourceModule { /// /// 默认资源包名称。 /// public string DefaultPackageName { get; set; } = "DefaultPackage"; /// /// 资源系统运行模式。 /// public EPlayMode PlayMode { get; set; } = EPlayMode.OfflinePlayMode; public EncryptionType EncryptionType { get; set; } = EncryptionType.None; /// /// 设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒) /// public long Milliseconds { get; set; } = 30; /// /// 获取游戏框架模块优先级。 /// /// 优先级较高的模块会优先轮询,并且关闭操作会后进行。 public override int Priority => 4; public override void OnInit() { } public override void Shutdown() { } /// /// 资源服务器地址。 /// public string HostServerURL { get; set; } public string FallbackHostServerURL { get; set; } 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; } /// /// 是否支持边玩边下载。 /// public bool UpdatableWhilePlaying { 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 objectPoolManager = ModuleSystem.GetModule(); SetObjectPoolModule(objectPoolManager); } public async UniTask InitPackage(string packageName) { #if UNITY_EDITOR //编辑器模式使用。 EPlayMode playMode = (EPlayMode)UnityEditor.EditorPrefs.GetInt("EditorPlayMode"); Log.Warning($"Editor Module Used :{playMode}"); #else //运行时使用。 EPlayMode playMode = (EPlayMode)PlayMode; #endif if (PackageMap.TryGetValue(packageName, out var resourcePackage)) { if (resourcePackage.InitializeStatus is EOperationStatus.Processing or EOperationStatus.Succeed) { Log.Error($"ResourceSystem has already init package : {packageName}"); return null; } else { PackageMap.Remove(packageName); } } // 创建资源包裹类 var package = YooAssets.TryGetPackage(packageName); if (package == null) { package = YooAssets.CreatePackage(packageName); } PackageMap[packageName] = package; // 编辑器下的模拟模式 InitializationOperation initializationOperation = null; if (playMode == EPlayMode.EditorSimulateMode) { var buildResult = EditorSimulateModeHelper.SimulateBuild(packageName); var packageRoot = buildResult.PackageRootDirectory; var createParameters = new EditorSimulateModeParameters(); createParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot); initializationOperation = package.InitializeAsync(createParameters); } IDecryptionServices decryptionServices = CreateDecryptionServices(); // 单机运行模式 if (playMode == EPlayMode.OfflinePlayMode) { var createParameters = new OfflinePlayModeParameters(); createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(decryptionServices); initializationOperation = package.InitializeAsync(createParameters); } // 联机运行模式 if (playMode == EPlayMode.HostPlayMode) { string defaultHostServer = HostServerURL; string fallbackHostServer = FallbackHostServerURL; IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer); var createParameters = new HostPlayModeParameters(); createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(decryptionServices); createParameters.CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices, decryptionServices); initializationOperation = package.InitializeAsync(createParameters); } // WebGL运行模式 if (playMode == EPlayMode.WebPlayMode) { var createParameters = new WebPlayModeParameters(); IWebDecryptionServices webDecryptionServices = CreateWebDecryptionServices(); string defaultHostServer = HostServerURL; string fallbackHostServer = FallbackHostServerURL; IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer); #if UNITY_WEBGL && WEIXINMINIGAME && !UNITY_EDITOR Log.Info("=======================WEIXINMINIGAME======================="); // 注意:如果有子目录,请修改此处! string packageRoot = $"{WeChatWASM.WX.env.USER_DATA_PATH}/__GAME_FILE_CACHE"; createParameters.WebServerFileSystemParameters = WechatFileSystemCreater.CreateFileSystemParameters(packageRoot, remoteServices, webDecryptionServices); #else Log.Info("=======================UNITY_WEBGL======================="); createParameters.WebRemoteFileSystemParameters = FileSystemParameters.CreateDefaultWebRemoteFileSystemParameters(remoteServices, webDecryptionServices); createParameters.WebServerFileSystemParameters = FileSystemParameters.CreateDefaultWebServerFileSystemParameters(webDecryptionServices); #endif initializationOperation = package.InitializeAsync(createParameters); } await initializationOperation.ToUniTask(); Log.Info($"Init resource package version : {initializationOperation?.Status}"); return initializationOperation; } /// /// 创建解密服务。 /// private IDecryptionServices CreateDecryptionServices() { return EncryptionType switch { EncryptionType.FileOffSet => new FileOffsetDecryption(), EncryptionType.FileStream => new FileStreamDecryption(), _ => null }; } /// /// 创建Web解密服务。 /// private IWebDecryptionServices CreateWebDecryptionServices() { return EncryptionType switch { EncryptionType.FileOffSet => new FileOffsetWebDecryption(), EncryptionType.FileStream => new FileStreamWebDecryption(), _ => null }; } /// /// 获取当前资源包版本。 /// /// 指定资源包的名称。不传使用默认资源包 /// 资源包版本。 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); } public void SetRemoteServicesUrl(string defaultHostServer, string fallbackHostServer) { HostServerURL = defaultHostServer; FallbackHostServerURL = fallbackHostServer; } /// /// 向网络端请求并更新清单 /// /// 更新的包裹版本 /// 超时时间(默认值: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 Downloader { get; set; } /// /// 创建资源下载器,用于下载当前资源版本所有的资源包文件。 /// /// 指定资源包的名称。不传使用默认资源包 public ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = "") { ResourcePackage package = null; if (string.IsNullOrEmpty(customPackageName)) { package = YooAssets.GetPackage(this.DefaultPackageName); } else { package = YooAssets.GetPackage(customPackageName); } Downloader = package.CreateResourceDownloader(DownloadingMaxNum, FailedTryAgain); return Downloader; } /// /// 清理包裹未使用的缓存文件。 /// /// 文件清理方式。 /// 指定资源包的名称。不传使用默认资源包 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 } }