32 KiB
32 KiB
#灵感/代码缓存
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;
}
}
}