对象池的用法和意义
为什么要用对象池,它有什么用呢?它一般用在哪些方面呢?比如,你要制作一个射击类游戏,射击肯定要经常发射子弹吧,如果你不使用对象池,那么你每发射一颗子弹就要实例化一颗子弹,之后还要Destroy销毁掉,不断的实例化和销毁,是非常消耗性能的,小游戏还好,如果是比较大点的游戏,很容易造成卡顿现象,这样玩家肯定就不乐意玩了。又比如跑酷游戏,游戏中会反复的生成金币和障碍物和道具,如果也是反复的生成销毁的话,性能非常差。所以就引入了对象池。
对象池的实现
无非就是,游戏物体在用的时候就设为显示的,即SetActive(true),用不到的时候就SetActive(false),当要使用的false的游戏物体用完了,就再实例化出来,没用完的时候,将false的游戏物体设为true即可。
当游戏中有许多的物品需要重复的实例化和销毁时,如金币、小车、栅栏、磁铁等,这个时候我们可以先建一个子池子,不同类型的东西分别在不同的子池子里,最后由一个大池子统一管理。
子池子的实现
在实现子池子之前,先写个接口,C#中接口可以多继承,而类只能单继承,所以使用接口,接口名一般由大写的I开头。代码如下:
using System.Collections;
using System.Collections.Generic;
public interface IReusable
{
//取出的时候调用
void OnSpawn();
//回收的时候调用
void OnUnSpawn();
}
接口写完后,再写个类,让这个类同时继承MonoBehaviour和 IReusable。
之后在对象池中只需要继承这个新写的类就可以了,相当于同时继承了MonoBehaviour和 IReusable。新的类代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class ReusableObject : MonoBehaviour, IReusable
{
public abstract void OnSpawn();
public abstract void OnUnSpawn();
}
为什么要先写这个接口和这个类呢,因为下面的子池子中会用到。
就是go.SendMessage(“OnSpawn”,SendMessageOptions.DontRequireReceiver);
和go.SendMessage(“OnUnSpawn”,SendMessageOptions.DontRequireReceiver);
将游戏物体设为true时,发送消息让该游戏物体执行OnSpawn()的方法,可以在OnSpawn中设置生成的游戏物体的位置和大小之类的。
将游戏物体设为false时,发送消息让该游戏物体执行OnUnSpawn()的方法,可以在OnUnSpawn中将变换过的游戏物体恢复成原来的模样,如果没有变换过,那就在OnUnSpawn中留空就好了。
子池子的代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SubPool
{
//这个是负责保存子池子中的游戏物体
private List<GameObject> m_objects = new List<GameObject>();
private GameObject m_prefab;
public string Name { get { return m_prefab.name; } }//每个子池子都有一个名字标识
private Transform m_parent;
public SubPool(Transform parent,GameObject go)
{
m_parent = parent;
m_prefab = go;
}
/// <summary>
/// 取出物体
/// </summary>
/// <returns></returns>
public GameObject Spawn()
{
GameObject go = null;
foreach (var obj in m_objects)
{
if(!obj.activeSelf) //!obj.activeSelf代表游戏物体是false的;
{
go = obj;
break;
}
}
if(go==null)
{
go = GameObject.Instantiate<GameObject>(m_prefab);
go.transform.parent = m_parent;
m_objects.Add(go);
}
go.SetActive(true);
go.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver);
return go;
}
/// <summary>
/// 回收一个游戏物体
/// </summary>
/// <param name="go"></param>
public void UnSpawn(GameObject go)
{
if(Contain(go))
{
go.SendMessage("OnUnSpawn", SendMessageOptions.DontRequireReceiver);
go.SetActive(false);
}
}
/// <summary>
/// 回收所有游戏物体
/// </summary>
public void UnSpawnAll()
{
foreach (var obj in m_objects)
{
if(obj.activeSelf)
{
UnSpawn(obj);
}
}
}
/// <summary>
/// 判断是否属于list里面,,因为m_objects是私有的,所以在大池子中访问不到,无法判断小池子中是否包含有传进来的游戏物体,所以这里提供一个供外部访问的方法。
/// </summary>
/// <returns></returns>
public bool Contain(GameObject go)
{
return m_objects.Contains(go);
}
}
小池子建完了以后,就需要建一个大池子来统一管理这些小池子了。
大池子的代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool : MonoSingleton<ObjectPool>
{
/// <summary>
/// 资源目录,就是需要实例化的那些游戏物体所在的路径
/// </summary>
public string ResourceDir = "";
//游戏物体的名字对应相应的子池子
private Dictionary<string, SubPool> m_pools = new Dictionary<string, SubPool>();
public GameObject Spawn(string name,Transform trans)
{
SubPool pool = null;
if(!m_pools.ContainsKey(name))
{
RegisteNew(name,trans);
}
pool = m_pools[name];
return pool.Spawn();
}
/// <summary>
/// 新建一个池子
/// </summary>
private void RegisteNew(string names,Transform trans)
{
string path = ResourceDir + "/" + names;
GameObject go = Resources.Load<GameObject>(path);
SubPool pool = new SubPool(trans, go);
m_pools.Add(pool.Name, pool);
}
/// <summary>
/// 清除所有
/// </summary>
public void Clear()
{
m_pools.Clear();
}
/// <summary>
/// 回收物体
/// </summary>
/// <param name="go"></param>
public void UnSpawn(GameObject go)
{
SubPool pool = null;
foreach (var p in m_pools.Values)
{
if(p.Contain(go))
{
pool = p;
break;
}
}
pool.UnSpawn(go);
}
//回收所有物体
public void UnSpawnAll()
{
foreach (var p in m_pools.Values)
{
p.UnSpawnAll();
}
}
}
大池子继承自 MonoSingleton< ObjectPool>,就是将自身变成一个单例,单例的内容请移步https://blog.csdn.net/weixin_43839583/article/details/103405296
因为大池子只有一个,并且需要频繁的使用,所以使用到单例。
对象池的使用就到这里了,性能非常的高效。
我对象池已经建好了,那么我的对象在哪啊,国家怎么还没给我分配对象。