前言
游戏中,经常会大量频繁的创建、销毁游戏对象(子弹、溅血粒子特效等),这会带来一定的性能开销,此时,我们可以利用对象池技术来优化.
实现对象池的步骤
1. 首先,使用一个数据结构直接存储对象,为了降低装箱、拆箱操作,使用List<GameObject>来存储。
2. 其次,使用字典Dictionary<string,List<GameObject>>来存储多个对象。
3. 然后,从对象池中提取对象,如果对象池中已经存在,则激活该对象,并且从List集合中移除,否则,创建新的对象(OutPool方法实现该功能)。
4. 最后,将不用的对象再加入到对象池,加入之前,先判断该对象对应的键是否已经存在,如果存在,就直接添加进对应键的值中,否则,创建新的键值对。(JoinPool方法实现该功能)
代码
using UnityEngine;
using System.Collections.Generic;
/// <summary>
/// 对象池单例类
/// </summary>
public class ObjectPoolSingleton
{
//存储多个对象(存储一个也可以哦)
//使用字典存储对象名与对象
//1、键用的字符串,保存对象的名字,可以通过名字来从Resources文件夹动态加载资源
//2、用泛型集合直接存储对象,降低装箱、拆箱次数
private Dictionary<string, List<GameObject>> pools;
#region 单例
private static ObjectPoolSingleton instance;
private ObjectPoolSingleton()
{
//初始化对象池
pools = new Dictionary<string, List<GameObject>>();
}
public static ObjectPoolSingleton Instance
{
get { return instance = instance ?? new ObjectPoolSingleton(); }
}
#endregion
/// <summary>
/// 出对象池(激活对象)
/// </summary>
/// <param name="objName">对象的名称</param>
/// <param name="pos">位置</param>
/// <param name="qua">旋转</param>
/// <returns></returns>
public GameObject OutPool(string objName, Vector3 pos, Quaternion qua)
{
//首先根据名称判断字典里有没有该对象
if (pools.ContainsKey(objName))
{
//有,再判断个数(想想,为什么要判断个数,难道还会为0?)
if (pools[objName].Count > 0)
{
//获得第集合第一个对象
GameObject currentObj = pools[objName][0];
//提取了,就应该将其从集合中移除
pools[objName].Remove(currentObj);
//激活该对象
currentObj.SetActive(true);
//初始化该对象的位置、旋转信息
currentObj.transform.position = pos;
currentObj.transform.rotation = qua;
return currentObj;
}
}
//没有,怎么办呢,创建一个,我们这里是用Resources动态加载要实例化的对象
GameObject newObj = GameObject.Instantiate(Resources.Load<GameObject>(objName), pos, qua) as GameObject;
return newObj;
}
/// <summary>
/// 进对象池
/// </summary>
/// <param name="obj">要加入的对象</param>
public void JoinPool(GameObject obj)
{
//隐藏对象
obj.SetActive(false);
//去掉对象名称后的‘(Clone)’
string objName = obj.name.Substring(0, obj.name.Length - 7);
//首先判断字典中是否已经有了该对象
if (pools.ContainsKey(objName))
{
//有了,就添加到对应键的集合里
pools[objName].Add(obj);
}
else
{
//没有,添加新的键值对
pools.Add(objName, new List<GameObject>() {obj});
}
}
}
该脚本可以添加到创建的空物体身上,注意游戏场景中必须要有带有碰撞器的对象存在,不然,射线射谁呢?
测试代码
using UnityEngine;
using System.Collections;
//游戏控制脚本,可以添加到创建的空物体身上
public class GameController : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonDown(0))
{
//从摄像机发出一条射线
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//是否碰撞到带有碰撞器的对象
if (Physics.Raycast(ray, out hit, Mathf.Infinity))
{
//从对象池中提取对象
GameObject currentObj = ObjectPoolSingleton.Instance.OutPool("Sphere",
Camera.main.transform.position, Quaternion.identity);
//球对象的速度要初始化为0,不然,会在原来基础上再次添加力
currentObj.GetComponent<Rigidbody>().velocity = Vector3.zero;
//给对象一个力
currentObj.GetComponent<Rigidbody>().AddForce(ray.direction*1200);
}
}
}
}
}
该脚本挂载到球预设体身上(注意:球要添加刚体组件,需要添加力,并且该预设体要放在Resources文件夹下,需要动态加载,名称要与你提取对象所传入的字符串名称相同)
using UnityEngine;
using System.Collections;
//该脚本挂载到球预设体身上(球要添加刚体组件,需要添加力)
public class Bullet : MonoBehaviour {
//注意:必须OnEnable回调函数里,此函数在脚本生命周期里是可以循环调用的,对象SetActive(true)
//时,就会调用
void OnEnable()
{
StartCoroutine("Join");
}
IEnumerator Join()
{
yield return new WaitForSeconds(2.5f);
//延时2.5S后进对象池,你就看不见了
ObjectPoolSingleton.Instance.JoinPool(gameObject);
}
}
写在最后
###代码虐我千百遍,催我快点找初恋###