Unity GC优化学习(三):对象池技术与PoolManager插件

来源:互联网 发布:ae渲染快捷键 mac 编辑:程序博客网 时间:2024/05/20 15:41

1.简介

对象池技术——是一种广泛通用的内存优化手段。在首次建立对象时,便将其存储在池子中,需要再次使用时,不是每次都实例化一个新的对象,而是查找回收池中是否存在闲置的对象,将其重新激活利用。

2.简单对象池的实现

单个预支体的对象池

using UnityEngine;using System.Collections;using System.Collections.Generic;namespace Farther{    public class PrefabPool{        #region Public Properties Available in the Editor        public SpwanPool spawnPool;        public Transform prefab;        internal GameObject prefabGO;        /// <summary>        /// 最多可以存在的实例个数,如果超过这一上限,则会强制回收重用最早激活的实例        /// </summary>        public int maxAmount;        #endregion Public Properties Available in the Editor        #region Constructor and Self-Destruction        public PrefabPool(Transform prefab)        {            this.prefab = prefab;            this.prefabGO = prefab.gameObject;            _spawned = new List<Transform> ();            _despawned = new List<Transform>();        }        /// <summary>        /// 供Editor构建界面使用        /// </summary>        public PrefabPool()        {        }        internal void SelfDestruct()        {            this._despawned.Clear ();            this._spawned.Clear ();            this.prefab = null;            this.prefabGO = null;            this.spawnPool = null;        }        #endregion Constructor and Self-Destruction        #region Pool Functionality        internal List<Transform> _spawned;        internal List<Transform> _despawned;        public int totalCount        {            get            {                 int count = 0;                count += this._spawned.Count;                count += this._despawned.Count;                return count;            }        }        internal void inspectorInstructor()        {            this.prefabGO = this.prefab.gameObject;            this._spawned = new List<Transform> ();            this._despawned = new List<Transform> ();        }        internal Transform SpawnInstance(Vector3 pos, Quaternion rot)        {            if (this._spawned.Count >= maxAmount) {                Transform firstIn = this._spawned [0];                this.DespawnInstance (firstIn);            }            Transform inst;            if (this._despawned.Count == 0) {                Debug.Log ("_despawned.Count == 0");                inst = this.SpawnNew (pos,rot);            }             else             {                inst = this._despawned [0];                this._despawned.RemoveAt (0);                this._spawned.Add (inst);                inst.position = pos;                inst.rotation = rot;                inst.gameObject.SetActive (true);            }            return inst;        }        internal bool DespawnInstance(Transform xform)        {            if(this._spawned.Contains(xform))            {                //否则说明该对象已经被despawn了                if (this._spawned.Contains (xform))                {                    this._spawned.Remove (xform);                    this._despawned.Add (xform);                    xform.gameObject.SetActive (false);                }            }            return true;        }        internal Transform SpawnNew(Vector3 pos,Quaternion rot)        {            Transform inst = (Transform)Object.Instantiate (this.prefab, pos, rot);            this._spawned.Add (inst);            return inst;        }        /// <summary>        /// 在该“池中”的_spawned 以及 _despawned 中查找,是否存在目标物体        /// </summary>        /// <param name="instance">Instance.</param>        public bool Contain(Transform instance)        {            bool contains =false;            contains = _spawned.Contains (instance);            if (contains)                return true;            contains = _despawned.Contains (instance);            if (contains)                return true;            return false;        }        #endregion Pool Functionality    }}

管理多个预制体的对象池

using UnityEngine;using System.Collections;using System.Collections.Generic;namespace Farther{    [AddComponentMenu("Custom/SpawnPool")]    //[ExecuteInEditMode]    public sealed class SpwanPool : UnitySingleton<SpwanPool> {        public string poolName;        /// <summary>        /// 是否默认将生成的物体的父物体放置到Group下        /// </summary>        public bool isDefaultGroup;        public Transform group {            get;            private set;        }        public List<PrefabPool> perPrefabPoolOptions = new List<PrefabPool>();        public Dictionary<object,bool> _editorListItemStates = new Dictionary<object, bool>();        /// <summary>        /// 得到只读的预制体池,每次回调用都会产生额外的内存分配,慎用!        /// </summary>        /// <value>The read only prefab pools.</value>        public Dictionary<string ,PrefabPool> readOnlyPrefabPools        {            get            {                 Dictionary<string,PrefabPool> dic = new Dictionary<string, PrefabPool> ();                foreach (PrefabPool pool in m_PrefabsPools) {                    dic [pool.prefabGO.name] = pool;                }                return dic;            }        }        private Dictionary<string,PrefabPool> m_PrefabPoolsDic = new Dictionary<string, PrefabPool>();        /// <summary>        /// Gets the prefab pools.        /// </summary>        /// <value>The prefab pools.</value>        public Dictionary<string ,PrefabPool> PrefabPools        {            get            {                 m_PrefabPoolsDic.Clear ();                foreach (PrefabPool pool in m_PrefabsPools) {                    m_PrefabPoolsDic [pool.prefabGO.name] = pool;                }                return m_PrefabPoolsDic;            }        }        private  List<PrefabPool> m_PrefabsPools = new List<PrefabPool>();        internal List<Transform> _spawned = new List<Transform>();        internal List<Transform> _despawned = new List<Transform>();        void Awake()        {            group = this.transform;            gameObject.name = "Group";            //对编辑器界面的定义的prefab pool进行初始化            for (int i = 0; i < perPrefabPoolOptions.Count; i++)             {                if (perPrefabPoolOptions [i].prefab == null) {                    Debug.LogError ("对象池中的预制体为空!");                    continue;                }                this.perPrefabPoolOptions [i].inspectorInstructor ();            }        }        public Transform Spawn(Transform prefab,Vector3 pos,Quaternion rot,Transform parent)        {            for (int i = 0; i < m_PrefabsPools.Count; i++)             {                if (m_PrefabsPools [i].prefabGO == prefab.gameObject)                 {                    Transform inst = m_PrefabsPools [i].SpawnInstance (pos, rot);                    if (parent != null) {                        ///同样也会适配RectTransform                        bool worldPositionStays = !(inst is RectTransform);                        inst.SetParent (parent, worldPositionStays);                    }                     else if(isDefaultGroup)                    {                        bool worldPositionStays = !(inst is RectTransform);                        inst.SetParent (this.group, worldPositionStays);                    }                    return inst;                }            }            return null;        }        public void Despawn(Transform instance)        {            bool isDespawned = false;            for (int i = 0; i < this.m_PrefabsPools.Count; i++)            {                if (m_PrefabsPools [i].Contain (instance))                 {                    m_PrefabsPools [i].DespawnInstance (instance);                    return;                }            }            Debug.LogError ("无法在对象池中找到该实例!!!!");        }        public PrefabPool AddPrefabPool(Transform prefab)        {            PrefabPool prefabPool = new PrefabPool (prefab);            prefabPool.spawnPool = this;            m_PrefabPoolsDic.Add (prefab.gameObject.name, prefabPool);            m_PrefabsPools.Add (prefabPool);            return prefabPool;        }    }}

使用

using UnityEngine;using System.Collections;using Farther;public class ObjPoolTest : MonoBehaviour {    public Transform prefab1;    public Transform prefab2;    public Transform prefab3;    // Use this for initialization    void Start () {        SpwanPool.instance.AddPrefabPool (prefab1).maxAmount = 2;        SpwanPool.instance.AddPrefabPool (prefab2).maxAmount = 2;        SpwanPool.instance.AddPrefabPool (prefab3).maxAmount = 2;    }    // Update is called once per frame    void Update () {        if (Input.GetKeyDown (KeyCode.A)) {            SpwanPool.instance.Spawn (prefab1, transform.position, transform.rotation, transform);        }        if (Input.GetKeyDown (KeyCode.S)) {            SpwanPool.instance.Spawn (prefab2, transform.position, transform.rotation, transform);        }        if (Input.GetKeyDown (KeyCode.D)) {            SpwanPool.instance.Spawn (prefab3, transform.position, transform.rotation, transform);        }    }}

这部分代码只是实现了最为简单的对象池,存在着很多功能上的不足甚至是缺陷,但是对于理解对象池技术或者是PoolMnanger插件都是存在极其重要的意义。

3.PoolManager介绍

国外大神编写,核心脚本只有三个:PoolManager、PreRunTimePoolItem、 SpawnPool,功能非常强大完善,可以调节配置的属性非常多,但是代码量也是不少,上面的代码也只是抽离了其中的一小部分。

4.PoolMananger基础学习

这里写图片描述

这里写图片描述

这里写图片描述

5.案例学习

1.AudioExample

这个例子让我注意到了,插件内部是使用了SendMessage机制,通知具体的对象被激活或被回收,这两个函数【前提一定要声明为公有的】:

    public void OnSpawned(SpawnPool pool)    public void OnDespawned(SpawnPool pool)

这样就可以订制对象被回收或激活时的行为了。

2.DeleteTest

简单的使用了对象池的创建以及销毁。

3.ExampleScene

使用了PreRunTimePoolItem脚本,场景运行后,这个预先放置好的PoolItem将会更根据PoolName以及PrefabName被管理。

4.OnCreatedDelegateExample

展示了添加对象池创建时的回调,赶脚没啥用。

5.PreloadOverTime

展示了如果随时间匀速生成新的实例。

图中设定为,开启随着时间进行的预加载,预加载总数量为50个,在50帧内加载完成,预加载在该脚本激活后延迟3秒进行。

6.SpaceShooter 官方Demo中实践使用 PoolManger

这里写图片描述

[初学Unity时,学习的第一个官方Demo,当时还不懂得任何内存管理的知识,回首发现在这漫漫的学习过程中,自己已经成长进步了那么多了。]

后记:

1.当随时间自动销毁粒子时,发现,OnEnable会在该对象所在的SpawnPool尚未被加入到PoolManager中就被调用,这个很别扭。
so,下面的脚本尝试了好几次:

using UnityEngine;using System.Collections;using PathologicalGames;public class Done_DestroyByTime : MonoBehaviour{    private SpawnPool explosionPool;    public float lifetime;    private bool initialized = false;    void Awake()    {        initialized = false;    }    void Start()    {        Initialize ();    }    void Initialize()    {        explosionPool = PoolManager.Pools["ExplosionParticle"];        explosionPool.Despawn (transform, lifetime);        initialized = true;    }    void OnEnable ()    {        if (initialized) {            explosionPool.Despawn (transform, lifetime);        }    }}

2.改动量最大的脚本

using UnityEngine;using System.Collections;using PathologicalGames;public class Done_DestroyByTrigger : MonoBehaviour{    public GameObject asteroidExplosion;    public GameObject enemyShipExplosion;    public GameObject playerExplosion;    public int scoreValue;    private Done_GameController gameController;    private string enemyTag = "Enemy";    private SpawnPool explosionPool;    private SpawnPool enemyShipPool;    private SpawnPool playerBoltPool;    private SpawnPool enemyAsteroidPool;    private SpawnPool enemyBoltPool;    void Start ()    {        GameObject gameControllerObject = GameObject.FindGameObjectWithTag ("GameController");        if (gameControllerObject != null)        {            gameController = gameControllerObject.GetComponent <Done_GameController>();        }        if (gameController == null)        {            Debug.Log ("Cannot find 'GameController' script");        }        explosionPool = PoolManager.Pools["ExplosionParticle"];        enemyShipPool = PoolManager.Pools["EnemyShip"];        playerBoltPool = PoolManager.Pools["PlayerBullet"];        enemyAsteroidPool = PoolManager.Pools["Asteroid"];        enemyBoltPool = PoolManager.Pools ["EnemyBullet"];    }    void OnTriggerEnter (Collider other)    {        if (gameObject.CompareTag (Tags.EnemyBolt) ||            gameObject.CompareTag (Tags.EnemyShip) ||            gameObject.CompareTag (Tags.EnemyAsteroid))         {            OnTriggerEnterEnemy (other);        }//      Destroy (other.gameObject);//      Destroy (gameObject);    }    void OnTriggerExit(Collider other)    {        if (gameObject.CompareTag (Tags.Boundary)) {            OnTriggerExitBoudary (other);        }    }    void OnTriggerExitBoudary(Collider other)    {        if (other.gameObject.CompareTag (Tags.EnemyShip)) {            enemyShipPool.Despawn (other.transform);        } else if (other.CompareTag (Tags.EnemyAsteroid)) {            enemyAsteroidPool.Despawn (other.transform);        } else if (other.gameObject.CompareTag (Tags.EnemyBolt)) {            enemyBoltPool.Despawn (other.transform);        } else if (other.gameObject.CompareTag (Tags.PlayerBolt)) {            playerBoltPool.Despawn (other.transform);        }    }    void OnTriggerEnterEnemy(Collider other)    {        if (other.transform.CompareTag (Tags.Player))         {            explosionPool.Spawn (playerExplosion, other.transform.position, other.transform.rotation);            GameObject.Destroy(other.transform.gameObject);            if (gameObject.CompareTag (Tags.EnemyShip)) {                enemyShipPool.Despawn (transform);            } else if (gameObject.CompareTag (Tags.EnemyAsteroid)) {                enemyAsteroidPool.Despawn (transform);            } else if (gameObject.CompareTag (Tags.EnemyBolt)) {                enemyBoltPool.Despawn (transform);            }            gameController.GameOver();        }        if (other.transform.CompareTag (Tags.Boundary) ||            other.transform.CompareTag (Tags.EnemyShip) ||            other.transform.CompareTag (Tags.EnemyAsteroid) ||            other.transform.CompareTag (Tags.EnemyBolt))        {            return;        }        if (other.transform.CompareTag (Tags.PlayerBolt))         {            playerBoltPool.Despawn (other.transform);            if (gameObject.CompareTag (Tags.EnemyShip)) {                enemyShipPool.Despawn (transform);                explosionPool.Spawn (enemyShipExplosion,transform.position, transform.rotation);            } else if (gameObject.CompareTag (Tags.EnemyAsteroid)) {                enemyAsteroidPool.Despawn (transform);                explosionPool.Spawn (asteroidExplosion,transform.position, transform.rotation);            }            gameController.AddScore(scoreValue);            return;        }    }}

3.在SpawnPool中加入的两个新的自定义函数

        /// <summary>        /// 随机产生/激活对象池中的某一预制体的实例        /// </summary>        /// <returns>The spawn.</returns>        /// <param name="pos">Position.</param>        /// <param name="rot">Rot.</param>        public Transform RandomSpawn(Vector3 pos,Quaternion rot)        {            int upbound = _prefabPools.Count;            int index = Random.Range (0, upbound);            PrefabPool pool = _prefabPools [index];            return pool.SpawnInstance (pos,rot);        }        /// <summary>        /// 推荐当对象池中只有一个预支体时使用,激活对象池中最先被加入的预制体        /// </summary>        /// <param name="pos">Position.</param>        /// <param name="rot">Rot.</param>        public Transform Spawn(Vector3 pos,Quaternion rot)        {            if (_prefabPools.Count > 0) {                return _prefabPools [0].SpawnInstance (pos, rot);            }            Debug.LogError ("对象池为空无法进行任何Spawn!!");            return null;        }

4.在SpawnPool中发现了一个比较好用的延迟回收的重载函数

        /// <description>        /// See docs for Despawn(Transform instance). This expands that functionality.        ///   If the passed object is managed by this SpawnPool, it will be         ///   deactivated and made available to be spawned again.        /// </description>        /// <param name="item">The transform of the instance to process</param>        /// <param name="seconds">The time in seconds to wait before despawning</param>        public void Despawn(Transform instance, float seconds)        {            this.StartCoroutine(this.DoDespawnAfterSeconds(instance, seconds, false, null));        }

最后改好的Demo:

        http://pan.baidu.com/s/1kVIEg3l