当游戏场景中出现大量的可重复利用的物体时,通过Destory来销毁再创建会触发不必要的GC回收机制,浪费性能,我们可以利用unity自带的对象池系统,从而节约性能来得到同样的效果。
为了使用这个对象池系统,我写了一个瞬间产生多枚子弹的测试脚本如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UnlimitedSwordSystem : MonoBehaviour
{
public GameObject projiectilePrefab;
public Rigidbody2D rigidbody2d;
private int projectileNum=20;
private Vector2 CreatPosition;
GameObject projectile;
private Coroutine launch3times;
public void CreatProjectiles()
{
if(transform.GetComponent<SkillTrigger>().available==false)return;
if(launch3times!=null)
{
StopCoroutine(launch3times);
}
launch3times=StartCoroutine("CreatProjectile");
}
private IEnumerator CreatProjectile()
{
int k=2;
while(k>=0)
{
k--;
for(int i=0;i<projectileNum;i++)
{
CreatPosition=Random.insideUnitCircle*1.5f;
projectile = Instantiate(projiectilePrefab,
CreatPosition+rigidbody2d.position,
Quaternion.identity);
launch(CreatPosition);
}
yield return new WaitForSeconds(0.3f);
}
}
void launch(Vector2 p)
{
projectile.GetComponent<Projectile>().Launch(p.normalized,500);
}
}
这个脚本可以在瞬间分三次产生共60枚子弹,运行游戏,打开Profiler,可以看到GC Allocated In Frame有明显波动
接下来使用对象池来优化
创建一个脚本pool,使用unity的对象池需引入UnityEngine.Pool;
从对象池中取出和放入对应着Get和Release这两个函数:
为了实现对象池,必须修改一下子弹的脚本,让子弹停留2秒后回收入对象池而非摧毁
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Projectile : MonoBehaviour
{
System.Action<Projectile>dea;
public void SetDea(System.Action<Projectile> dea)
{
this.dea=dea;
}
private Rigidbody2D rigidbody2d;
public int DamageValue=1;
void Awake() //在Start之前调用
{
rigidbody2d = GetComponent<Rigidbody2D>();
}
public void Launch(Vector2 direction,float force)
{
rigidbody2d.AddForce(direction*force);
}
float lifeTime=2;
bool IsActive=true;
private void Update()
{
// if (transform.position.magnitude > 250f)
// {
// Destroy(gameObject);
// }
if(!IsActive)return;
lifeTime-=Time.deltaTime;
if(lifeTime<=0)
{
lifeTime=2;
dea.Invoke(this);
}
}
创建对象池所需的参数:
createFunc:
创建一个对象,值得注意的是当Get不能得到一个对像时,会调用这个方法创建对象。
actionOnGet:
调出对象池对象时要执行的功能
actionOnRelease:
回收对象到对象池时要执行的功能
actionOnDestroy:
摧毁对象池中对象要执行的操作
collectionCheck:
是否开启回收检测功能
defaultCapacity:
对象池的默认容量,和链表或队列类似,当容量不足时会自动扩容。
maxSize:
限制对象池的最大容量,防止对象池的无限扩张,如果maxSize已满,当调用Release函数时,这个Release函数将会忽略。
对象池:
public class Pool : MonoBehaviour
{
[SerializeField] Projectile prefab;
[SerializeField] int defaultSize=100;
[SerializeField] int maxSize=500;
ObjectPool<Projectile>pool;
[SerializeField] Rigidbody2D rigidbody2d;
private void Awake() {
pool=new ObjectPool<Projectile>(OnCreatPoolItem,OnGetPoolItem,OnReleasePoolItem,
OnDestoryPoolItem,true,defaultSize,maxSize);
}
private void Update() {
var p=pool.Get();
p.transform.position=rigidbody2d.position+ Random.insideUnitCircle*2.5f;
}
private void OnDestoryPoolItem(Projectile projectile)
{
Destroy(projectile.gameObject);
}
private void OnReleasePoolItem(Projectile projectile)
{
projectile.gameObject.SetActive(false);
}
private void OnGetPoolItem(Projectile projectile)
{
projectile.gameObject.SetActive(true);
}
private Projectile OnCreatPoolItem()
{
var projectile=Instantiate(prefab,transform);
projectile.SetDea(delegate{pool.Release(projectile);});
return projectile;
}
}
运行游戏可以看到,GC Allocated In Frame在对象池稳定运行后为0