关于对象池的简单使用
发表于2019-03-19
上一篇中给大家分享了《对象池的设计及实现》,这篇我们就来介绍下对象池的简单使用,方便大家掌握对象池。
对象池概念
对象池是一种Unity经常用到的内存管理服务,它的作用在于可以减少创建每个对象的系统开销。
在Unity游戏开发的过程中经常会创建一些新的对象,如果数量较少还可以接受,如果创建的新对象数量庞大,那么对内存而言是一个极大的隐患。例如射击游戏当中,每发射一颗子弹,都要创建一个新的子弹对象,那么子弹是数量庞大,可想而知一场游戏当中会创建多少这样的新对象,那么如果这些子弹创建之后都对游戏起着关键且持续性的作用也无可厚非,问题是子弹发射完成之后,几秒之后就不再拥有任何的意义,一般会将它自动的隐藏,也就是我们所说的SetActive(false),因此大量的非活跃对象出现在游戏场景当中。
为了解决大量创建重复对象造成的内存损耗,我们采用对象池的方式来解决。
下面用一个例子来演示。
用代码在plane上自动生成一堵方块墙,通过点击屏幕来发射子弹,击塌这堵墙。
生成墙以及发射子弹的代码 (挂在摄像机上):
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameController : MonoBehaviour { public int row = 6; public Vector2 offset = new Vector2(); public GameObject cubPrefab; public GameObject bulletPrefab; private RaycastHit hit; public float speed=3; void Start () { //生成墙 for (int i = 0; i < row; i++) { for (int j= 0; j < row; j++) { Instantiate(cubPrefab, new Vector3(i, j, 0)+new Vector3(offset.x,offset.y,0), Quaternion.identity); } } } // Update is called once per frame void Update () { if (Input.GetMouseButtonDown(0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray,out hit)) { Vector3 dir = hit.point - Camera.main.transform.position; //从对象池中获取对象 GameObject bullet = ObjectPool.GetInstance().GetObj("Bullet"); bullet.transform.position = Camera.main.transform.position; bullet.GetComponent<Rigidbody>().velocity = dir.normalized * speed; } } } }
对象池代码:(不需要挂载)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ObjectPool { #region 单例 private static ObjectPool instance; private ObjectPool() { pool = new Dictionary<string, List<GameObject>>(); prefabs = new Dictionary<string, GameObject>(); } public static ObjectPool GetInstance() { if (instance==null) { instance = new ObjectPool(); } return instance; } #endregion /// <summary> /// 对象池 /// </summary> private Dictionary<string,List<GameObject>> pool; /// <summary> /// 预设体 /// </summary> private Dictionary<string, GameObject> prefabs; /// <summary> /// 从对象池中获取对象 /// </summary> /// <param name="objName"></param> /// <returns></returns> public GameObject GetObj(string objName) { //结果对象 GameObject result=null; //判断是否有该名字的对象池 if (pool.ContainsKey(objName)) { //对象池里有对象 if (pool[objName].Count>0) { //获取结果 result = pool[objName][0]; //激活对象 result.SetActive(true); //从池中移除该对象 pool[objName].Remove(result); //返回结果 return result; } } //如果没有该名字的对象池或者该名字对象池没有对象 GameObject prefab = null; //如果已经加载过该预设体 if (prefabs.ContainsKey(objName)) { prefab = prefabs[objName]; } else //如果没有加载过该预设体 { //加载预设体 prefab = Resources.Load<GameObject>("Prefabs/"+objName); //更新字典 prefabs.Add(objName, prefab); } //生成 result = UnityEngine.Object.Instantiate(prefab); //改名(去除 Clone) result.name = objName; //返回 return result; } /// <summary> /// 回收对象到对象池 /// </summary> /// <param name="objName"></param> public void RecycleObj(GameObject obj) { //设置为非激活 obj.SetActive(false); //判断是否有该对象的对象池 if (pool.ContainsKey(obj.name)) { //放置到该对象池 pool[obj.name].Add(obj); } else { //创建该类型的池子,并将对象放入 pool.Add(obj.name, new List<GameObject>() { obj }); } } void Start () { } // Update is called once per frame void Update () { } }
挂载在子弹上,自动回收到对象池的代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Bullet : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { } /// <summary> /// 3秒后自动回收到对象池 /// </summary> /// <returns></returns> IEnumerator AutoRecycle() { yield return new WaitForSeconds(3f); ObjectPool.GetInstance().RecycleObj(gameObject); } private void OnEnable() { StartCoroutine(AutoRecycle()); } }
这样就减少了重复的生成和销毁子弹产生的内存消耗,优化了性能。
以上就是对象池的简单使用,如果还想对对象池加深了解,可以参考《深入理解对象池技术》。