这种操作是危险操作,如无必要尽量避免。
但是当需求出现的时候,如何处理?
1.使用数组而不是列表。(涉及到大量的增加移除,数组性能好一点)
2.增加:向数组的最后面添加,更新活跃数量。
3.移除:移除元素,把后面的所有元素前移一位,更新活跃数量。
4.遍历操作:
由于c#是单线程,所以在遍历的时候增添移除了元素,那么只能是在遍历的代码中进行了增添和移除。
因此在更新前后比较激活元素数量。如果元素减少了,那么说明元素被移除了。更新for循环的id。
5.例子如下,注意事项及可能问题也在代码中
private int m_ActiveBulletCount;
private Bullet[] m_FrameBulletList;
private int m_MaxBulletCount = 200;
public void FrameUpdate()
{
var count = m_ActiveBulletCount;
for (int i = 0; i < count; ++i)
{
var prevCount = m_ActiveBulletCount;
if (m_FrameBulletList[i].Enabled)
{
m_FrameBulletList[i].FixedUpdate(); //i=5
}
// 执行一次后,数量可能发生变化。
// 如果移除了后面的,包括自己本身,那不会出bug。
// 如果要移除前面的,那就会出bug:因为数组整体前移,会导致有的子弹不会被帧推。
// 所以不要连锁销毁子弹,子弹的销毁必须在自己的FixedUpdate中执行。
// 如果有类似需求:给子弹加一个toDelete标志,在被帧推的下一帧自我销毁。
if (m_ActiveBulletCount < prevCount)
{
var diff = prevCount - m_ActiveBulletCount; //10-9=1
count -= diff; //9
i = Mathf.Max(i - diff, 0); //i=4 ++4=5
}
}
}
// 添加,向数组最后一个地方添加
public void Regist(Bullet bullet)
{
if (bullet == null) return;
if (m_ActiveBulletCount >= m_MaxBulletCount)
{
Debug.LogError("Error: 子弹数量超过数组大小,增大数组容量或者进行其他优化");
return;
}
// 异常判断,todo:删掉,比较占用资源
for (int i = 0; i < m_ActiveBulletCount; i++)
{
if(m_FrameBulletList[i] == bullet)
{
Debug.LogError("注册了重复子弹,检查bug。");
return;
}
}
m_FrameBulletList[m_ActiveBulletCount] = bullet;
m_ActiveBulletCount++;
}
// 移除,把后面的数组挪到前面位置来。更新当前活跃数量
public void UnRegist(Bullet bullet)
{
if (bullet == null) return;
var removeIndex = -1;
for (int i = m_ActiveBulletCount - 1; i >= 0; --i)
{
if (m_FrameBulletList[i] == bullet)
{
removeIndex = i;
break;
}
}
if(removeIndex != -1)
{
// 数组元素整体前移
for (int j = removeIndex + 1; j < m_ActiveBulletCount; ++j)
{
m_FrameBulletList[j - 1] = m_FrameBulletList[j];
}
m_ActiveBulletCount--;
m_FrameBulletList[m_ActiveBulletCount] = null;
}
}
网友评论