原本用拖拽操作实现的克隆非常消耗资源,点击进入GameObject你就会发现每次克隆需要进行很多操作。
所以此时我们就引用了对象池的概念:
对象池是一个集合,用来存放一些需要大量重复使用的游戏对象
在FPS游戏中,我们需要大量的生成子弹,特效,敌人等,如果我们频繁的创建和销毁会很消耗CPU的性能,我想一想可不可以像我们用的白酒瓶一样回收利用,
你用这个瓶子的时候,你需要从消毒柜中取出,如果没有的话,我们使用新造的瓶子,往里边加白酒,然后对酒瓶进行封盖。用完之后,你需要对瓶子进行回收,回收后清洗,消毒,烘干,放入消毒柜,准备进行下一次使用,酒厂也不会把用过的完好的瓶子摔了再造新的,这样既消耗成本也浪费资源和时间。
我们程序中也可以使用这样的思路,例如我们在塔防游戏里边使用的敌人游戏对象,再具体一点《兽人必须死》游戏里的那个恶魔小鬼,反正都是一个预制体克隆出来的,恶魔小鬼在实例化的时候,会根据你选择的难度,给他们赋不同的血量等属性,游戏里,你建造的箭塔会攻击这个恶魔小鬼,这个恶魔小鬼死亡之后,我们不要销毁它,把这个恶魔小鬼激活状态关闭,存放在某个地方,再次需要使用的时候,我们把那些回收的恶魔小鬼,重新激活,并给它赋一些属性,如血量,状态等属性修改一下,继续使用。这个可以有效的避免重复创建和消耗产生的性能消耗。
对象池的基本模式:
IpoolObject(进行约束行为,也可以用抽象类)
///
/// 放在对象池里面的对象必须实现的接口
///
public interface IPoolObject {
///
/// Object初始化时调用
///
void Init();
///
/// Object回收时调用
///
void Release();
BasePoolObject(需要将此脚本挂载到预制体上)
using UnityEngine;
using System.Collections;
public class BasePoolObject : MonoBehaviour,IPoolObject {
public void Init ()
{
Debug.Log("初始化对象,比如位置");
}
public void Release ()
{
Debug.Log("释放对象");
}
}
ObjectPool(进行逻辑编程)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ObjectPool : MonoBehaviour {
/// <summary>
/// 预生成的个数
/// </summary>
public int preNum;
/// <summary>
/// 对象列表
/// </summary>
public Queue<GameObject> pool = new Queue<GameObject>();
/// <summary>
///要放入的对象的预置
/// </summary>
public GameObject poolObjectPefab;
void InitPool(){
for(int i=0; i<preNum;i++)
{
GameObject obj = GameObject.Instantiate(poolObjectPefab);
IPoolObject comp = obj.GetComponent<IPoolObject>();
if(comp==null)
{
Debug.LogError("预置上必须绑有实现了IPoolObject接口的类");
}
comp.Init();
pool.Enqueue(obj);
obj.SetActive(false);
}
}
/// <summary>
/// 创建一个对象
/// </summary>
/// <returns>从池里取出对象.</returns>
public GameObject RequestObject(){
GameObject obj;
if(pool.Count!=0)
{
obj = pool.Dequeue();
}else{
obj = GameObject.Instantiate(poolObjectPefab);
}
IPoolObject comp = obj.GetComponent<IPoolObject>();
if(comp==null)
{
Debug.LogError("预置上必须绑有实现了IPoolObject接口的类");
}
obj.SetActive(true);
comp.Init();
return obj;
}
/// <summary>
/// 回收对象
/// </summary>
/// <param name="obj">Object.</param>
public void RecoverObject(GameObject obj){
IPoolObject comp = obj.GetComponent<IPoolObject>();
if(comp==null)
{
Debug.LogError("预置上必须绑有实现了IPoolObject接口的类");
}
pool.Enqueue(obj);
comp.Release();
obj.SetActive(false);
}
void Awake () {
InitPool();
}
// Update is called once per frame
void Update () {
}
}
Test(测试)
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
public ObjectPool pool1;
public ObjectPool pool2;
void Start () {
GameObject obj =pool1.RequestObject();
pool1.RecoverObject(obj);
}
// Update is called once per frame
void Update () {
}
}
using UnityEngine;
using System.Collections;
public interface IPoolObjec{
void EnterPool();
void ExitPool();
}
using UnityEngine;
using System.Collections;
using System;
public class BasePoolObject : MonoBehaviour,IPoolObjec {
public void EnterPool()
{
Debug.Log("huishou");
}
public void ExitPool()
{
Debug.Log("huoqu");
}
void OnMouseDown()
{
Pool.instance.Recycle(this.gameObject);
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Pool : MonoBehaviour
{
//实现了类似单例
public static Pool instance;
void Awake()
{
instance = this;
//初始化,意义是在游戏开始初期就将所有需要的资源加载出来
Init();
}
public int initCount;
//产生什么预制体
public GameObject objPrefab;
//存到哪里
public Queue<GameObject> queue = new Queue<GameObject>();
//进货
public void Init()
{
//将接口当成标签使用,进行判断
IPoolObjec ipoolObject = objPrefab.GetComponent<IPoolObjec>();
if (ipoolObject!=null)
{
//生成适当的预制体进行储存(initCount)
for (int i = 0; i < initCount; i++)
{
GameObject obj = GameObject.Instantiate(objPrefab);
//两种方式防止对象影响游戏(放置不可能出现在游戏界面的位置(2))
//obj.transform.position = new Vector3(10000000, 10000, 10000);
//产生并禁用(1)
obj.SetActive(false);
obj.transform.position = new Vector3(2 * i, 0, 0);
queue.Enqueue(obj);
}
}
else
{
Debug.Log("不是我想要,初始化失败");
return;
}
}
//出仓
public GameObject GetObject()
{
GameObject result = null;
//判断是否还剩余预制体,有就用,没有就产生
if (queue.Count>=1)
{
//如果有就激活并进行初始化操作,并将队列中的移除
result = queue.Dequeue();
result.SetActive(true);
result.GetComponent<IPoolObjec>().ExitPool();
}
else
{
//一般设置恰当就不会发生这种情况(此时我们还可以对接口进行判断类似69)
result = GameObject.Instantiate(objPrefab);
result.GetComponent<IPoolObjec>().ExitPool();
}
return result;
}
//回收
public void Recycle(GameObject obj)
{
//进行回收判断
IPoolObjec ipoolObiect = obj.GetComponent<IPoolObjec>();
if (ipoolObiect!=null)
{
//将对象失活,并加入队列()
obj.SetActive(false);
ipoolObiect.EnterPool();
queue.Enqueue(obj);
}
}
}
using UnityEngine;
using System.Collections;
public class Test : MonoBehaviour {
// Update is called once per frame
void Update () {
//进行按键回收我们需要在BasePoolObject中添加以下代码
//void OnMouseDown()
//{
// Pool.instance.Recvcle(this.gameObject);
//}
if (Input.GetMouseButtonDown(1))
{
Debug.Log("na");
Pool.instance.GetObject();
}
if (Input.GetMouseButtonDown(0))
{
Debug.Log("shou");
Pool.instance.Recycle(gameObject);
}
}
}
这是一种子弹移动的方式(此时给预制体添加刚体时我们要注意,我们要将Use Gravity 取消勾选)
using UnityEngine;
using System.Collections;
public class Weapon : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
GameObject bullt = Pool.instance.GetObject();
bullt.transform.position = ray.origin;
bullt.GetComponent<Rigidbody>().AddForce(ray.direction * 20);
}
}
}
using UnityEngine;
using System.Collections;
using System;
public class BasePoolObject : MonoBehaviour, IpoolObject
{
public void EnterPool()
{
Debug.Log("回收");
}
public void ExitPoll()
{
Debug.Log("获取");
}
void OnMouseDown()
{
Pool.instance.Recycle(this.gameObject);
}
void OnTriggerEnter(Collider other)
{
if (other.tag.Equals("Wall"))
{
Pool.instance.Recycle(this.gameObject);
}
}
}