第一种写法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
using System.Collections.Generic;
using UnityEngine;

public abstract class Pooling<T> : MonoBehaviour where T : Component
{
	private List<T> m_pool = new List<T>();

	[Header("Pool Settings")]
    [SerializeField] private T m_prefab;
	[SerializeField] private Transform m_parent;
    [SerializeField] private int m_poolSize = 1;
    [SerializeField] private bool m_poolCanGrow = false;

    public virtual void Awake()
    {
        if (m_prefab == null)
        {
            Debug.LogError("Has not been defined a prefab!");
        }

        GeneratePool();
    }

	public void SetObjectToPool(T oldObject)
	{
		oldObject.gameObject.SetActive(false);

		oldObject.transform.localPosition = Vector3.zero;
		oldObject.transform.localRotation = Quaternion.identity;
	}

	protected T GetObjectFromPool(bool active = true)
    {
        for (int i = 0; i < m_pool.Count; i++)
        {
            T obj = m_pool[i];

			if (!obj.gameObject.activeInHierarchy)
			{
				obj.gameObject.SetActive(active);
				
				return obj;
			}
        }

        if (m_poolCanGrow)
        {
            var obj = CreateNewObject();

            obj.gameObject.SetActive(active);

            return obj;
        }

        return null;
    }

    private void GeneratePool()
    {
        for (int i = 0; i < m_poolSize; i++)
        {
            CreateNewObject();
        }
    }

	private T CreateNewObject()
    {
        var newObject = Instantiate(m_prefab) as T;

		newObject.gameObject.SetActive(false);
		
		newObject.transform.SetParent(m_parent);
		newObject.transform.localPosition = Vector3.zero;
		newObject.transform.localRotation = Quaternion.identity;

        m_pool.Add(newObject);

        return newObject;
    }
}

这种写法,需要添加一个类来继承它,并指定池中对象所属的类为T,一共需要三个类。在使用时,可以直接添加到一个GameObject上。

第二种写法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class GameObjectPool<T> where T : MonoBehaviour
{
	private Stack<T> m_objectStack;

	private Action<T> m_resetAction;
	private Action<T> m_onceInitAction;

	private GameObject m_prefabObj;

	public GameObjectPool(GameObject prefabObj, Action<T> resetAction = null, Action<T> onceInitAction = null)
	{
		m_objectStack = new Stack<T> ();
		m_resetAction = resetAction;
		m_onceInitAction = onceInitAction;
		m_prefabObj = prefabObj;
	}

	public T Get()
	{
		if (m_objectStack.Count > 0) 
		{
			T t = m_objectStack.Pop ();
            t.gameObject.SetActive (true);

			if (m_resetAction != null)
				m_resetAction (t);

			return t;
		}
		else 
		{
			GameObject gameObject = GameObject.Instantiate (m_prefabObj);
			T t = gameObject.GetComponent<T> ();

			if (m_onceInitAction != null)
				m_onceInitAction (t);

			return t;
		}
	}

	public void Store (T obj)
	{
        obj.gameObject.SetActive (false);
        if (!m_objectStack.Contains (obj)) {
            m_objectStack.Push (obj);
        }
	}
}

这种对象池的写法使用了栈作为数据结构,在构造函数中可以指定Prefab,一个自动重置对象的委托,一个只执行一次的初始化委托。好处是,不需要挂在某个GameObject上了,下面的例子创建了一个Dictionary保存了多个对象池。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private Dictionary<int, GameObjectPool<DefenseBuilding>> defenseBuildingPoolDict = new Dictionary<int, GameObjectPool<DefenseBuilding>>();

GameObjectPool<DefenseBuilding> GetDefenseBuildingPoolByNpcId (int resId)
{
    if (!defenseBuildingPoolDict.ContainsKey (resId)) {
        var pool = new GameObjectPool<DefenseBuilding> (GetPrefabByResId(resId), null, (DefenseBuilding obj) => {
            SetNPCProperty(obj, selectedEntity.DataId, 0);
        });
        defenseBuildingPoolDict.Add (resId, pool);
    }
    return defenseBuildingPoolDict [resId];
}

第三种写法

除了为每个资源创建一个对象池,通过Dictionary去获取不同的对象池外,还可以只使用一个对象池。对象池的Key可以是字符串,这里使用的是一个Prefab或者GameObject。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public sealed class ObjectPool
{
    private Dictionary<GameObject, Queue<GameObject>> container = new Dictionary<GameObject, Queue<GameObject>>();

    private static ObjectPool instance = null;
    public static ObjectPool Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new ObjectPool();
            }
            return instance;
        }
    }

    /// <summary>
    /// Reset the pool but does not destroy the content.
    /// </summary>
    public void Reset()
    {
        instance = null;
    }

    private ObjectPool() { }

    /// <summary>
    /// Adds to pool.
    /// </summary>
    /// <returns><c>true</c>, if item was successfully created, <c>false</c> otherwise.</returns>
    /// <param name="prefab">The prefab to instantiate new items.</param>
    /// <param name="count">The amount of instances to be created.</param>
    /// <param name="parent">The Transform container to store the items. If null, items are placed as parent</param>
    public bool AddToPool(GameObject prefab, int count, Transform parent = null)
    {
        if (prefab == null || count <= 0) { return false; }
        for (int i = 0; i < count; i++)
        {
            GameObject obj = PopFromPool(prefab, true, false, parent);
            PushToPool(ref obj, true, parent);
        }
        return true;
    }

    /// <summary>
    /// Pops item from pool.
    /// </summary>
    /// <returns>The from pool.</returns>
    /// <param name="prefab">Prefab to be used. Matches the prefab used to create the instance</param>
    /// <param name="forceInstantiate">If set to <c>true</c> force instantiate regardless the pool already contains the same item.</param>
    /// <param name="instantiateIfNone">If set to <c>true</c> instantiate if no item is found in the pool.</param>
    /// <param name="container">The Transform container to store the popped item.</param>
    public GameObject PopFromPool(GameObject prefab, bool forceInstantiate = false, bool instantiateIfNone = false, Transform container = null)
    {
        GameObject obj = null;
        if (forceInstantiate == true) { 
            obj = CreateObject (prefab, null); 
        } else {
            Queue<GameObject> queue = FindInContainer (prefab);
            if (queue.Count > 0) {
                obj = queue.Dequeue ();
                obj.transform.parent = container;
            }
        }
        if (obj == null && instantiateIfNone == true)
        {
            obj = CreateObject(prefab, container);
        }
		obj.GetComponent<IPoolObject> ().ParentTransform = container;
        obj.GetComponent<IPoolObject> ().Init ();
        return obj;
    }

    private Queue<GameObject> FindInContainer(GameObject prefab)
    {
        if (container.ContainsKey(prefab) == false)
        {
            container.Add(prefab, new Queue<GameObject>());
        }
        return container[prefab];
    }

    private GameObject CreateObject(GameObject prefab, Transform container)
    {
        IPoolObject poolObjectPrefab = prefab.GetComponent<IPoolObject>();
        if(poolObjectPrefab== null){Debug.Log ("Wrong type of object"); return null;}

        GameObject obj = (GameObject)Object.Instantiate(prefab);
        IPoolObject poolObject = obj.GetComponent<IPoolObject>();
        obj.name = prefab.name;
        poolObject.Prefab = prefab;
        obj.transform.parent = container;
        return obj;
    }

    /// <summary>
    /// Pushs back the item to the pool.
    /// </summary>
    /// <param name="obj">A reference to the item to be pushed back.</param>
    /// <param name="retainObject">If set to <c>true</c> retain object.</param>
    /// <param name="newParent">The Transform container to store the item.</param>
    public void PushToPool(ref GameObject obj, bool retainObject = true, Transform newParent = null)
    {
        if (obj == null) { return; }
        if (retainObject == false)
        {
            Object.Destroy(obj);
            obj = null;
            return;
        }
        if (newParent != null)
        {
            obj.transform.parent = newParent;
        }
        IPoolObject poolObject = obj.GetComponent<IPoolObject>();
        if(poolObject != null)
        {
            GameObject prefab = poolObject.Prefab;
            Queue<GameObject> queue = FindInContainer(prefab);
            queue.Enqueue(obj);
            obj.SetActive(false);
        }
        obj = null;
    }

    /// <summary>
    /// Releases the pool from all items.
    /// </summary>
    /// <param name="prefab">The prefab to be used to find the items.</param>
    /// <param name="destroyObject">If set to <c>true</c> destroy object, else object is removed from pool but kept in scene. </param>
    public void ReleaseItems(GameObject prefab, bool destroyObject = false)
    {
        if (prefab == null) { return; }
        Queue<GameObject> queue = FindInContainer(prefab);
        if (queue == null) { return; }
        while (queue.Count > 0)
        {
            GameObject obj = queue.Dequeue();
            if (destroyObject == true)
            {
                Object.Destroy(obj);
            }
        }
    }

    /// <summary>
    /// Releases all items from the pool and destroys them.
    /// </summary>
    public void ReleasePool() 
    {
        foreach (var kvp in container)
        {
            Queue<GameObject> queue = kvp.Value;
            while (queue.Count > 0)
            {
                GameObject obj = queue.Dequeue();
                Object.Destroy(obj);
            }
        }
        container = null;
        container = new Dictionary<GameObject, Queue<GameObject>>();
    }
}

public interface IPoolObject
{
    GameObject Prefab { get; set;}
	Transform ParentTransform{ get; set; }
    void Init();
}

下面的例子展示了动态加载Prefab时使用该对象池的方法,如果不是动态加载,CheckPoolPrefabDictionary函数是不需要的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// 资源Id与GameObject的对应列表
public Dictionary<int, GameObject> PoolPrefabDictionary = new Dictionary<int, GameObject> ();

/// 检查是否有这个GameObject,没有则创建一个
public void CheckPoolPrefabDictionary<T> (int resId, float scale) where T : MonoBehaviour,IPoolObject
{
    if (!PoolPrefabDictionary.ContainsKey (resId)) {
        Tables_Resources res = Tables_Resources_Manage.GetIns ().GetTabList [resId.ToString()];
        GameObject prefab = (GameObject) Instantiate (Resources.Load (res.Path + res.Name), BattleController.Instance.PrefabParent);
        if (prefab.GetComponent<T> () == null) {
            prefab.AddComponent<T> ();
        }
        prefab.transform.position = BattleController.Instance.InvisiblePos;
        prefab.transform.rotation = Quaternion.identity;
        ToolFunc.ScaleParticleSystem (prefab, scale);
        prefab.SetActive (false);
        BattleController.Instance.PoolPrefabDictionary.Add (resId, prefab);
    }
}

/// Bullet类必须继承自MonoBehaviour,同时实现IPoolObject接口, BulletParent是一个公共的父节点,最后记得使用SetActive函数激活。
CheckPoolPrefabDictionary<Bullet> (resId, scale);
GameObject bulletObj = ObjectPool.Instance.PopFromPool (PoolPrefabDictionary [resId], false, true, BulletParent);
if (bulletObj != null)
{
    bulletObj.transform.position = bulletPos;
    bulletObj.SetActive (true);
}

第四种写法

上面的写法比较麻烦,除了基础的对象池功能,还包括了一些自动初始化的功能,下面介绍一种只包含基础功能的写法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

public class ObjectPool
{
    private Func<GameObject> instantiator;
    private List<GameObject> unused;

    public ObjectPool(Func<GameObject> instantiator, int initial_count = 0)
    {
        this.instantiator = instantiator;
        this.unused = new List<GameObject>();
        for (int i = 0; i < initial_count; i++)
        {
            this.unused.Add(instantiator());
        }
    }

    public void Destroy()
    {
        for (int i = 0; i < this.unused.Count; i++)
        {
            UnityEngine.Object.Destroy(this.unused[i]);
        }
        this.unused.Clear();
    }

    public GameObject GetInstance()
    {
        GameObject obj2 = null;
        if (this.unused.Count > 0)
        {
            obj2 = this.unused[this.unused.Count - 1];
            this.unused.RemoveAt(this.unused.Count - 1);
            return obj2;
        }
        return this.instantiator();
    }

    public void ReleaseInstance(GameObject go)
    {
        this.unused.Add(go);
    }
}

这种写法比较简单,如果需要多个对象池,需要创建多个 ObjectPool 对象,并在构造函数中立即创建所需的个数。由于只提供了初始化函数 instantiator,所以从池中获取对象后,还需要做一些重置对象的工作。