目的:以后项目需要对象池,直接将这四个脚本放入工程调用PoolManager.SpawnObject()创建物体,调用PoolManager.ReleaseObject(this.gameObject);释放物体即可实现,效果:
对象池实现,三个对象池相关类 + 一个单例类(这个单例类挺好,所以加上了)
直接将这四个脚本放入工程:
1、在场景中只挂在PoolMnanager脚本,指定一个存放物体的对象(Root);
2、调用PoolManager.SpawnObject(prefab,position,Quaternion.identity),第一个参数是想要生成的预制体对象,第二个参数是位置,第三个是四元素的角度。
示例源代码:点击一次鼠左键标生成一个Cube,Cube上挂载一个脚本,自动向前运动,2秒后释放此对象;
1、三个对象池相关类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPoolContainer<T>
{
private T item;//对象(最小单元)
public T Item { get => item; set => item = value; }
public bool Used { get; private set; }
public void Consume()//设置为使用状态
{
Used = true;
}
public void Release()//设置为未使用状态
{
Used = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Linq;
public class ObjectPool<T>
{
public List<ObjectPoolContainer<T>> list;//未使用的对象列表
public Dictionary<T, ObjectPoolContainer<T>> lookup;//正在使用的对象字典
private Func<T> factoryFunc;
private int lastIndex = 0;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="factoryFunc">返回一个T类型的对象</param>
/// <param name="initialSize">初始化大小</param>
public ObjectPool(Func<T> factoryFunc, int initialSize)
{
this.factoryFunc = factoryFunc;
list = new List<ObjectPoolContainer<T>>(initialSize);
lookup = new Dictionary<T, ObjectPoolContainer<T>>(initialSize);
Warm(initialSize);
}
/// <summary>
/// 创建对象池
/// </summary>
/// <param name="capacity">大小</param>
private void Warm(int capacity)
{
for (int i = 0; i < capacity; i++)
{
CreateContainer();
}
}
/// <summary>
/// 创建对象
/// </summary>
/// <returns></returns>
private ObjectPoolContainer<T> CreateContainer()
{
var container = new ObjectPoolContainer<T>();
container.Item = factoryFunc();
list.Add(container);
return container;
}
/// <summary>
/// 从池中获取一个对象
/// </summary>
/// <returns></returns>
public T GetItem()
{
ObjectPoolContainer<T> container = null;
for (int i = 0; i < list.Count; i++)//从位置用的list中找未使用的对象
{
lastIndex++;
if (lastIndex > list.Count - 1)
{
lastIndex = 0;
}
if (list[lastIndex].Used)//正在使用
{
continue;
}
else//未使用
{
container = list[lastIndex];
}
}
if (container == null)//没有的话就创建一个新的
{
container = CreateContainer();
}
container.Consume();//标记为使用状态
lookup.Add(container.Item, container);//添加到正在使用的字典中
return container.Item;
}
/// <summary>
/// 释放不用的游戏对象
/// </summary>
/// <param name="item"></param>
public void ReleaseItem(object item)
{
ReleaseItem(item);
}
public void ReleaseItem(T item)
{
if (lookup.ContainsKey(item))//存在
{
var container = lookup[item];
container.Release();释放此物体
lookup.Remove(item);从正在使用的字典中移除
}
else
{
Debug.LogWarning("this object pool does not contain the item provided:" + item);
}
}
public int Count
{
get { return list.Count; }
}
public int CountUsedItems
{
get { return lookup.Count; }
}
#region Func详解用法
/* Func详解用法
Func<T in, T Tresult>:封装一个具有一个参数并返回 TResult 参数指定的类型值的方法。其实个人感觉,Func和Action的区别很明显,也很直接。二者都是委托,但Func能返回函数执行结果,而Action返回类型是Void,这个区别很明显,在具体的项目中,也很容易确定该使用那个。下文就说明具体Func的代码调用:
public string myName;
Func<string, string> myFunc;
public MyBlogBase()
{
//myFunc = delegate(string curName) { return curName.ToUpper(); };
//myFunc = new Func<string, string>(SetFunc);
myFunc = name => { return name.ToUpper(); };
}
private string SetFunc(string name)
{
return name.ToUpper();
}
public void StartFun(string curName)
{
myName = myFunc(curName);
}
如上3种写法,都是合适的Func定义,大家可以选择适合自己的编程模式,其实匿名方法,有个优点,就是可以直接使用当前函数出现的变量,代码更简洁,但可能有些人觉得不易读。
*/
#endregion
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : Singleton<PoolManager>
{
public bool logStatus;
public Transform root;
private Dictionary<GameObject, ObjectPool<GameObject>> prefabLookup;//已经创建的对象池
private Dictionary<GameObject, ObjectPool<GameObject>> instanceLookup;//当前使用的对象池
//private bool dirty = false;//
private void Awake()
{
prefabLookup = new Dictionary<GameObject, ObjectPool<GameObject>>();
instanceLookup = new Dictionary<GameObject, ObjectPool<GameObject>>();
}
//private void Update()
//{
// if (logStatus && dirty)
// {
// PrintStatus();
// dirty = false;
// }
//}
/// <summary>
/// 创建一个对象池
/// </summary>
/// <param name="prefab">什么类型</param>
/// <param name="size">大小</param>
public void warmPool(GameObject prefab, int size)
{
if (prefabLookup.ContainsKey(prefab))
{
throw new System.Exception("pool for prefab " + prefab.name + "has already been created");
}
var pool = new ObjectPool<GameObject>(() => { return InstatiatePrefab(prefab); }, size);
prefabLookup[prefab] = pool;
//dirty = true;
}
/// <summary>
/// 创建游戏对象,默认位置角度
/// </summary>
/// <param name="prefab"></param>
/// <returns></returns>
public GameObject spawnObject(GameObject prefab)
{
return spawnObject(prefab, Vector3.zero, Quaternion.identity);
}
/// <summary>
/// 创建游戏对象,自定义位置,角度
/// </summary>
/// <param name="prefab">要创建的对象</param>
/// <param name="position">坐标</param>
/// <param name="rotation">角度</param>
/// <returns></returns>
public GameObject spawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
if (!prefabLookup.ContainsKey(prefab))
{
warmPool(prefab, 1);
}
var pool = prefabLookup[prefab];
var clone = pool.GetItem();
clone.transform.position = position;
clone.transform.rotation = rotation;
clone.SetActive(true);
instanceLookup.Add(clone, pool);
//dirty = true;
return clone;
}
/// <summary>
/// 释放目标对象
/// </summary>
/// <param name="clone"></param>
public void releaseObject(GameObject clone)
{
clone.SetActive(false);
if (instanceLookup.ContainsKey(clone))
{
instanceLookup[clone].ReleaseItem(clone);
instanceLookup.Remove(clone);
//dirty = true;
}
else
{
Debug.LogWarning("No pool contains the objcet : " + clone.name);
}
}
/// <summary>
/// 创建指定游戏对象(返回Func)
/// </summary>
/// <param name="prefab"></param>
/// <returns></returns>
private GameObject InstatiatePrefab(GameObject prefab)
{
var go = Instantiate(prefab) as GameObject;
if (root != null) go.transform.parent = root;
return go;
}
//public void PrintStatus()
//{
// foreach (KeyValuePair<GameObject, ObjectPool<GameObject>> keyVal in prefabLookup)
// {
// Debug.Log(string.Format("Object pool for Prefab: {0} InUse:{1} Total {2}", keyVal.Key.name, keyVal.Value.CountUsedItems,keyVal.Value.Count));
// }
//}
//以下是做成了静态方法,直接通过类调用,可有可无,不写静态就直接通过单例来调用
public static void WarmPool(GameObject prefab, int size)
{
Instance.warmPool(prefab, size);
}
public static GameObject SpawnObject(GameObject prefab)
{
return Instance.spawnObject(prefab);
}
public static GameObject SpawnObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
return Instance.spawnObject(prefab, position, rotation);
}
public static void ReleaseObject(GameObject clone)
{
Instance.releaseObject(clone);
}
}
2、单例类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
private static T instance;
public static T Instance
{
get
{
if (instance == null)
{
T[] managers = Object.FindObjectsOfType(typeof(T)) as T[];//尝试获取场景中当前所有的此对象
if (managers.Length != 0)
{
if (managers.Length == 1)//如果只有一个,那么直接return,找到合适的了,下边不执行了
{
instance = managers[0];
instance.gameObject.name = typeof(T).Name;
return instance;
}
else//若存在多个,则全部删除,之后重新建立符合条件的单例
{
Debug.LogError("Class" + typeof(T).Name + "exists multiple times in volation of singleton pattern. destroying all copies");
foreach (var item in managers)
{
Destroy(item.gameObject);
}
}
}
var go = new GameObject(typeof(T).Name, typeof(T));//创建此对象的单例
instance = go.GetComponent<T>();
DontDestroyOnLoad(go);
}
return instance;
}
set { instance = value as T; }
}
}
3、Cube移动、自释放的控制
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeStatus : MonoBehaviour
{
private float timeCube = 2;//移动时间
private void Update()
{
this.transform.Translate(Vector3.forward * Time.deltaTime * 30, Space.World);//移动
if (timeCube >0)
{
timeCube -= Time.deltaTime;
if (timeCube <=0)
{
timeCube = 2;
PoolManager.ReleaseObject(this.gameObject);
}
}
}
}
4、Cube的创建
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CubeController : MonoBehaviour
{
public GameObject myCubePrefab;//预制体,手动赋值
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
print("按下了鼠标右键");
Vector3 v = new Vector3(Input.mousePosition.x, Input.mousePosition.y,10f);
PoolManager.SpawnObject(myCubePrefab, Camera.main.ScreenToWorldPoint(v), Quaternion.identity);
}
}
}
注意:随便 创建一个Cube做成预制体,将此预制体托给脚本CubeController ,记得给Root赋值(空物体就行)
完成!