对象池:在对象销毁时将对象放入池中,在使用对象时从池中取出对象,避免对象初始化和销毁时的昂贵代价。
在游戏中我们需要很多的对象,这些对象可能是动态变化的,例如射击游戏中的子弹,是不断计数的,游戏中的特效也是随着游戏不断增加的。如果我们每次在使用时创建在消失时销毁,务必会造成很大的开销,使用对象池的概念我们可以初始化一定数量的对象在使用对象时使用池中的对象,在消失时把对象归还给对象池,实现对象的复用。
# 单例模板
一般来说,对于全局的对象池我们需要一个全局的对象池管理器单例来管理所有的对象池。
在 unity 中我们会用到 2 种单例,一种是非 mono 单例,一种是 mono 单例。
# 非 mono 单例
纯粹的单例模式,添加了线程锁。
using System; | |
using System.Threading; | |
namespace ObjectPool | |
{ | |
public class SingletonManager<T> where T:new () | |
{ | |
private static T _instance; | |
public static T Instance | |
{ | |
get | |
{ | |
if (_instance != null) return _instance; | |
try | |
{ | |
_instance = new T(); | |
Monitor.Enter(_instance); | |
} | |
finally | |
{ | |
Monitor.Exit(_instance); | |
} | |
return _instance; | |
} | |
} | |
} | |
} |
# mono 单例
mono 单例用于挂载在游戏对象上。
using System; | |
using UnityEngine; | |
namespace ObjectPool | |
{ | |
/// <summary> | |
/// 通用单例模式 | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
public class Singleton<T>: MonoBehaviour where T : Singleton<T> | |
{ | |
public static T Instance { get; private set; } | |
protected virtual void Awake() | |
{ | |
if (Instance==null) | |
{ | |
Instance = this as T; | |
} | |
else | |
{ | |
Destroy(gameObject); | |
} | |
} | |
} | |
} |
# 对象池单例
这里我以 mono 单例为例,其实现代码如下:
关键在于对象池的容量自动扩充,继承单例模板即可。
using System; | |
using System.Collections.Generic; | |
using UnityEngine; | |
namespace ObjectPool | |
{ | |
/// <summary> | |
/// 对象池 | |
/// </summary> | |
public class ObjectPool : Singleton<ObjectPool> | |
{ | |
public List<GameObject> freeObjects; // 自由对象 | |
public List<GameObject> useObjects; // 正在被使用的对象 | |
public GameObject objectPrefab; | |
public int maxCount; | |
public int addCount; | |
protected override void Awake() | |
{ | |
base.Awake(); | |
InitPool(); | |
} | |
/// <summary> | |
/// 获得对象 | |
/// </summary> | |
/// <returns></returns> | |
public GameObject GetItem() | |
{ | |
if (freeObjects.Count > 0) | |
{ | |
var item = freeObjects[0]; | |
freeObjects.Remove(item); | |
useObjects.Add(item); | |
item.SetActive(true); | |
return item; | |
} | |
// 扩充容量 | |
maxCount += addCount; | |
for (int i = 0; i < addCount; i++) | |
{ | |
freeObjects.Add(InitObject()); | |
} | |
var item2 = freeObjects[0]; | |
freeObjects.Remove(item2); | |
useObjects.Add(item2); | |
return item2; | |
} | |
/// <summary> | |
/// 释放对象 | |
/// </summary> | |
/// <param name="t"></param> | |
public void FreeItem(GameObject t) | |
{ | |
useObjects.Remove(t); | |
t.SetActive(false); | |
freeObjects.Add(t); | |
} | |
private void InitPool() | |
{ | |
for (int i = 0; i < maxCount; i++) | |
{ | |
freeObjects.Add(InitObject()); | |
} | |
} | |
private GameObject InitObject() | |
{ | |
var go = Instantiate(objectPrefab, transform, true); | |
go.SetActive(false); | |
return go; | |
} | |
} | |
} |
# 对象池的使用
using System; | |
using UnityEngine; | |
namespace ObjectPool | |
{ | |
public class ObjectPoolTest : MonoBehaviour | |
{ | |
private GameObject go; | |
private void Update() | |
{ | |
if (Input.GetKeyDown(KeyCode.A)) | |
{ | |
go= ObjectPool.Instance.GetItem(); | |
print(go.name); | |
} | |
if (Input.GetKeyDown(KeyCode.S)) | |
{ | |
ObjectPool.Instance.FreeItem(go); | |
} | |
} | |
} | |
} |