技术要点:
- 不用的粒子返回缓冲池
- 按照单个粒子名字创建一个池,当a池上一次操作时间(回收对象)超过阈值,每隔一段时间删除池里一个GameObject,可针对单个粒子配表,频繁使用的,驻留时间更长
- 一个完整池被删除,增加GC权值,GC权值达到最大值调用System.GC.Collect()
遇到问题:
- 父节点OnDisable,不可设置子节点的父物体 Cannot set the parent of the GameObject ''XXX“ while activating or deactivating the parent GameObject “XXX” ,由于父对象进入回收池时,代码正在对该对象子节点的父节点进行修改。修改方式为:给子节点套一层空对象
- 回收后再从池里调出,粒子只显示一半,或者不显示 每次粒子取出后,如果上一个粒子是在scroll中使用,在裁剪shader影响下,会出现裁剪一半情况,要重新设置粒子裁剪区
public void ResetMaskParams(Material mat)
{
mat.SetFloat("_MinX", -1);
mat.SetFloat("_MinY", -1);
mat.SetFloat("_MaxX", 1);
mat.SetFloat("_MaxY", 1);
}
- 有些粒子是带有特殊处理,例如用完即销毁子物体;或者挂载脚本会丢失引用,不放入缓冲池
代码
得到粒子
简单测试,真正使用用resid代替objPrefab,并用Assetbundle同步加载
代码语言:javascript复制public GameObject GetEffect(GameObject objPrefab)
{
string name = GetNoCloneName(objPrefab.name);
GameObject obj = null;
if (m_dicPool.ContainsKey(name))
{
if (m_dicPool[name] != null && m_dicPool[name].Count > 0)
{
obj = m_dicPool[name].Dequeue();
while (obj == null && m_dicPool[name].Count > 0)
{
obj = m_dicPool[name].Dequeue();
if (obj != null)
{
//return obj;
break;
}
}
if (obj == null)
{
obj = GameObject.Instantiate(objPrefab) as GameObject;
}
}
else
{
obj = GameObject.Instantiate(objPrefab) as GameObject;
}
}
else
{
m_dicPool[name] = new Queue<GameObject>(m_count);
obj = GameObject.Instantiate(objPrefab) as GameObject;
}
obj.SetActive(true);
SetInDicUse(obj);
return obj;
}
回收粒子
代码语言:javascript复制public void RecycleEffect(GameObject obj)
{
if (obj == null)
{
return;
}
//这里加要过滤不回收的粒子名字,或条件
string name = GetNoCloneName(obj.name);
m_lastUsedTime[name] = Time.time;
SetOutDicUse(obj);
obj.transform.SetParent(GetEffectPoolObj());
obj.SetActive(false);
if (m_dicPool.ContainsKey(name))
{
if (m_dicPool[name] == null)
{
m_dicPool[name] = new Queue<GameObject>(m_count);
}
m_dicPool[name].Enqueue(obj);
}
else
{
m_dicPool[name] = new Queue<GameObject>(m_count);
m_dicPool[name].Enqueue(obj);
}
}
粒子池超时未再使用,Destroy一个GameObject
代码语言:javascript复制private void FixedUpdate()
{
foreach (var item in m_lastUsedTime)
{
string keyName = item.Key;
float lastTime = item.Value;
if (m_dicUse.ContainsKey(keyName) && m_dicUse[keyName].Count == 0)
{
if (m_dicPool.ContainsKey(keyName))
{
if (m_dicPool[keyName].Count > 0)//某个池里还有空闲对象
{
if (Time.time - lastTime > GetDeleOneObjTimeClip(m_dicPool[keyName].Count)) // 超时移除一个空闲对象
{
GameObject idleObj = m_dicPool[keyName].Dequeue();
GameObject.Destroy(idleObj);
timeUpdateList.Add(keyName);
}
}
else//某个池里无空闲对象
{
if (Time.time - lastTime > mResReleaseTime)
{
releaseList.Add(keyName);
}
}
}
}
}
for (int i = 0; i < timeUpdateList.Count; i )
{
m_lastUsedTime[timeUpdateList[i]] = Time.time; // 上次删除缓冲池里某个obj的时间
}
GC权值
代码语言:javascript复制public void AddUnloadWeights(int nWeights = 1)
{
m_nSumWeights = nWeights;
}
void Update()
{
//每60帧执行一次检测
if (Time.frameCount % CHECK_INTERVAL_FRAME == 0)
{
TryUnloadUnusedAssets();
}
}
void TryUnloadUnusedAssets()
{
if ((Time.realtimeSinceStartup - m_fLastUnloadTime >= m_nMaxInterval)
|| (m_nSumWeights >= m_nWeightsThreshold))
{
DoUnloadUnusedAssets();
}
}
void DoUnloadUnusedAssets()
{
Resources.UnloadUnusedAssets();
System.GC.Collect();
m_nSumWeights = 0;
m_fLastUnloadTime = Time.realtimeSinceStartup;
}
源码
https://github.com/luoyikun/UnityForTest EffectPoolMgr.cs