using System; using System.Threading; using Cysharp.Threading.Tasks; using UnityEngine; using YooAsset; namespace TEngine { /// /// 资源组件。 /// [DisallowMultipleComponent] public class ResourceModule : Module { #region Propreties private const int DefaultPriority = 0; private IResourceManager m_ResourceManager; private bool m_ForceUnloadUnusedAssets = false; private bool m_PreorderUnloadUnusedAssets = false; private bool m_PerformGCCollect = false; private AsyncOperation m_AsyncOperation = null; private float m_LastUnloadUnusedAssetsOperationElapseSeconds = 0f; [SerializeField] private float m_MinUnloadUnusedAssetsInterval = 60f; [SerializeField] private float m_MaxUnloadUnusedAssetsInterval = 300f; [SerializeField] private bool m_UseSystemUnloadUnusedAssets = true; /// /// 当前最新的包裹版本。 /// public string PackageVersion { set; get; } /// /// 资源包名称。 /// [SerializeField] private string packageName = "DefaultPackage"; /// /// 资源包名称。 /// public string PackageName { get => packageName; set => packageName = value; } /// /// 资源系统运行模式。 /// [SerializeField] private EPlayMode playMode = EPlayMode.EditorSimulateMode; /// /// 资源系统运行模式。 /// 编辑器内优先使用。 /// public EPlayMode PlayMode { get { #if UNITY_EDITOR //编辑器模式使用。 return (EPlayMode)UnityEditor.EditorPrefs.GetInt("EditorPlayMode"); #else if (playMode == EPlayMode.EditorSimulateMode) { playMode = EPlayMode.OfflinePlayMode; } //运行时使用。 return playMode; #endif } set { #if UNITY_EDITOR playMode = value; #endif } } /// /// 是否支持边玩边下载。 /// [SerializeField] private bool m_UpdatableWhilePlaying = false; /// /// 是否支持边玩边下载。 /// public bool UpdatableWhilePlaying => m_UpdatableWhilePlaying; /// /// 下载文件校验等级。 /// public EVerifyLevel VerifyLevel = EVerifyLevel.Middle; [SerializeField] private ReadWritePathType m_ReadWritePathType = ReadWritePathType.Unspecified; /// /// 设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒) /// [SerializeField] public long Milliseconds = 30; public int m_DownloadingMaxNum = 10; /// /// 获取或设置同时最大下载数目。 /// public int DownloadingMaxNum { get => m_DownloadingMaxNum; set => m_DownloadingMaxNum = value; } public int m_FailedTryAgain = 3; public int FailedTryAgain { get => m_FailedTryAgain; set => m_FailedTryAgain = value; } /// /// 获取当前资源适用的游戏版本号。 /// public string ApplicableGameVersion => m_ResourceManager.ApplicableGameVersion; /// /// 获取当前内部资源版本号。 /// public int InternalResourceVersion => m_ResourceManager.InternalResourceVersion; /// /// 获取资源读写路径类型。 /// public ReadWritePathType ReadWritePathType => m_ReadWritePathType; /// /// 获取或设置无用资源释放的最小间隔时间,以秒为单位。 /// public float MinUnloadUnusedAssetsInterval { get => m_MinUnloadUnusedAssetsInterval; set => m_MinUnloadUnusedAssetsInterval = value; } /// /// 获取或设置无用资源释放的最大间隔时间,以秒为单位。 /// public float MaxUnloadUnusedAssetsInterval { get => m_MaxUnloadUnusedAssetsInterval; set => m_MaxUnloadUnusedAssetsInterval = value; } /// /// 使用系统释放无用资源策略。 /// public bool UseSystemUnloadUnusedAssets { get => m_UseSystemUnloadUnusedAssets; set => m_UseSystemUnloadUnusedAssets = value; } /// /// 获取无用资源释放的等待时长,以秒为单位。 /// public float LastUnloadUnusedAssetsOperationElapseSeconds => m_LastUnloadUnusedAssetsOperationElapseSeconds; /// /// 获取资源只读路径。 /// public string ReadOnlyPath => m_ResourceManager?.ReadOnlyPath; /// /// 获取资源读写路径。 /// public string ReadWritePath => m_ResourceManager?.ReadWritePath; [SerializeField] private float m_AssetAutoReleaseInterval = 60f; [SerializeField] private int m_AssetCapacity = 64; [SerializeField] private float m_AssetExpireTime = 60f; [SerializeField] private int m_AssetPriority = 0; /// /// 获取或设置资源对象池自动释放可释放对象的间隔秒数。 /// public float AssetAutoReleaseInterval { get { return m_ResourceManager.AssetAutoReleaseInterval; } set { m_ResourceManager.AssetAutoReleaseInterval = m_AssetAutoReleaseInterval = value; } } /// /// 获取或设置资源对象池的容量。 /// public int AssetCapacity { get { return m_ResourceManager.AssetCapacity; } set { m_ResourceManager.AssetCapacity = m_AssetCapacity = value; } } /// /// 获取或设置资源对象池对象过期秒数。 /// public float AssetExpireTime { get { return m_ResourceManager.AssetExpireTime; } set { m_ResourceManager.AssetExpireTime = m_AssetExpireTime = value; } } /// /// 获取或设置资源对象池的优先级。 /// public int AssetPriority { get { return m_ResourceManager.AssetPriority; } set { m_ResourceManager.AssetPriority = m_AssetPriority = value; } } #endregion private void Start() { RootModule rootModule = ModuleSystem.GetModule(); if (rootModule == null) { Log.Fatal("Root module is invalid."); return; } m_ResourceManager = ModuleImpSystem.GetModule(); if (m_ResourceManager == null) { Log.Fatal("Resource module is invalid."); return; } if (PlayMode == EPlayMode.EditorSimulateMode) { Log.Info("During this run, Game Framework will use editor resource files, which you should validate first."); #if !UNITY_EDITOR PlayMode = EPlayMode.OfflinePlayMode; #endif } m_ResourceManager.SetReadOnlyPath(Application.streamingAssetsPath); if (m_ReadWritePathType == ReadWritePathType.TemporaryCache) { m_ResourceManager.SetReadWritePath(Application.temporaryCachePath); } else { if (m_ReadWritePathType == ReadWritePathType.Unspecified) { m_ReadWritePathType = ReadWritePathType.PersistentData; } m_ResourceManager.SetReadWritePath(Application.persistentDataPath); } m_ResourceManager.DefaultPackageName = PackageName; m_ResourceManager.PlayMode = PlayMode; m_ResourceManager.VerifyLevel = VerifyLevel; m_ResourceManager.Milliseconds = Milliseconds; m_ResourceManager.InstanceRoot = transform; m_ResourceManager.HostServerURL = SettingsUtils.GetResDownLoadPath(); m_ResourceManager.Initialize(); m_ResourceManager.AssetAutoReleaseInterval = m_AssetAutoReleaseInterval; m_ResourceManager.AssetCapacity = m_AssetCapacity; m_ResourceManager.AssetExpireTime = m_AssetExpireTime; m_ResourceManager.AssetPriority = m_AssetPriority; Log.Info($"ResourceComponent Run Mode:{PlayMode}"); } /// /// 初始化操作。 /// /// public async UniTask InitPackage(string packageName = "") { if (m_ResourceManager == null) { Log.Fatal("Resource component is invalid."); return null; } return await m_ResourceManager.InitPackage(string.IsNullOrEmpty(packageName) ? PackageName:packageName); } #region 版本更新 /// /// 获取当前资源包版本。 /// /// 指定资源包的名称。不传使用默认资源包 /// 资源包版本。 public string GetPackageVersion(string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(PackageName) : YooAssets.GetPackage(customPackageName); if (package == null) { return string.Empty; } return package.GetPackageVersion(); } /// /// 异步更新最新包的版本。 /// /// 请求URL是否需要带时间戳。 /// 超时时间。 /// 指定资源包的名称。不传使用默认资源包 /// 请求远端包裹的最新版本操作句柄。 public UpdatePackageVersionOperation UpdatePackageVersionAsync(bool appendTimeTicks = false, int timeout = 60, string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(PackageName) : YooAssets.GetPackage(customPackageName); return package.UpdatePackageVersionAsync(appendTimeTicks, timeout); } /// /// 向网络端请求并更新清单 /// /// 更新的包裹版本 /// 更新成功后自动保存版本号,作为下次初始化的版本。 /// 超时时间(默认值:60秒) /// 指定资源包的名称。不传使用默认资源包 public UpdatePackageManifestOperation UpdatePackageManifestAsync(string packageVersion, bool autoSaveVersion = true, int timeout = 60, string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(PackageName) : YooAssets.GetPackage(customPackageName); return package.UpdatePackageManifestAsync(packageVersion, autoSaveVersion, timeout); } /// /// 资源下载器,用于下载当前资源版本所有的资源包文件。 /// public ResourceDownloaderOperation Downloader { get; set; } /// /// 创建资源下载器,用于下载当前资源版本所有的资源包文件。 /// /// 指定资源包的名称。不传使用默认资源包 public ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = "") { if (string.IsNullOrEmpty(customPackageName)) { var package = YooAssets.GetPackage(PackageName); Downloader = package.CreateResourceDownloader(DownloadingMaxNum, FailedTryAgain); return Downloader; } else { var package = YooAssets.GetPackage(customPackageName); Downloader = package.CreateResourceDownloader(DownloadingMaxNum, FailedTryAgain); return Downloader; } } /// /// 清理包裹未使用的缓存文件。 /// /// 指定资源包的名称。不传使用默认资源包 public ClearUnusedCacheFilesOperation ClearUnusedCacheFilesAsync(string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(PackageName) : YooAssets.GetPackage(customPackageName); return package.ClearUnusedCacheFilesAsync(); } /// /// 清理沙盒路径。 /// /// 指定资源包的名称。不传使用默认资源包 public void ClearSandbox(string customPackageName = "") { var package = string.IsNullOrEmpty(customPackageName) ? YooAssets.GetPackage(PackageName) : YooAssets.GetPackage(customPackageName); package.ClearPackageSandbox(); } #endregion #region 获取资源 /// /// 检查资源是否存在。 /// /// 要检查资源的名称。 /// 指定资源包的名称。不传使用默认资源包 /// 检查资源是否存在的结果。 public HasAssetResult HasAsset(string location, string customPackageName = "") { return m_ResourceManager.HasAsset(location, packageName: customPackageName); } /// /// 检查资源定位地址是否有效。 /// /// 资源的定位地址 /// 指定资源包的名称。不传使用默认资源包 public bool CheckLocationValid(string location, string customPackageName = "") { return m_ResourceManager.CheckLocationValid(location, packageName: customPackageName); } /// /// 获取资源信息列表。 /// /// 资源标签。 /// 指定资源包的名称。不传使用默认资源包 /// 资源信息列表。 public AssetInfo[] GetAssetInfos(string resTag, string customPackageName = "") { return m_ResourceManager.GetAssetInfos(resTag, packageName: customPackageName); } /// /// 获取资源信息列表。 /// /// 资源标签列表。 /// 指定资源包的名称。不传使用默认资源包 /// 资源信息列表。 public AssetInfo[] GetAssetInfos(string[] tags, string customPackageName = "") { return m_ResourceManager.GetAssetInfos(tags, packageName: customPackageName); } /// /// 获取资源信息。 /// /// 资源的定位地址。 /// 指定资源包的名称。不传使用默认资源包 /// 资源信息。 public AssetInfo GetAssetInfo(string location, string customPackageName = "") { return m_ResourceManager.GetAssetInfo(location, packageName: customPackageName); } #endregion #region 加载资源 /// /// 异步加载资源。 /// /// 资源的定位地址。 /// 要加载资源的类型。 /// 加载资源回调函数集。 /// 用户自定义数据。 /// 指定资源包的名称。不传使用默认资源包。 public void LoadAssetAsync(string location, Type assetType, LoadAssetCallbacks loadAssetCallbacks, object userData = null, string packageName = "") { LoadAssetAsync(location, assetType, DefaultPriority, loadAssetCallbacks, userData, packageName); } /// /// 异步加载资源。 /// /// 资源的定位地址。 /// 要加载资源的类型。 /// 加载资源的优先级。 /// 加载资源回调函数集。 /// 用户自定义数据。 /// 指定资源包的名称。不传使用默认资源包。 public void LoadAssetAsync(string location, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "") { if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); return; } m_ResourceManager.LoadAssetAsync(location, assetType, priority, loadAssetCallbacks, userData, packageName); } /// /// 同步加载资源。 /// /// 资源的定位地址。 /// 指定资源包的名称。不传使用默认资源包。 /// 要加载资源的类型。 /// 资源实例。 public T LoadAsset(string location, string packageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); return null; } return m_ResourceManager.LoadAsset(location, packageName); } /// /// 同步加载游戏物体并实例化。 /// /// 资源的定位地址。 /// 资源实例父节点。 /// 指定资源包的名称。不传使用默认资源包。 /// 资源实例。 public GameObject LoadGameObject(string location, Transform parent = null, string packageName = "") { if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); return null; } return m_ResourceManager.LoadGameObject(location, parent, packageName); } /// /// 异步加载资源。 /// /// 资源的定位地址。 /// 回调函数。 /// 指定资源包的名称。不传使用默认资源包 /// 要加载资源的类型。 public void LoadAsset(string location, Action callback, string customPackageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); return; } m_ResourceManager.LoadAsset(location, callback, packageName: customPackageName); } /// /// 异步加载资源。 /// /// 资源定位地址。 /// 取消操作Token。 /// 指定资源包的名称。不传使用默认资源包。 /// 要加载资源的类型。 /// 异步资源实例。 public async UniTask LoadAssetAsync(string location, CancellationToken cancellationToken = default, string packageName = "") where T : UnityEngine.Object { if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); return null; } return await m_ResourceManager.LoadAssetAsync(location, cancellationToken, packageName); } /// /// 异步加载游戏物体并实例化。 /// /// 资源定位地址。 /// 资源实例父节点。 /// 取消操作Token。 /// 指定资源包的名称。不传使用默认资源包。 /// 异步游戏物体实例。 public async UniTask LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "") { if (string.IsNullOrEmpty(location)) { Log.Error("Asset name is invalid."); return null; } return await m_ResourceManager.LoadGameObjectAsync(location, parent, cancellationToken, packageName); } internal 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); } internal 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 卸载资源 /// /// 卸载资源。 /// /// 要卸载的资源。 public void UnloadAsset(object asset) { if (asset == null) { return; } m_ResourceManager.UnloadAsset(asset); } #endregion #region 释放资源 /// /// 强制执行释放未被使用的资源。 /// /// 是否使用垃圾回收。 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; } } private void Update() { m_LastUnloadUnusedAssetsOperationElapseSeconds += Time.unscaledDeltaTime; if (m_AsyncOperation == 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) { m_ResourceManager.UnloadUnusedAssets(); } } if (m_AsyncOperation is { isDone: true }) { m_AsyncOperation = null; if (m_PerformGCCollect) { Log.Info("GC.Collect..."); m_PerformGCCollect = false; GC.Collect(); } } } #endregion } }