最近在看教程,学到了一点关于unity对象池的技术,现在来总结一下。
1.适用范围
在游戏中,总会有一些对象会重复出现在游戏场景中,比如敌人、子弹、特效……这些对象如果用一般的生成(Instantiate)及销毁(Destroy)方法来使其出现或消失在游戏场景中时,往往会造成很大的性能开销,尤其是在移动设备,如果大量使用此方法来处理对象的话会使得游戏变卡顿,影响游戏体验。因此可以用对象池技术来解决这个问题。
2.关于对象池
对象池就是将要重复出现在游戏场景中的物体(即可复用对象)在进入游戏场景中时预先生成,将其设置为未激活状态(设置SetActive属性为false,防止其在场景中出现)并存放在对象池中,当要让该物体出现在场景中时,将该物体从对象池中取出,激活并移动至指定地方。而当要让该物体消失于场景中时,则将其放入对象池中,并将其各项数值重置为默认(一些粒子对象是在播放完特效才消失的,便需要将其重置到播放前的状态),再设置其为未激活状态,需要注意的是,每个对象池里存储的可复用物体不能太多。
3.实现思路
以下为我参照教程中的方法整理的对象池实现思路图,可能有些简陋:
以及实现对象池时所用的脚本的功能图:
4.代码
废话不多说,下面贴出所用脚本的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//作为静态方法
public class GameObjectUtil {
private static Dictionary<RecycleGameObject,ObjectPool> pools=new Dictionary<RecycleGameObject, ObjectPool>();
//提供一个实例化方法
public static GameObject Instantiate(GameObject prefab, Vector3 pos)
{
GameObject instance;
RecycleGameObject reference = prefab.GetComponent<RecycleGameObject>();
//检查是否为可复用对象
if (reference!=null)
{
//取得该预制体的对象池
var pool= GetObjectPool(reference);
//在对象池中生成克隆体,得到该克隆体
instance = pool.NextObject(pos).gameObject;
return instance;
}
else
{
//调用monobehaviour自带的实例化方法直接生成克隆体
instance = GameObject.Instantiate(prefab);
instance.transform.position = pos;
return instance;
}
}
/// <summary>
/// 提供一个销毁方法
/// </summary>
/// <param name="prefab">待克隆的预制体</param>
public static void Destroy(GameObject prefab)
{
RecycleGameObject temp = prefab.GetComponent<RecycleGameObject>();
//检查是否为可复用对象
if (temp!=null)
{
temp.ShutDown();
}
else
{
Destroy(prefab);
}
}
/// <summary>
/// 获取对象的对象池
/// </summary>
/// <param name="reference"></param>
/// <returns></returns>
private static ObjectPool GetObjectPool(RecycleGameObject reference)
{
ObjectPool pool;
//查询字典里有没有该对象相对应的对象池
if (pools.ContainsKey(reference))
{
//得到相应的对象池
pool = pools[reference];
return pool;
}
else
{
//没有对象池的话新建一个对象池
var poolContainer=new GameObject(reference.gameObject.name+"pool");
//添加对象池管理组件
pool = poolContainer.AddComponent<ObjectPool>();
//将预制体绑定到对象池中
pool.prefab = reference;
//将预制体与对象池添加到字典中
pools.Add(reference,pool);
return pool;
}
}
}
注:其中GameObjectUtil类为静态类,不需要继承monobehaviour类,也无需挂载到游戏对象中。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool : MonoBehaviour {
//对象池的管理脚本
public List<RecycleGameObject> poolInstance=new List<RecycleGameObject>();
public RecycleGameObject prefab;
/// <summary>
/// 创建预制体的克隆体
/// </summary>
/// <param name="pos">克隆体的位置</param>
/// <returns></returns>
private RecycleGameObject CreateObject(Vector3 pos)
{
//实例化克隆体
var clone=GameObject.Instantiate(prefab);
//设置克隆体的位置
clone.transform.position = pos;
//将克隆体作为对象池的子对象
clone.transform.parent = transform;
//将克隆体加入到列表中
poolInstance.Add(clone);
return clone;
}
/// <summary>
/// 取出克隆体
/// </summary>
/// <param name="pos">克隆体的位置</param>
/// <returns></returns>
public RecycleGameObject NextObject(Vector3 pos)
{
RecycleGameObject instance = null;
//调用方法获取克隆体实例
instance = CreateObject(pos);
//激活克隆体
instance.Restart();
return instance;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RecycleGameObject : MonoBehaviour {
//通过设置游戏对象的SetActive属性来激活/关闭游戏对象,并作为判断此物体是否为可复用对象的标准
public void Restart()
{
gameObject.SetActive(true);
}
public void ShutDown()
{
gameObject.SetActive(false);
}
}
5.使用方法
只需要在预制件里挂上RecycleGameObject脚本,就能将此物体作为可复用的物体。调用GameObjectUtil类里面的Instantiate方法就能实例化物体及其对象池。比如我要生成个对象池,里面有3个物体。那我可以这样写个脚本来调用。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
public int mun;
public GameObject prefab;
private Vector3 pos;
// Use this for initialization
void Start ()
{
pos = transform.position+Vector3.right;
}
// Update is called once per frame
void Update () {
if (mun>0)
{
GameObjectUtil.Instantiate(prefab,pos);
mun--;
}
}
}
在场景中放入一个Cube,挂载上此脚本,如图设置:
为脚本中的prefab指定物体,在这里我指定了一个Sphere,Sphere里挂载了RecycleGameObject脚本,运行情况如图:
可以看到对象池里有3个对象,且对象池中的物体都挂载了RecycleGameObject脚本。