对象池:对象存储在一个池子中,当需要再次使用时取出,而不需要每次都实例化一个新的对象,将对象循环利用起来。当我们需要大量实例化对象时可采用对象池,如游戏中的子弹等物体,当我们玩射击类游戏时,要发射大量子弹,如果每发子弹直接通过Instantiate全部实例化(笔者在unity中试过大量Instantiate后不销毁,unity引擎直接崩溃了),当然还有打怪类游戏,小怪的生成等。
下面有个小例子
对象池脚本(单例脚本)主要有以下三个部分
1、创建一个集合,当做你的对象池,用来存储对象
2、一个取对象的方法
3、一个存对象的方法
一、创建一个对象池脚本ObjectPools,将脚本设置成单例(单例在Unity中经常使用到),不用挂在任何物体上
注:集合中Add(),添加后,自动添加在集合末尾 Remove()删除元素时,假设删除第一个元素,集合中的元素自动向前补位(详情看另一篇集合泛型)
1 public class ObjectPools : MonoBehaviour { 2 //创建集合作为对象池 3 List<GameObject> pools = new List<GameObject>(); 4 //单例 5 private static ObjectPools instance; 6 private ObjectPools() { } 7 public static ObjectPools GetInstance() 8 { 9 if (instance==null) 10 { 11 //创建一个名字为ShellPool的对象,并将单例脚本附上 12 instance = new GameObject("ShellPool").AddComponent<ObjectPools>(); 13 } 14 return instance; 15 16 } 17 //从对象池中取对象 18 public GameObject MyInstantiate(GameObject name) 19 { 20 //对象池中没有对象 实例化对象 21 if (pools.Count==0) 22 { 23 return Instantiate(name, Vector3.zero, Quaternion.identity) as GameObject; 24 } 25 else 26 { 27 //如果池中有对象 取出第一个 28 GameObject go = pools[0]; 29 //将对象设置激活状态 30 go.SetActive(true); 31 //从对象池中删除对象 32 pools.Remove(go); 33 return go; 34 35 } 36 } 37 //向对象池存对象 38 public void DelayInstantiate(GameObject name) 39 { 40 //隐藏对象 41 name.SetActive(false); 42 //放入对象池中(集合中) 43 pools.Add(name); 44 } 45 }
二、创建一个GameManager脚本,控制物体生成(预制体放在Resources文件下)
1 public class GameManager : MonoBehaviour { 2 GameObject shellPrefab; 3 public void Awake() 4 { 5 //找到预制体 6 shellPrefab = Resources.Load("Part01/Prefabs/Shell") as GameObject; 7 } 8 private void Update() 9 { 10 if (Input.GetMouseButtonDown(0)) 11 { 12 //实例化对象 13 ObjectPools.GetInstance().MyInstantiate(shellPrefab); 14 } 15 } 16 }
三、预制体脚本,挂在预制体上,控制子弹向前
1 public class Shell : MonoBehaviour { 2 Transform target; 3 private void OnEnable() 4 { 5 //找到要生成的对象的空物体 位置,将空物体位置赋给对象初始位置 6 target = GameObject.Find("GameBullet").transform; 7 transform.position = target.position; 8 //启动协程 延时两秒 9 StartCoroutine(DelayObject(2f)); 10 } 11 private void Update() 12 { 13 //物体移动 14 transform.Translate(Vector3.forward * Time.deltaTime * 10); 15 } 16 //协程 17 IEnumerator DelayObject(float time) 18 { 19 yield return new WaitForSeconds(time); 20 //调用脚本 向对象池存对象 21 ObjectPools.GetInstance().DelayInstantiate(gameObject); 22 } 23 24 }
小结:对象池创建一部分对象,使用对象后,不销毁对象,通过隐藏对象,让人感觉被销毁,待下次需要继续使用时,通过激活之前被隐藏的对象,减少了实例化消耗内存的缺点。但因为创建对象后,对象只是隐藏,未被销毁,在运行时,还是可能占用大量内存。