obsidian/笔记文件/2.笔记/xAsset 开发 临时记录.md
2025-03-26 00:02:56 +08:00

32 KiB
Raw Permalink Blame History

#灵感/代码缓存

IResService

/****************************************************
	文件IResService.cs
	作者HuskyT
	邮箱1005240602@qq.com
	日期2021/8/6 10:29:16
	功能:资源 服务 接口
*****************************************************/

using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEngine;
using xasset;

namespace HTFramework.Core
{
    public interface IResService : IService
    {
        void ClearRes();

        void Instantiate(string key, bool isSyncLoad, Transform parent = null,
            bool instantiateInWorldSpace = false,
            Action<GameObject> completed = null);

        GameObject InstantiateSync(string key, Transform parent = null,
            bool instantiateInWorldSpace = false);

        void InstantiateAsync(string key, Transform parent = null,
            bool instantiateInWorldSpace = false, Action<GameObject> completed = null);

        void InstantiateAsync(string key, Vector3 position, Quaternion rotation,
            Transform parent = null, Action<GameObject> complete = null);

        AssetRequest LoadAsset<T>(string key, bool isSync, Action<T> complete = null) where T : UnityEngine.Object;
        T LoadAssetSync<T>(string key) where T : UnityEngine.Object;
        AssetRequest LoadAssetAsync<T>(string key, Action<T> complete = null) where T : UnityEngine.Object;

        AssetRequest LoadSprite(string key, bool isSync, Action<Sprite> complete = null);
        Sprite LoadSpriteSync(string key);

        AssetRequest LoadSpriteAsync(string key,
            Action<Sprite> complete = null);

        Texture LoadTextureSync(string key, bool pIsCompletePath );

        AssetRequest LoadTextureAsync(string key, bool pIsCompletePath,
            Action<Texture> complete = null);

        Texture2D LoadTexture2DSync(string key,bool pIsCompletePath);

        AssetRequest LoadTexture2DAsync(string key,bool pIsCompletePath,
            Action<Texture2D> complete = null);

        AudioClip LoadAudioSync(string key);

        AssetRequest LoadAudioAsync(string key,
            Action<AudioClip> complete = null);

        void ReleaseInstance(GameObject go,string key = null,Transform parent = null,bool instantiateInWorldSpace = false,Action<GameObject> complete = null);
        void Release(Material material);
        void Release(AudioClip audioClip);
        void Release(Sprite sprite);
        void Release(Texture2D tex);
        void Release(TextAsset textAsset);
        void Release(Texture texture);

        void InitShader();
    }
}

ResService

/****************************************************
	文件ResService.cs
	作者HuskyT
	邮箱1005240602@qq.com
	日期2021/8/2 20:12:27
	功能:资源 服务
*****************************************************/

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.U2D;
using xasset;
using Object = UnityEngine.Object;

namespace HTFramework.Core
{

    /// <summary>
    /// 资源缓存 结构体
    /// </summary>
    public class ResTempCache
    {
        public Transform parent;
        public bool instantiateInWorldSpace;
        public Action<GameObject> complete;
    }

    /// <summary>
    /// 资源 加载/卸载 服务(注意:异步加载的回调中不能执行同步加载,非要执行的话可以使用计时器延迟一帧执行)
    /// </summary>
    public class ResService : IResService
    {
        private Dictionary<string, List<ResTempCache>> ResTempCacheDic; //资源缓存字典
        private Dictionary<string, List<ResTempCache>> DeleteTempCacheDic; //删除资源缓存临时字典
        ResCache mResCache;
        HEnum.ContextType mContextType;
        bool mHasInit;

        public void Init(HEnum.ContextType contextType)
        {
            if (mHasInit) return;
            mHasInit = true;
            mContextType = contextType;
            mResCache = new ResCache();
            ResTempCacheDic = new Dictionary<string, List<ResTempCache>>();
            DeleteTempCacheDic = new Dictionary<string, List<ResTempCache>>();
            //图集全部勾选了Include in Build属性无需进行后期绑定
            //SpriteAtlasManager.atlasRequested += OnLoadAtlas;
        }

        public void Dispose()
        {
            //SpriteAtlasManager.atlasRequested -= OnLoadAtlas;
            if (mResCache != null)
            {
                mResCache.Dispose();
                mResCache = null;
            }
        }

        public void ClearRes()
        {
            mResCache?.Clear();
            ResTempCacheDic?.Clear();
            DeleteTempCacheDic?.Clear();
        }

        #region 实例化预制体

        /// <summary>
        /// 实例化
        /// </summary>
        /// <param name="key">address</param>
        /// <param name="isSyncLoad">是否为同步资源加载</param>
        /// <param name="parent"></param>
        /// <param name="instantiateInWorldSpace"></param>
        /// <param name="completed"></param>
        public void Instantiate(string key, bool isSyncLoad, Transform parent = null,
            bool instantiateInWorldSpace = false,
            Action<GameObject> completed = null)
        {
            if (isSyncLoad) //同步
            {
                var obj = InstantiateSync(key, parent, instantiateInWorldSpace);
                completed?.Invoke(obj);
            }
            else //异步
                InstantiateAsync(key, parent, instantiateInWorldSpace, completed);
        }

        /// <summary>
        /// 同步实例化
        /// </summary>
        /// <param name="key">address</param>
        /// <param name="parent"></param>
        /// <param name="instantiateInWorldSpace"></param>
        /// <returns></returns>
        public GameObject InstantiateSync(string key, Transform parent = null,
            bool instantiateInWorldSpace = false)
        {
            //Debug.Log($"InstantiateSync1 {key}");

            // Debug.Log("======> InstantiateSync同步实例化 "+key);
            var path = XAssetManager.Instance.GetAssetPathByName(key);
            var asset = Asset.Load(path, typeof(Object));
            var obj = Object.Instantiate(asset.asset, parent, instantiateInWorldSpace) as GameObject;
            var assetInfo = new ResCache.AssetInfo
            {
                refCount = 0,
                assetRequest = asset
            };
            mResCache.Add(obj, assetInfo);
            return obj;
        }


        /// <summary>
        /// 异步实例化
        /// </summary>
        /// <param name="key">address</param>
        /// <param name="parent"></param>
        /// <param name="instantiateInWorldSpace"></param>
        /// <param name="complete"></param>
        /// <returns></returns>
        public void InstantiateAsync(string key, Transform parent = null,
            bool instantiateInWorldSpace = false, Action<GameObject> complete = null)
        {
            if (key == "prefab_elienv_grid")
            {
                Debug.Log("sasa");
            }

            key = "prefab_item_farmisland_03";
            var path = XAssetManager.Instance.GetAssetPathByName(key);
            var asset = Asset.LoadAsync(path, typeof(Object));

            if (!ResTempCacheDic.ContainsKey(key))
            {
                List<ResTempCache> tempCachaes = new List<ResTempCache>();
                ResTempCacheDic.Add(key, tempCachaes);
            }
            
            ResTempCacheDic[key].Add
                (
                new ResTempCache
                {
                    parent = parent,
                    instantiateInWorldSpace = instantiateInWorldSpace,
                    complete = complete
                }
                );
            


            GameObject testGameObject = null;
            asset.key = key;
            asset.completed += request =>
            {
                if (DeleteTempCacheDic.ContainsKey(asset.key))
                {
                    bool checkResTemp = false; //检测资源是否缓存到字典中
                    for (int i = 0; i < DeleteTempCacheDic[asset.key].Count; i++)
                    {
                        for (int j = 0; j < ResTempCacheDic[asset.key].Count; j++)
                        {
                            if (DeleteTempCacheDic[asset.key][i].parent == ResTempCacheDic[asset.key][i].parent &&
                                DeleteTempCacheDic[asset.key][i].instantiateInWorldSpace == ResTempCacheDic[asset.key][i].instantiateInWorldSpace &&
                                DeleteTempCacheDic[asset.key][i].complete == ResTempCacheDic[asset.key][i].complete)
                            {
                                ResTempCacheDic[asset.key].RemoveAt(j);
                                checkResTemp = true;
                                break;

                            }
                        }

                        if (checkResTemp)
                        {
                            DeleteTempCacheDic[asset.key].RemoveAt(i);
                            asset.Release();
                            return;
                        }
                    }
                }
                
                var obj = Object.Instantiate(asset.asset, parent, instantiateInWorldSpace) as GameObject;
                testGameObject = obj;
                var assetInfo = new ResCache.AssetInfo
                {
                    refCount = 0,
                    assetRequest = asset
                };
                mResCache.Add(obj, assetInfo);
                complete?.Invoke(obj);
            };
            
            ReleaseInstance(testGameObject,"prefab_item_farmisland_03",parent,instantiateInWorldSpace,complete);
            
        }

        /// <summary>
        /// 异步实例化
        /// </summary>
        /// <param name="key">address</param>
        /// <param name="position"></param>
        /// <param name="rotation"></param>
        /// <param name="parent"></param>
        /// <param name="complete"></param>
        /// <returns></returns>
        public void InstantiateAsync(string key, Vector3 position, Quaternion rotation,
            Transform parent = null, Action<GameObject> complete = null)
        {
            // Debug.Log("======> InstantiateSync异步实例化 "+key);
            var path = XAssetManager.Instance.GetAssetPathByName(key);
            var asset = Asset.LoadAsync(path, typeof(Object));
            asset.completed += request =>
            {
                var obj = Object.Instantiate(asset.asset, position, rotation, parent) as GameObject;
                 var assetInfo = new ResCache.AssetInfo
                 {
                     refCount = 0,
                     assetRequest = asset
                 };
                 mResCache.Add(obj, assetInfo);
                 complete?.Invoke(obj);
            };
        }

        #endregion


        #region 加载资源-泛型

        // private bool isTest;

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <param name="key"></param>
        /// <param name="isSync">是否为同步加载</param>
        /// <param name="complete"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public AssetRequest LoadAsset<T>(string key, bool isSync,
            Action<T> complete = null) where T : Object
        {
            if (isSync)
            {
                // Debug.Log("======> LoadAsset 加载资源  "+key);
                var request = Asset.Load(key, typeof(T));
                var assetInfo = new ResCache.AssetInfo
                {
                    refCount = 0,
                    assetRequest = request
                };
                mResCache.Add<T>(request.asset as T, assetInfo);
                complete?.Invoke(request.asset as T);
                return request;
            }
            else
            {
                return LoadAssetAsync(key, complete);
            }
        }

        /// <summary>
        /// 同步加载 资源
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T LoadAssetSync<T>(string key) where T : Object
        {
            Debug.Log("======> LoadAsync 同步加载 资源  "+key);
            var request = Asset.Load(key, typeof(T));
            if (request != null)
            {
                var asset = request.asset as T;
                var assetInfo = new ResCache.AssetInfo
                {
                    refCount = 0,
                    assetRequest = request
                };
                mResCache.Add<T>(asset, assetInfo);
                return asset;
            }
            else
            {
                Debug.LogWarning("======> 资源加载失败 path =  "+ key);
            }

            return null;
        }

        /// <summary>
        /// 异步加载 资源
        /// </summary>
        /// <param name="key"></param>
        /// <param name="complete"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public AssetRequest LoadAssetAsync<T>(string key, Action<T> complete = null) where T : Object
        {
            Debug.Log("======> LoadAsync 异步加载 资源  "+key);
            var request = Asset.LoadAsync(key, typeof(T));
            request.completed += rq =>
            {
                if (rq.result == Request.Result.Success)
                {
                    var assetInfo = new ResCache.AssetInfo
                    {
                        refCount = 0,
                        assetRequest = request
                    };
                    
                    Debug.Log("预制体:"+request.asset.name);

                    // if (request.asset == null)
                    // {
                    //     Debug.LogError($"LoadAsync 异步加载 资源 null : {key}");
                    // }
                    //
                    // var asset = request.asset as T;
                    //
                    // if (asset == null)
                    // {
                    //     Debug.LogError($"LoadAsync 异步加载 资源 type error : {key}, type : {typeof(T)}");
                    // }
                    
                    mResCache.Add<T>(request.asset as T, assetInfo);
                    complete?.Invoke(request.asset as T);
                }
            };
            return request;
        }

        #endregion

        #region 加载图集回调

        /// <summary>
        /// 加载图集
        /// </summary>
        /// <param name="pAtlasName"></param>
        /// <param name="pCallback"></param>
        private void OnLoadAtlas(string pAtlasName, Action<SpriteAtlas> pCallback)
        {
            var atlasPath = XAssetManager.Instance.GetAssetPathByName(pAtlasName);
            var request = Asset.LoadAsync(atlasPath, typeof(SpriteAtlas));
            request.completed += rq =>
            {
                if (rq.result == Request.Result.Success)
                {
                    pCallback(request.asset as SpriteAtlas);
                }
                else
                {
                    Debug.LogError("图集加载失败 " + atlasPath);
                }
            };
        }

        #endregion

        #region Sprite

        public AssetRequest LoadSprite(string key, bool isSync, Action<Sprite> complete = null)
        {
            if (isSync)
            {
                // Debug.Log("======> LoadSprite   "+key);
                if (string.IsNullOrEmpty(key)) return null;
                GetAtlasSpriteName(key, out var spriteName);
                var spritePath = XAssetManager.Instance.GetAssetPathByName(spriteName);
                var request = Asset.Load(spritePath, typeof(Sprite));
                var assetInfo = new ResCache.AssetInfo
                {
                    refCount = 0,
                    assetRequest = request
                };
                mResCache.Add(request.asset, assetInfo);
                complete?.Invoke(request.asset as Sprite);
                return request;
            }
            else
            {
                return LoadSpriteAsync(key, complete);
            }
        }

        /// <summary>
        /// 同步加载 Sprite
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public Sprite LoadSpriteSync(string key)
        {
            if (string.IsNullOrEmpty(key)) return null;
            GetAtlasSpriteName(key, out var spriteName);
            var spritePath = XAssetManager.Instance.GetAssetPathByName(spriteName);
            var request = Asset.Load(spritePath, typeof(Sprite));
            var sprite = request.asset as Sprite;
            var assetInfo = new ResCache.AssetInfo
            {
                refCount = 0,
                assetRequest = request
            };
            mResCache.Add(request.asset, assetInfo);
            return sprite;
        }

        /// <summary>
        /// 异步加载 Sprite
        /// </summary>
        /// <param name="key"></param>
        /// <param name="complete"></param>
        /// <returns></returns>
        public AssetRequest LoadSpriteAsync(string key,
            Action<Sprite> complete = null)
        {
            // Debug.Log("======> LoadSpriteAsync  异步加载 Sprite "+key);
            if (string.IsNullOrEmpty(key)) return null;
            GetAtlasSpriteName(key, out var spriteName);
            var spritePath = XAssetManager.Instance.GetAssetPathByName(spriteName);
            var request = Asset.LoadAsync(spritePath, typeof(Sprite));
            request.completed += rq =>
            {
                if (rq.result == Request.Result.Success)
                {
                    var sprite = request.asset as Sprite;
                    var assetInfo = new ResCache.AssetInfo
                    {
                        refCount = 0,
                        assetRequest = request
                    };
                    
                    // if (request.asset == null)
                    // {
                    //     Debug.LogError($"LoadSpriteAsync 异步加载 Sprite null : {key}");
                    // }
                    
                    mResCache.Add(request.asset, assetInfo);
                    complete?.Invoke(sprite);
                }
                else
                {
                    Debug.LogError("图集加载失败 " + spritePath);
                }
            };
            return request;
        }

        private void GetAtlasSpriteName(string key, out string spriteName)
        {
            int startIndex = key.IndexOf('[');
            if (startIndex < 0)
            {
                spriteName = key;
                return;
            }

            int endIndex = key.IndexOf(']');
            if (endIndex < 0)
            {
                spriteName = key;
                return;
            }

            startIndex += 1;

            spriteName = key.Substring(startIndex, endIndex - startIndex);

            #region 注释掉正则匹配用法
            //if (!key.Contains("[") || !key.Contains("]"))
            //{
            //    spriteName = key;
            //    return;
            //}

            //// 获取中括号内的字符串-Sprite名字
            //string pattern = @"\[(.*?)\]";
            //Match match = Regex.Match(key, pattern);
            //spriteName = match.Groups[1].Value;
            #endregion
        }

        #endregion


        #region Texture

        /// <summary>
        /// 同步加载 Texture
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public Texture LoadTextureSync(string key, bool pIsCompletePath)
        {
            // Debug.Log("======> LoadTextureSync  同步加载 Texture "+key);
            string varPath = key;
            if (pIsCompletePath == false)
            {
                varPath = XAssetManager.Instance.GetAssetPathByName(key);
            }

            var request = Asset.Load(varPath, typeof(Texture));
            var asset = request.asset as Texture;
            var assetInfo = new ResCache.AssetInfo
            {
                refCount = 0,
                assetRequest = request
            };
            mResCache.Add(request.asset, assetInfo);
            return asset;
        }

        /// <summary>
        /// 异步加载 Texture
        /// </summary>
        /// <param name="key"></param>
        /// <param name="complete"></param>
        /// <returns></returns>
        public AssetRequest LoadTextureAsync(string key, bool pIsCompletePath,
            Action<Texture> complete = null)
        {
            // Debug.Log("======> LoadTextureAsync  异步加载 Texture "+key);
            var request = Asset.LoadAsync(key, typeof(Texture));
            request.completed += rq =>
            {
                if (rq.result == Request.Result.Success)
                {
                    var assetInfo = new ResCache.AssetInfo
                    {
                        refCount = 0,
                        assetRequest = request
                    };
                    mResCache.Add(request.asset, assetInfo);
                    complete?.Invoke(request.asset as Texture);
                }
            };
            return request;
        }

        public Texture2D LoadTexture2DSync(string key, bool pIsCompletePath)
        {
            // Debug.Log("======> LoadTexture2DSync 同步加载 Texture "+key);
            string varPath = key;
            if (pIsCompletePath == false)
            {
                varPath = XAssetManager.Instance.GetAssetPathByName(key);
            }

            var request = Asset.Load(varPath, typeof(Texture2D));
            var asset = request.asset as Texture2D;
            var assetInfo = new ResCache.AssetInfo
            {
                refCount = 0,
                assetRequest = request
            };
            mResCache.Add(request.asset, assetInfo);
            return asset;
        }

        public AssetRequest LoadTexture2DAsync(string key, bool pIsCompletePath,
            Action<Texture2D> complete = null)
        {
            // Debug.Log("======> LoadTexture2DAsync  异步加载 Texture "+key);
            string varPath = key;
            if (pIsCompletePath == false)
            {
                varPath = XAssetManager.Instance.GetAssetPathByName(key);
            }

            var request = Asset.LoadAsync(varPath, typeof(Texture2D));
            request.completed += rq =>
            {
                if (rq.result == Request.Result.Success)
                {
                    var assetInfo = new ResCache.AssetInfo
                    {
                        refCount = 0,
                        assetRequest = request
                    };
                    mResCache.Add(request.asset, assetInfo);
                    complete?.Invoke(request.asset as Texture2D);
                }
            };
            return request;
        }

        #endregion


        #region AudioClip

        /// <summary>
        /// 同步加载 AudioClip
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public AudioClip LoadAudioSync(string key)
        {
            // Debug.Log("======> LoadAudioSync 同步加载 AudioClip "+key);
            var request = Asset.Load(key, typeof(AudioClip));
            var asset = request.asset as AudioClip;
            var assetInfo = new ResCache.AssetInfo
            {
                refCount = 0,
                assetRequest = request
            };
            mResCache.Add(request.asset, assetInfo);
            return asset;
        }

        /// <summary>
        /// 异步加载 AudioClip
        /// </summary>
        /// <param name="key"></param>
        /// <param name="complete"></param>
        /// <returns></returns>
        public AssetRequest LoadAudioAsync(string key,
            Action<AudioClip> complete = null)
        {
            // Debug.Log("======> LoadAudioAsync 异步加载 AudioClip "+key);
            var request = Asset.LoadAsync(key, typeof(AudioClip));
            request.completed += rq =>
            {
                if (rq.result == Request.Result.Success)
                {
                    var assetInfo = new ResCache.AssetInfo
                    {
                        refCount = 0,
                        assetRequest = request
                    };
                    mResCache.Add(request.asset, assetInfo);
                    complete?.Invoke(request.asset as AudioClip);
                }
            };
            return request;
        }

        #endregion


        #region Shader
        
        public void InitShader()
        {
#if !UNITY_EDITOR
            string[] list =
            {
                "Assets/ThirdParty/TextMesh Pro/Shaders/TMP_SDF-Mobile.shader",

            };
            for (int i = 0; i < list.Length; i++)
            {
                LoadShader(list[i]);   
            }
#endif
            
        }
        /// <summary>
        /// 加载shader
        /// </summary>
        /// <param name="varPath"></param>
        private void LoadShader( string varPath)
        {
            var request = Asset.LoadAsync(varPath, typeof(Shader));
            request.completed += rq =>
            {
                if (rq.result == Request.Result.Success)
                {
                    var assetInfo = new ResCache.AssetInfo
                    {
                        refCount = 0,
                        assetRequest = request
                    };
                    mResCache.Add(request.asset, assetInfo);
                }
            };
        }
        #endregion
        #region 卸载资源

        /// <summary>
        /// GameObject销毁时卸载相关资源
        /// </summary>
        /// <param name="go"></param>
        public void ReleaseInstance(GameObject go,string key =null,Transform parent = null,bool instantiateInWorldSpace = false,Action<GameObject> complete = null)
        {
            if ( key != null)
            {
                if (go == null)
                {
                    if (!DeleteTempCacheDic.ContainsKey(key))
                    {
                        List<ResTempCache> delList = new List<ResTempCache>();
                        DeleteTempCacheDic.Add(key,delList);
                    }
                
                    DeleteTempCacheDic[key].Add
                    (
                        new ResTempCache
                        {
                            parent = parent,
                            instantiateInWorldSpace = instantiateInWorldSpace,
                            complete = complete
                        }
                    );
                }
                else
                {
                    if (ResTempCacheDic.ContainsKey(key))
                    {
                        for (int i = 0; i < ResTempCacheDic[key].Count; i++)
                        {
                            if (ResTempCacheDic[key][i].parent == parent && 
                                ResTempCacheDic[key][i].instantiateInWorldSpace == instantiateInWorldSpace && 
                                ResTempCacheDic[key][i].complete == complete)
                            {
                                ResTempCacheDic[key].RemoveAt(i);
                                break;
                            }
                        }
                    }
                }
            }

            mResCache.Remove(go, out var assetInfo);
            assetInfo?.assetRequest.Release();
            GameObject.Destroy(go);
        }
        

        public void Release(Texture texture)
        {
            if (mResCache == null)
            {
                return;
            }
            mResCache.Remove(texture, out var assetInfo);
            assetInfo?.assetRequest.Release();
        }

        public void Release(Sprite sprite)
        {
            if (mResCache == null)
            {
                return;
            }
            mResCache.Remove(sprite, out var assetInfo);
            assetInfo?.assetRequest.Release();
        }

        public void Release(Material material)
        {
            mResCache.Remove(material, out var assetInfo);
            assetInfo?.assetRequest.Release();
        }

        public void Release(AudioClip audioClip)
        {
            mResCache.Remove(audioClip, out var assetInfo);
            assetInfo?.assetRequest.Release();
        }

        public void Release(Texture2D tex)
        {
            mResCache.Remove(tex, out var assetInfo);
            assetInfo?.assetRequest.Release();
        }

        public void Release(TextAsset textAsset)
        {
            mResCache.Remove(textAsset, out var assetInfo);
            assetInfo?.assetRequest.Release();
        }

        #endregion
    }
}

AssetRequest

using System;
using System.Collections.Generic;
using Object = UnityEngine.Object;

namespace xasset
{
    public class AssetRequest : LoadRequest
    {
        private static readonly Queue<AssetRequest> Unused = new Queue<AssetRequest>();
        private static readonly Dictionary<string, AssetRequest> Loaded = new Dictionary<string, AssetRequest>();
        private AssetRequestHandler handler { get; set; }

        public Object asset { get; set; }
        public Object[] assets { get; set; }
        public bool isAll { get; private set; }
        public ManifestAsset info { get; private set; }
        public Type type { get; private set; }

        public string key;

        public static Func<AssetRequest, AssetRequestHandler> CreateHandler { get; set; } = AssetRequestHandlerRuntime.CreateInstance;

        protected override void OnStart()
        {
            handler.OnStart();
        }

        protected override void OnWaitForCompletion()
        {
            handler.WaitForCompletion();
        }

        protected override void OnUpdated()
        {
            handler.Update();
        }

        protected override void OnDispose()
        {
            Remove(this);
            handler.Dispose();
            asset = null;
            assets = null;
            isAll = false;
        }

        private static void Remove(AssetRequest request)
        {
            Loaded.Remove($"{request.info.path}[{request.type.Name}]");
            Unused.Enqueue(request);
        }

        internal static T Get<T>(string path) where T : Object
        {
            if (!Assets.Versions.TryGetAsset(path, out var info)) return null;
            if (!Loaded.TryGetValue($"{info.path}[{typeof(T).Name}]", out var request)) return null;
            return request.asset as T;
        }

        internal static T[] GetAll<T>(string path) where T : Object
        {
            if (!Assets.Versions.TryGetAsset(path, out var info)) return null;
            if (!Loaded.TryGetValue($"{info.path}[{typeof(T).Name}]", out var request)) return null;
            return request.assets as T[];
        }

        internal static AssetRequest Load(string path, Type type, bool isAll = false)
        {
            if (!Assets.Versions.TryGetAsset(path, out var info))
            {
                Logger.E($"File not found:{path}");
                return null;
            }

            var key = $"{info.path}[{type.Name}]";
            if (!Loaded.TryGetValue(key, out var request))
            {
                request = Unused.Count > 0 ? Unused.Dequeue() : new AssetRequest();
                request.Reset();
                request.type = type;
                request.info = info;
                request.isAll = isAll;
                request.path = info.path;
                request.handler = CreateHandler(request);


                Loaded[key] = request;
            }

            request.LoadAsync();
            return request;
        }
        
        private string GenerateRandomString(int length)
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
            var random = new Random();
            var stringChars = new char[length];
        
            for (int i = 0; i < stringChars.Length; i++)
            {
                stringChars[i] = chars[random.Next(chars.Length)];
            }

            var randomString = new string(stringChars);
            return randomString;
        }
    }
}