前文
为何使用PoolManager?
PoolManager,中文名称,对象池。避免了在运行中反复创建GameObject和进行垃圾处理所造成的内存损耗,尤其是Particle这样的粒子系统,使用PoolManager可以有效的减少内存损耗,同时便于对Clone对象进行统一管理
对象池(模式)一种创建式设计模式
概念:
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer, 但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
若游戏中大量判定出现的粒子系统,且粒子效果在完成释放前不会销毁,大量的粒子Object同时运行时占用了大量内存,就会导致内存溢出,程序会报错,xxxx memory Allocated。
PoolManager
一个构建可重复使用GameObject的对象池,采用Queue来实现不同GameObjectPrefab的内部排序,在PoolManager中构建对象池,指的是为每个Prefab
构建一个专属的Pool Pool的Size 由Engine输入的SerializeField Private 提供,足以构建准确且可复用的对象管理池。Pool 的架构如下:
[System.Serializable]
public struct Pool
{
public int poolSize;
public GameObject prefab;
}
程序构建原理:
使用GetInstanceID。GetInstaceID是gameobject的已架构函数,使用instanceID可以确保复制出来的ID完全可靠。一个Prefab具备唯一且独特的
InstanceID,可以使用InstanceID检测GameObjet路径来源且确保使用完全一样的GameObject,为对象池的内容物提升安保性。
构建PoolDictionary 使用InstanceID作为Key,Queue<GameObject>作为value,如下:
private Dictionary<int,Queue<GameObject>> poolDictionary=new Dictionary<int, Queue<GameObject>>();
这样就完成了顶层架构。
ReUseGameObject,CreatePool在数据结构层级上进行操作。
VFXManger
控制粒子的Manger 通过订阅者模式来调用ParticleGameObject(粒子生成器)
先写订阅者模式: ///此处为一个外置的EventHandlerClass
可以直接在外部调用(类和函数的可访问区域均为public static)
public static EventHandler
{
public static event Action ParticleUse;
public static void CallParticleUse
{
//确定此处为非空
if(ParticleUse!=null)
{
//使用ParticleUse
ParticleUse();
}
}
}
然后,在VFX中使用固有函数,将event需要使用的函数添加入其中
private void OnEnable()
{
Eventhanlder.ParticleUse+=目的函数;
}
private void OnDisabled()
{
EventHandler.ParticleUse-=目的函数;
}
最后在需要调用ParticleGameObject的地方使用EventHandler.CallParticleUse()来调用VFXmanger中添加的粒子效果。
下方为完整代码
单例模式 泛型
当某个类继承于单例SingletonMonoBehaviour时,可以直接调用Instance获得唯一的StaticT,可直接调用
using UnityEngine;
public class SingletonMonoBehaviour<T> : MonoBehaviour where T:MonoBehaviour
{
//static 确保了这个的这个函数只有这一个内存空间,可见Test函数及其输出
private static T instance;
public static T Instance
{
get{
return instance;
}
}
//使用虚函数protected virtual 便于继承子类重写函数Awake
protected virtual void Awake()
{
if(instance==null)
{
instance=this as T;
}
else
{
Destroy(gameObject);
}
}
}
订阅者模式
using UnityEngine;
using System.Collections.Generic;
using System;
public static class EventHandler
{
public static event Action<GameObject,Vector3,Quaternion> ParticleUse;
public static void CallParticleUse(GameObject particleGameObject,Vector3 VFXPosition,Quaternion VFXQuternion)
{
if(ParticleUse!=null)
{
ParticleUse(particleGameObject,VFXPosition,VFXQuternion);
}
}
}
Test
用于说明单例泛型的原理
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
public static int TestValue;
private void Awake()
{
if(this.gameObject.name=="Test1")
{
TestValue=1;
}
}
private void Start()
{
if(this.gameObject.name=="Test2")
{
Debug.Log("Test2Debug"+" "+TestValue);
}
}
}
构建两个TestGameObject并挂载Test
ConSole输出结果如下,可见,在一个类中static变量属于一个唯一的内存空间所有基于此类构建的对象共享同一个static param
PoolManager
用于存储Pool,管理pool和生成PoolDictionary
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolManger : SingletonMonoBehaviour<PoolManger>
{
[System.Serializable]
public struct Pool
{
public int PoolSize;
public GameObject PoolPrefab;
}
[SerializeField]
private List<Pool> poolList=new List<Pool>();
[SerializeField]
private Transform poolParentTransform;
private Dictionary<int,Queue<GameObject>> poolDicitonary=new Dictionary<int, Queue<GameObject>>();
private void Start()
{
CreatePoolDictionary();
}
//通过外部导入的List<Pool>来构建poolDictionary//
private void CreatePoolDictionary()
{
foreach(Pool pool in poolList)
{
if(pool.PoolSize!=0&&pool.PoolPrefab!=null)
{
int poolKey=pool.PoolPrefab.GetInstanceID();
if(!poolDicitonary.ContainsKey(poolKey))
{
InaugurateNewPlaceInPoolDictionary(poolKey,pool);
}
}
}
}
//为每个 poolkey 开辟专属Place
private void InaugurateNewPlaceInPoolDictionary(int poolKey,Pool pool)
{
poolDicitonary.Add(poolKey,new Queue<GameObject>());
GameObject Anchor=new GameObject(pool.PoolPrefab.name+"Anchor");
//Make the newAnchor under the ParentTransform
Anchor.transform.SetParent(poolParentTransform);
for(int i=1;i<=pool.PoolSize;i++)
{
GameObject newPoolGameObject=Instantiate(pool.PoolPrefab,poolParentTransform.position,Quaternion.identity,Anchor.transform);
newPoolGameObject.SetActive(false);
poolDicitonary[poolKey].Enqueue(newPoolGameObject);
}
}
public GameObject ReUsePoolGameObject(GameObject poolPrefab,Vector3 usePosition,Quaternion useQuaternion)
{
int poolKey=poolPrefab.GetInstanceID();
if(poolDicitonary.ContainsKey(poolKey))
{
Queue<GameObject> exactQueue=poolDicitonary[poolKey];
GameObject poolTop=exactQueue.Dequeue();
if(poolTop.activeSelf==true)
poolTop.SetActive(false);
ResetPoolObject(poolTop,usePosition,useQuaternion);
exactQueue.Enqueue(poolTop);
return poolTop;
}
else
{
Debug.Log("there is no value of this key");
return null;
}
}
private void ResetPoolObject(GameObject poolTop,Vector3 usePosition,Quaternion useQuaternion)
{
poolTop.transform.position=usePosition;
poolTop.transform.rotation=useQuaternion;
poolTop.SetActive(true);
}
}
VFXManager
用于委托挂载,并且利用异步协程将VFXSetFalse
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VFXManger : SingletonMonoBehaviour<VFXManger>
{
[SerializeField] GameObject ToReuseVFXGameObjet;
[SerializeField] private WaitForSeconds temporaryWaitSeconds;
private bool Judge=true;
override protected void Awake()
{
base.Awake();
temporaryWaitSeconds=new WaitForSeconds(2f);
}
private IEnumerator DisableTemporaryVFX(GameObject temporaryVFX,WaitForSeconds secondsToWait)
{
yield return secondsToWait;
temporaryVFX.SetActive(false);
}
public void DisPlayVFX(GameObject VFXGameObject,Vector3 VFXPosition,Quaternion VFXQuternion)
{
if(Judge)
{
GameObject temporaryVFX;
temporaryVFX=PoolManger.Instance.ReUsePoolGameObject(ToReuseVFXGameObjet,VFXPosition,VFXQuternion);
StartCoroutine(DisableTemporaryVFX(temporaryVFX,temporaryWaitSeconds));
}
}
private void OnEnable()
{
EventHandler.ParticleUse+=DisPlayVFX;
}
private void OnDisable()
{
EventHandler.ParticleUse-=DisPlayVFX;
}
}
PlayerTest
用于使用委托
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerTest : MonoBehaviour
{
[SerializeField] private GameObject ReUsePoolPrefab=null;
private void Update()
{
if(Input.GetMouseButtonDown(1))
{
Vector3 mouseScreenPosition=Input.mousePosition;
Vector3 mouseWorldPosition=Camera.main.ScreenToWorldPoint(new Vector3(mouseScreenPosition.x,mouseScreenPosition.y,-Camera.main.transform.position.z));
// PoolManger.Instance.ReUsePoolGameObject(ReUsePoolPrefab,mouseWorldPosition,Quaternion.identity);
EventHandler.CallParticleUse(ReUsePoolPrefab,mouseWorldPosition,Quaternion.identity);
}
}
}
效果如下:![](https://i-blog.csdnimg.cn/blog_migrate/04a0f38d1bb1e95681cf1135ecf9cd9c.gif)
康娜爆破