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