使用双向链表的原因 : 资源的使用频率,如果资源频繁使用,希望其处于最顶端,不使用的慢慢放在底端去,清理缓存的时候从底端开始清理
基本功能 :
//双向链表结构节点
public class DoubleLinkedListNode where T : class, new()
{
//前一个节点
public DoubleLinkedListNode prev = null;
//后一个节点
public DoubleLinkedListNode next = null;
//当前节点
public T t = null;
}
//双向链表结构
public class DoubleLinedList where T : class, new()
{
//表头
public DoubleLinkedListNode Head = null;
//表尾
public DoubleLinkedListNode Tail = null;
//双向链表结构类对象池
protected ClassObjectPool> m_DoubleLinkNodePool = ObjectManager.Instance.GetOrCreatClassPool>(500);
//个数
protected int m_Count = 0;
public int Count
{
get { return m_Count; }
}
///
/// 添加一个节点到头部
///
///
///
public DoubleLinkedListNode AddToHeader(T t)
{
DoubleLinkedListNode pList = m_DoubleLinkNodePool.Spawn(true);
pList.next = null;
pList.prev = null;
pList.t = t;
return AddToHeader(pList);
}
///
/// 添加一个节点到头部
///
///
///
public DoubleLinkedListNode AddToHeader(DoubleLinkedListNode pNode)
{
if (pNode == null)
return null;
pNode.prev = null;
if (Head == null)
{
Head = Tail = pNode;
}
else
{
pNode.next = Head;
Head.prev = pNode;
Head = pNode;
}
m_Count++;
return Head;
}
///
/// 添加节点到尾部
///
///
///
public DoubleLinkedListNode AddToTail(T t)
{
DoubleLinkedListNode pList = m_DoubleLinkNodePool.Spawn(true);
pList.next = null;
pList.prev = null;
pList.t = t;
return AddToTail(pList);
}
///
/// 添加节点到尾部
///
///
///
public DoubleLinkedListNode AddToTail(DoubleLinkedListNode pNode)
{
if (pNode == null)
return null;
pNode.next = null;
if (Tail == null)
{
Head = Tail = pNode;
}
else
{
pNode.prev = Tail;
Tail.next = pNode;
Tail = pNode;
}
m_Count++;
return Tail;
}
///
/// 移除某个节点
///
///
public void RemoveNode(DoubleLinkedListNode pNode)
{
if (pNode == null)
return;
if (pNode == Head)
Head = pNode.next;
if (pNode == Tail)
Tail = pNode.prev;
if (pNode.prev != null)
pNode.prev.next = pNode.next;
if (pNode.next != null)
pNode.next.prev = pNode.prev;
pNode.next = pNode.prev = null;
pNode.t = null;
m_DoubleLinkNodePool.Recycle(pNode);
m_Count--;
}
///
/// 把某个节点移动到头部
///
///
public void MoveToHead(DoubleLinkedListNode pNode)
{
if (pNode == null || pNode == Head)
return;
if (pNode.prev == null && pNode.next == null)
return;
if (pNode == Tail)
Tail = pNode.prev;
if (pNode.prev != null)
pNode.prev.next = pNode.next;
if (pNode.next != null)
pNode.next.prev = pNode.prev;
pNode.prev = null;
pNode.next = Head;
Head.prev = pNode;
Head = pNode;
if (Tail == null)
{
Tail = Head;
}
}
}
对其简单的封装(一般包括插入,查找,获取表头,获取表尾,清除,移除,获取长度等):
//封装双向链表
public class CMapList where T : class, new()
{
DoubleLinedList m_DLink = new DoubleLinedList();
Dictionary> m_FindMap = new Dictionary>();
~CMapList()
{
Clear();
}
///
/// 清除列表
///
public void Clear()
{
while (m_DLink.Tail != null)
{
Remove(m_DLink.Tail.t);
}
}
///
/// 插入一个节点到表头
///
///
public void InsertToHead(T t)
{
DoubleLinkedListNode node = null;
if (m_FindMap.TryGetValue(t, out node) && node != null)
{
m_DLink.AddToHeader(node);
return;
}
m_DLink.AddToHeader(t);
m_FindMap.Add(t, m_DLink.Head);
}
///
/// 从表尾弹出一个结点
///
public void Pop()
{
if (m_DLink.Tail != null)
{
Remove(m_DLink.Tail.t);
}
}
///
/// 删除某个节点
///
///
public void Remove(T t)
{
DoubleLinkedListNode node = null;
if (!m_FindMap.TryGetValue(t, out node) || node == null)
{
return;
}
m_DLink.RemoveNode(node);
m_FindMap.Remove(t);
}
///
/// 获取到尾部节点
///
///
public T Back()
{
return m_DLink.Tail == null ? null : m_DLink.Tail.t;
}
///
/// 返回节点个数
///
///
public int Size()
{
return m_FindMap.Count;
}
///
/// 查找是否存在该节点
///
///
///
public bool Find(T t)
{
DoubleLinkedListNode node = null;
if (!m_FindMap.TryGetValue(t, out node) || node == null)
return false;
return true;
}
///
/// 刷新某个节点,把节点移动到头部
///
///
///
public bool Reflesh(T t)
{
DoubleLinkedListNode node = null;
if (!m_FindMap.TryGetValue(t, out node) || node == null)
return false;
m_DLink.MoveToHead(node);
return true;
}
}
思路 : 根据之前打包所在的路径获取CRC,根据CRC获取中间类ResouceItem(单个资源对应的中间类,哪怕prefabs依赖的资源也是单个对应一个ResouceItem)
///
/// 同步资源加载,外部直接调用,仅加载不需要实例化的资源,例如Texture,音频,txt等等
///
///
///
///
public T LoadResource(string path) where T : UnityEngine.Object
{
if (string.IsNullOrEmpty(path)) //string判断为空尽量用这个
{
return null;
}
uint crc = Crc32.GetCrc32(path);
//从缓存池进行缓存(判断是否已经缓存)
ResouceItem item = GetCacheResouceItem(crc);
if (item != null)
{
return item.m_Obj as T;
}
T obj = null;
// //用来测试编辑器中AB包的加载
//#if UNITY_EDITOR
// if (!m_LoadFormAssetBundle)
// {
// item = AssetBundleManager.Instance.FindResourceItme(crc);
// if (item != null && item.m_AssetBundle != null)
// {
// if (item.m_Obj != null)
// {
// obj = (T)item.m_Obj;
// }
// else
// {
// obj = item.m_AssetBundle.LoadAsset(item.m_AssetName);
// }
// }
// else
// {
// if (item == null)
// {
// item = new ResouceItem();
// item.m_Crc = crc;
// }
// obj = LoadAssetByEditor(path);
// }
// }
//#endif
//手机上运行
if (obj == null)
{
item = AssetBundleManager.Instance.LoadResouceAssetBundle(crc);
if (item != null && item.m_AssetBundle != null)
{
if (item.m_Obj != null)
{
obj = item.m_Obj as T;
}
else
{
obj = item.m_AssetBundle.LoadAsset(item.m_AssetName);
}
}
}
CacheResource(path, ref item, crc, obj);
return obj;
}
///
/// 根据ResouceObj卸载资源
///
///
///
///
public bool ReleaseResouce(ResouceObj resObj, bool destoryObj = false)
{
if (resObj == null)
return false;
ResouceItem item = null;
if (!AssetDic.TryGetValue(resObj.m_Crc, out item) || null == item)
{
Debug.LogError("AssetDic里不存在改资源:" + resObj.m_CloneObj.name + " 可能释放了多次");
}
GameObject.Destroy(resObj.m_CloneObj);
item.RefCount--;
DestoryResouceItme(item, destoryObj);
return true;
}
///
/// 不需要实例化的资源的卸载,根据对象
///
///
///
///
public bool ReleaseResouce(Object obj, bool destoryObj = false)
{
if (obj == null)
{
return false;
}
//根据GUID查找资源
ResouceItem item = null;
foreach (ResouceItem res in AssetDic.Values)
{
if (res.m_Guid == obj.GetInstanceID())
{
item = res;
}
}
if (item == null)
{
Debug.LogError("AssetDic里不存在改资源:" + obj.name + " 可能释放了多次");
return false;
}
//释放减少引用计数
item.RefCount--;
DestoryResouceItme(item, destoryObj);
return true;
}
///
/// 不需要实例化的资源卸载,根据路径
///
///
///
///
public bool ReleaseResouce(string path, bool destoryObj = false)
{
if (string.IsNullOrEmpty(path))
{
return false;
}
uint crc = Crc32.GetCrc32(path);
ResouceItem item = null;
if (!AssetDic.TryGetValue(crc, out item) || null == item)
{
Debug.LogError("AssetDic里不存在改资源:" + path + " 可能释放了多次");
}
item.RefCount--;
DestoryResouceItme(item, destoryObj);
return true;
}
///
/// 缓存加载的资源
///
///
///
///
///
///
void CacheResource(string path, ref ResouceItem item, uint crc, Object obj, int addrefcount = 1)
{
//缓存太多,清除最早没有使用的资源
WashOut();
if (item == null)
{
Debug.LogError("ResouceItem is null, path: " + path);
}
if (obj == null)
{
Debug.LogError("ResouceLoad Fail : " + path);
}
item.m_Obj = obj;
item.m_Guid = obj.GetInstanceID(); //获取GUID
item.m_LastUseTime = Time.realtimeSinceStartup; //修改成自游戏开始运行的实时时间
item.RefCount += addrefcount;
ResouceItem oldItme = null;
if (AssetDic.TryGetValue(item.m_Crc, out oldItme))
{
AssetDic[item.m_Crc] = item;
}
else
{
AssetDic.Add(item.m_Crc, item);
}
}
///
/// 缓存太多,清除最早没有使用的资源
///
protected void WashOut()
{
//当大于缓存个数时,进行一半释放
while (m_NoRefrenceAssetMapList.Size() >= MAXCACHECOUNT)
{
for (int i = 0; i < MAXCACHECOUNT / 2; i++)
{
ResouceItem item = m_NoRefrenceAssetMapList.Back();
DestoryResouceItme(item, true);
}
}
}
///
/// 回收一个资源
///
///
/// 是否清除AB包缓存
protected void DestoryResouceItme(ResouceItem item, bool destroyCache = false)
{
if (item == null || item.RefCount > 0)
{
return;
}
if (!destroyCache)
{
m_NoRefrenceAssetMapList.InsertToHead(item);
return;
}
if (!AssetDic.Remove(item.m_Crc))
{
return;
}
m_NoRefrenceAssetMapList.Remove(item);
//释放assetbundle引用
AssetBundleManager.Instance.ReleaseAsset(item);
//清空资源对应的对象池
ObjectManager.Instance.ClearPoolObject(item.m_Crc);
if (item.m_Obj != null)
{
item.m_Obj = null;
#if UNITY_EDITOR
Resources.UnloadUnusedAssets();
#endif
}
}
测试 :
Debug.Log(ResourceManager.Instance.LoadResource("Assets/GameData/Sounds/menusound.mp3").name);
思路 : 首先判断资源是否已经加载,如果已经加载,调用加载完成函数,如果未加载,判断是否正在加载资源,
///
/// 异步加载资源(仅仅是不需要实例化的资源,例如音频,图片等等)
///
public void AsyncLoadResource(string path, OnAsyncObjFinish dealFinish, LoadResPriority priority, bool isSprite = false, object param1 = null, object param2 = null,object param3 =null, uint crc = 0)
{
if (crc == 0)
{
crc = Crc32.GetCrc32(path);
}
ResouceItem item = GetCacheResouceItem(crc);
if (item != null)
{
if (dealFinish != null)
{
dealFinish(path, item.m_Obj, param1, param2, param3);
}
return;
}
//判断是否在加载中
AsyncLoadResParam para = null;
//如果未在加载中
if (!m_LoadingAssetDic.TryGetValue(crc, out para) || para == null)
{
para = m_AsyncLoadResParamPool.Spawn(true);
para.m_Crc = crc;
para.m_Path = path;
para.m_Sprite = isSprite;
para.m_Priority = priority;
//放入在加载的Dic中
m_LoadingAssetDic.Add(crc, para);
//放入优先级为(int)priority的在异步加载的List中
m_LoadingAssetList[(int)priority].Add(para);
}
//如果在加载中,将回调函数更改为此次调用的回调参数
//往回调列表里面加回调
AsyncCallBack callBack = m_AsyncCallBackPool.Spawn(true);
callBack.m_DealObjFinish = dealFinish;
//添加回调参数
callBack.m_Param1 = param1;
callBack.m_Param2 = param2;
callBack.m_Param3 = param3;
para.m_CallBackList.Add(callBack);
}
///
/// 从资源池获取缓存资源
///
///
///
///
ResouceItem GetCacheResouceItem(uint crc, int addrefcount = 1)
{
ResouceItem item = null;
if (AssetDic.TryGetValue(crc, out item))
{
if (item != null)
{
item.RefCount += addrefcount;
item.m_LastUseTime = Time.realtimeSinceStartup;
}
}
return item;
}
///
/// 异步加载
///
///
IEnumerator AsyncLoadCor()
{
List callBackList = null;
//上一次yield的视角,用来判断加载资源所用的时间是否超过MAXLOADRESTIME
long lastYiledTime = System.DateTime.Now.Ticks;
while (true)
{
bool haveYield = false;
//从最高优先级开始
for(int i = 0; i < (int)LoadResPriority.RES_NUM; i++)
{
//如果有优先级高的将i置换成优先级高的数值
if (m_LoadingAssetList[(int)LoadResPriority.RES_HIGHT].Count > 0)
{
i = (int)LoadResPriority.RES_HIGHT;
}
else if (m_LoadingAssetList[(int)LoadResPriority.RES_MIDDLE].Count > 0)
{
i = (int)LoadResPriority.RES_MIDDLE;
}
List loadingList = m_LoadingAssetList[i];
if (loadingList.Count <= 0)
continue;
//获取当前优先度第一个
AsyncLoadResParam loadingItem = loadingList[0];
loadingList.RemoveAt(0);
callBackList = loadingItem.m_CallBackList;
Object obj = null;
ResouceItem item = null;
//#if UNITY_EDITOR
// if (!m_LoadFormAssetBundle)
// {
// if (loadingItem.m_Sprite)
// {
// obj = LoadAssetByEditor(loadingItem.m_Path);
// }
// else
// {
// obj = LoadAssetByEditor
测试 :
ResourceManager.Instance.AsyncLoadResource("Assets/GameData/UGUI/Test1.png", OnLoadSpriteTest1, LoadResPriority.RES_MIDDLE, true);
在程序退出和跳转场景时进行调用
///
/// 清空缓存
///
public void ClearCache()
{
List tempList = new List();
foreach (ResouceItem item in AssetDic.Values)
{
if (item.m_Clear)
{
tempList.Add(item);
}
}
foreach (ResouceItem item in tempList)
{
DestoryResouceItme(item, true);
}
tempList.Clear();
}
///
/// 预加载资源
///
///
public void PreloadRes(string path)
{
if (string.IsNullOrEmpty(path))
{
return;
}
uint crc = Crc32.GetCrc32(path);
//注意没有增加引用计数
ResouceItem item = GetCacheResouceItem(crc, 0);
if (item != null)
{
return;
}
Object obj = null;
//#if UNITY_EDITOR
// if (!m_LoadFormAssetBundle)
// {
// item = AssetBundleManager.Instance.FindResourceItme(crc);
// if (item != null && item.m_Obj != null)
// {
// obj = item.m_Obj as Object;
// }
// else
// {
// if (item == null)
// {
// item = new ResouceItem();
// item.m_Crc = crc;
// }
// obj = LoadAssetByEditor