#灵感/代码缓存 ## IResService ``` cs /**************************************************** 文件: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 completed = null); GameObject InstantiateSync(string key, Transform parent = null, bool instantiateInWorldSpace = false); void InstantiateAsync(string key, Transform parent = null, bool instantiateInWorldSpace = false, Action completed = null); void InstantiateAsync(string key, Vector3 position, Quaternion rotation, Transform parent = null, Action complete = null); AssetRequest LoadAsset(string key, bool isSync, Action complete = null) where T : UnityEngine.Object; T LoadAssetSync(string key) where T : UnityEngine.Object; AssetRequest LoadAssetAsync(string key, Action complete = null) where T : UnityEngine.Object; AssetRequest LoadSprite(string key, bool isSync, Action complete = null); Sprite LoadSpriteSync(string key); AssetRequest LoadSpriteAsync(string key, Action complete = null); Texture LoadTextureSync(string key, bool pIsCompletePath ); AssetRequest LoadTextureAsync(string key, bool pIsCompletePath, Action complete = null); Texture2D LoadTexture2DSync(string key,bool pIsCompletePath); AssetRequest LoadTexture2DAsync(string key,bool pIsCompletePath, Action complete = null); AudioClip LoadAudioSync(string key); AssetRequest LoadAudioAsync(string key, Action complete = null); void ReleaseInstance(GameObject go,string key = null,Transform parent = null,bool instantiateInWorldSpace = false,Action 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 ``` cs /**************************************************** 文件: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 { /// /// 资源缓存 结构体 /// public class ResTempCache { public Transform parent; public bool instantiateInWorldSpace; public Action complete; } /// /// 资源 加载/卸载 服务(注意:异步加载的回调中不能执行同步加载,非要执行的话可以使用计时器延迟一帧执行) /// public class ResService : IResService { private Dictionary> ResTempCacheDic; //资源缓存字典 private Dictionary> 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>(); DeleteTempCacheDic = new Dictionary>(); //图集全部勾选了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 实例化预制体 /// /// 实例化 /// /// address /// 是否为同步资源加载 /// /// /// public void Instantiate(string key, bool isSyncLoad, Transform parent = null, bool instantiateInWorldSpace = false, Action completed = null) { if (isSyncLoad) //同步 { var obj = InstantiateSync(key, parent, instantiateInWorldSpace); completed?.Invoke(obj); } else //异步 InstantiateAsync(key, parent, instantiateInWorldSpace, completed); } /// /// 同步实例化 /// /// address /// /// /// 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; } /// /// 异步实例化 /// /// address /// /// /// /// public void InstantiateAsync(string key, Transform parent = null, bool instantiateInWorldSpace = false, Action 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 tempCachaes = new List(); 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); } /// /// 异步实例化 /// /// address /// /// /// /// /// public void InstantiateAsync(string key, Vector3 position, Quaternion rotation, Transform parent = null, Action 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; /// /// 加载资源 /// /// /// 是否为同步加载 /// /// /// public AssetRequest LoadAsset(string key, bool isSync, Action 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(request.asset as T, assetInfo); complete?.Invoke(request.asset as T); return request; } else { return LoadAssetAsync(key, complete); } } /// /// 同步加载 资源 /// /// /// /// public T LoadAssetSync(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(asset, assetInfo); return asset; } else { Debug.LogWarning("======> 资源加载失败 path = "+ key); } return null; } /// /// 异步加载 资源 /// /// /// /// /// public AssetRequest LoadAssetAsync(string key, Action 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(request.asset as T, assetInfo); complete?.Invoke(request.asset as T); } }; return request; } #endregion #region 加载图集回调 /// /// 加载图集 /// /// /// private void OnLoadAtlas(string pAtlasName, Action 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 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); } } /// /// 同步加载 Sprite /// /// /// 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; } /// /// 异步加载 Sprite /// /// /// /// public AssetRequest LoadSpriteAsync(string key, Action 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 /// /// 同步加载 Texture /// /// /// 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; } /// /// 异步加载 Texture /// /// /// /// public AssetRequest LoadTextureAsync(string key, bool pIsCompletePath, Action 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 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 /// /// 同步加载 AudioClip /// /// /// 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; } /// /// 异步加载 AudioClip /// /// /// /// public AssetRequest LoadAudioAsync(string key, Action 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 } /// /// 加载shader /// /// 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 卸载资源 /// /// GameObject销毁时卸载相关资源 /// /// public void ReleaseInstance(GameObject go,string key =null,Transform parent = null,bool instantiateInWorldSpace = false,Action complete = null) { if ( key != null) { if (go == null) { if (!DeleteTempCacheDic.ContainsKey(key)) { List delList = new List(); 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 ``` cs using System; using System.Collections.Generic; using Object = UnityEngine.Object; namespace xasset { public class AssetRequest : LoadRequest { private static readonly Queue Unused = new Queue(); private static readonly Dictionary Loaded = new Dictionary(); 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 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(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(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; } } } ```