在公司看源码的时候,发现一个投篮的场景中,玩家把篮球不断投出去,然后不断的创建和销毁对象。于是试着写个缓存池来管理。
有个叫PoolManager的工具类插件可以很方便的实现,可以看这里的介绍Unity3D研究院之初探PoolManager插件。
但是其实对象池就是预先创建一点对象,当我们需要用的时候,去拿就行了。如果没有,再创建。使用完毕后也并不销毁,方便下次使用。有点类似于Android中的listview的holder。看起来并不难,花了点时间写了个。
BufferPoolList.cs
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
42using UnityEngine; using System.Collections; using System.Collections.Generic; public class BufferPoolList{ private List<GameObject> pool; private GameObject prefab; private Transform prefabParent; public BufferPoolList(GameObject obj, Transform parent, int count) { prefab = obj; pool = new List<GameObject>(count); prefabParent = parent; for (int i = 0; i < count; i++) { GameObject objClone = GameObject.Instantiate(prefab) as GameObject; objClone.transform.parent = prefabParent;//为克隆出来的子弹指定父物体 objClone.name = "Clone0" + i.ToString(); objClone.SetActive(false); pool.Add(objClone); } } public GameObject GetObject() { //遍历缓存池 找空闲的物体 foreach (GameObject iter in pool) { if (iter.activeSelf == false) { iter.transform.SetParent(prefabParent); iter.SetActive(true); return iter; } } GameObject newPrefab = GameObject.Instantiate(prefab) as GameObject; newPrefab.transform.SetParent(prefabParent); newPrefab.name = "Clone0" + pool.Count.ToString(); newPrefab.SetActive(true); pool.Add(newPrefab); return newPrefab; } }
在Player上的脚本或者控制类脚本上初始化之后,需要使用我们初始化的物体时,只需要GetObject()即可,用完将物体设置不可见即可。可以在实例化的物体上判断物体是否超出屏幕边界,超出 则 .SetActive(false);设置物体不可见。
这样写虽然不用每次都创建对象,缓存池的目的算是达到了,但是每次获取可用对象时都去做个循环,总感觉怪怪的。所以换种写法,不用list,改用 Queue 队列来写。代码如下:
BufferPool.cs
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
48using UnityEngine; using System.Collections; using System.Collections.Generic; public class BufferPool { private Queue<GameObject> pool; private GameObject prefab; private Transform prefabParent; //使用构造函数构造对象池 public BufferPool(GameObject obj,Transform parent,int count) { prefab = obj; pool = new Queue<GameObject>(count); prefabParent = parent; for (int i = 0; i < count; i++) { GameObject objClone = GameObject.Instantiate(prefab) as GameObject; objClone.transform.parent = prefabParent;//为克隆出来的子弹指定父物体 objClone.name = "Clone0" + i.ToString(); objClone.SetActive(false); pool.Enqueue(objClone); } } public GameObject GetObject() { GameObject obj = null; if (pool.Count > 0) { obj = pool.Dequeue(); //Dequeue()方法 移除并返回位于 Queue 开始处的对象 obj.transform.position = prefabParent.position; } else { obj = GameObject.Instantiate(prefab) as GameObject; obj.transform.SetParent(prefabParent); } obj.SetActive(true); return obj; } //回收对象 public void Recycle(GameObject obj) { obj.SetActive(false); pool.Enqueue(obj);//加入队列 } }
这样获取对象时,就不用做循环了。每次使用时,出列。实例化的物体不再使用时,再让他加入队列。这样也有个不方便的地方,需要在游戏物体的脚本上,拿到 Player上的 BufferPool.cs 脚本 ,来回收对象,因为获取对象时,已经把该实例给移出队列了,所以当不再使用时,必须调用Recycle方法来将其加入到队列中去。
虽然一个用的List ,一个用的 Queue,但是道理是一样的。目的也都是为了解决Unity实例化对象慢的问题。
链接:http://539go.com/2016/11/10/Unity-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B9%8B%E5%86%99%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84%E7%BC%93%E5%AD%98%E6%B1%A0/
最后
以上就是爱听歌星月最近收集整理的关于Unity 性能优化之写个简单的缓存池的全部内容,更多相关Unity内容请搜索靠谱客的其他文章。
发表评论 取消回复