对象池:对象存储在一个池子中,当需要再次使用时取出,而不需要每次都实例化一个新的对象,将对象循环利用起来。当我们需要大量实例化对象时可采用对象池,如游戏中的子弹等物体,当我们玩射击类游戏时,要发射大量子弹,如果每发子弹直接通过Instantiate全部实例化(笔者在unity中试过大量Instantiate后不销毁,unity引擎直接崩溃了),当然还有打怪类游戏,小怪的生成等。
下面有个小例子
对象池脚本(单例脚本)主要有以下三个部分
1、创建一个集合,当做你的对象池,用来存储对象
2、一个取对象的方法
3、一个存对象的方法
一、创建一个对象池脚本ObjectPools,将脚本设置成单例(单例在Unity中经常使用到),不用挂在任何物体上
注:集合中Add(),添加后,自动添加在集合末尾 Remove()删除元素时,假设删除第一个元素,集合中的元素自动向前补位(详情看另一篇集合泛型)
1 public class ObjectPools : MonoBehaviour { 2 //创建集合作为对象池 3 Listpools = new List (); 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 ();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 else26 {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 }
小结:对象池创建一部分对象,使用对象后,不销毁对象,通过隐藏对象,让人感觉被销毁,待下次需要继续使用时,通过激活之前被隐藏的对象,减少了实例化消耗内存的缺点。但因为创建对象后,对象只是隐藏,未被销毁,在运行时,还是可能占用大量内存。