欢迎加入Unity业内qq交流群:956187480 qq扫描二维码加群
一:对象池的理解
1.核心思想
是创建一个池子,池子里面一开始有预存有一定数量的对象,当你需要使用对象时直接从池子里取就可以,如果池子里面预存的数量不够就通过池子创建新的对象拿来用。当对象使用结束后不直接删除,而是归还给池子,如果池子里对象总数大于预存数量的话就删掉多余的(灵活处理),保证池子在空闲状态里面只有预存的对象。
2.优化逻辑
对于对象池的优化逻辑其实是见仁见智的,并不是随随便便就拿来用,其实很多时候我们并不需要通过对象池来处理,通过对象池的主要目的是: 防止对象被频繁的创建和删除,从而内存抖动、频繁GC(垃圾回收)对象初始化成本较高
二:对象池的操作
1.预加载
就是预加载一定数量的对象。如果不做预热的,那么第一次创建对象的时候还是直接涉及初始化问题。一个很容易懂道理是玩家宁愿在加载界面多等1秒,也不会愿意在游戏中卡顿0.1秒,所以我觉得如果不做预热的对象池优化只做了一半。
//初始化对象池,预加载指定对象
private void InitPoolObj()
{
go = Resources.Load("Prefabs/Entity/Cube") as GameObject;
for (int i = 0; i < _capacity; i++)
{
var initObj = Instantiate(go);
initObj.name = go.name;
initObj.transform.SetParent(transform);
_objectList.Add(initObj);
}
foreach (var item in _objectList)
{
item.SetActive(false);
}
}
2.借用
通俗点讲就是从池中获取物体,如果是第一次获取物体要初始化池。 如果池中没有想要的物体了,则创建一个该对象
// 在Objects获取指定的GameObject,如果没有则new一个。
public GameObject Spawn(string name, bool isDespawnSelf = false)
{
GameObject temp = null;
if (_objectList.Count > 0)
{
foreach (var item in _objectList)
{
if (item.name == name)
{
temp = item;
_objectList.Remove(item);
temp.gameObject.SetActive(true);
break;
}
}
}
else
{
temp = Instantiate(go) as GameObject;
temp.name = go.name;
}
if (isDespawnSelf)
{
StartCoroutine(Recovery(temp));
}
return temp;
}
3.归还
物品用完了原本是要删除的,但是应用了对象池之后则是把物体归还到池内,前提是池中数量是不大于预设的最大数量的(防止太多内存炸了),如果池中数量已经大于了预设的最大数量,则直接删除
//自动回收
IEnumerator Recovery(GameObject go)
{
yield return new WaitForSeconds(5);
Despawn(go);
}
// 将用完的GameObject放入dormantObjects中
public void Despawn(GameObject go)
{
go.transform.SetParent(transform);
go.SetActive(false);
_objectList.Add(go);
//FIFO 如果dormantObjects大于最大个数则将之前的GameObject都推出来。
while (_objectList.Count > _capacity)
{
GameObject temp = _objectList[0];
_objectList.RemoveAt(0);
Destroy(temp);
}
}
4.余量归还
上面在归还的时候说如果大于了设定的数量阈值就不返回池中而是直接删除,但实际上删除也有可能会带来时间成本,所以我们可以先不删除,在每次游戏中途的过关之类的加载界面的时候再删除缩小内存池。如果怕在加载界面之前内存爆了的话可以多设置一个必须删除的阈值(此处没有处理)
//FIFO 如果dormantObjects大于最大个数则将之前的GameObject都推出来。
while (_objectList.Count > _capacity)
{
GameObject temp = _objectList[0];
_objectList.RemoveAt(0);
Destroy(temp);
}
5.重置
每个新拿出来的物体应该和新创建的一样是崭新的,不能明显带有上次使用过的状态,因此再每次物体出池的时候要对可能存在后效性的地方重置。在unity中则是在物体的OnEnable()中写物体手动初始化的内容,包括清空刚体的力等等,OnEnable()和Start()的区别就是Start()只在物体第一次启用的第一帧运行,OnEnable会在每次物体重新启用的时候运行。
6.代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : BaseManager<PoolManager>
{
// 存储动可重用的GameObject。
public List<GameObject> _objectList = new List<GameObject>();
// 对象池大小
private int _capacity = 5;
GameObject go;
protected override void Awake()
{
base.Awake();
InitPoolObj();
}
//初始化对象池,预加载指定对象
private void InitPoolObj()
{
go = Resources.Load("Prefabs/Entity/Cube") as GameObject;
for (int i = 0; i < _capacity; i++)
{
var initObj = Instantiate(go);
initObj.name = go.name;
initObj.transform.SetParent(transform);
_objectList.Add(initObj);
}
foreach (var item in _objectList)
{
item.SetActive(false);
}
}
// 在Objects获取指定的GameObject,如果没有则new一个。
public GameObject Spawn(string name, bool isDespawnSelf = false)
{
GameObject temp = null;
if (_objectList.Count > 0)
{
foreach (var item in _objectList)
{
if (item.name == name)
{
temp = item;
_objectList.Remove(item);
temp.gameObject.SetActive(true);
break;
}
}
}
else
{
temp = Instantiate(go) as GameObject;
temp.name = go.name;
}
if (isDespawnSelf)
{
StartCoroutine(Recovery(temp));
}
return temp;
}
//自动回收
IEnumerator Recovery(GameObject go)
{
yield return new WaitForSeconds(5);
Despawn(go);
}
// 将用完的GameObject放入dormantObjects中
public void Despawn(GameObject go)
{
go.transform.SetParent(transform);
go.SetActive(false);
_objectList.Add(go);
//FIFO 如果dormantObjects大于最大个数则将之前的GameObject都推出来。
while (_objectList.Count > _capacity)
{
GameObject temp = _objectList[0];
_objectList.RemoveAt(0);
Destroy(temp);
}
}
}
7.扩展对象池 有各种各样的扩展此处不展开讨论?
欢迎加入Unity业内qq交流群:956187480 qq扫描二维码加群
|