这两天在制作主角的技能以及攻击方式,其中就涉及到关于大量创造与回收物体,比如主角发射一个小火球。假如使用最基础的方式,当然就是不断地通过new对象来进行实例化,然后再用碰撞检测来destroy掉这个火球,这是最开始学习的时候使用的方式。
但是这种方式就会面临一个非常大的问题,就是对性能会产生一个巨大的消耗,当我们频繁的去new对象的时候,每次都会在内存的托管堆上去分配一个空间,然后destroy的时候,只是会将资源释放,内存空间并没有被GC回收,这样就会造成内存的巨大浪费。
怎么解决呢?这时候就出现了对象池这个东西。对象池其实简单通俗的来说,就是对对象的一个管理,当我们需要这个物体的时候,比如说子弹,就实例化这个子弹出来,然后当子弹打中人或者消失,就将子弹的active设置为false,然后下一次发射子弹的时候呢,就不需要去实例化这个子弹,而是将刚才的子弹设置为true(当然,假如需要在短时间内发射很多颗子弹的话,还是需要实例化多颗子弹,但是对象池的使用可以让这种实例化的情况尽量减少)
接下来看看对象池的编写:
首先是编写一个单例模式的基类:
public class MonoSingleton<T> : MonoBehaviour where T: Component
{
public static T m_instance=null;
public static T Intance
{
get
{
m_instance = GameObject.FindObjectOfType(typeof(T)) as T;
if (m_instance == null)
{
GameObject go = new GameObject();
m_instance = go.AddComponent<T>();
go.name = m_instance + "Objectaaaaa";
}
//在场景切换时不要销毁
DontDestroyOnLoad(m_instance);
return m_instance;
}
}
}
然后是对象池
借鉴了这个博客https://blog.csdn.net/leonardo_davinci/article/details/78646562
public class ObjectsPool : MonoSingleton<ObjectsPool>
{
public Dictionary<string, List<GameObject>> dictionary = new Dictionary<string, List<GameObject>>();
//public GameObject m_obj;
/// <summary>
/// 得到存储在对象池中间的预制体
/// </summary>
/// <param name="_name">传入键的名字</param>
/// <returns></returns>
public GameObject GetInstanse(string _name)
{
Debug.Log(_name);
string str ="Prefabs/" + _name;
if (dictionary.ContainsKey(_name))
{
for(int i = 0; i < dictionary[_name].Count; i++)
{
//取值
if (!dictionary[_name][i].activeSelf)
{
dictionary[_name][i].SetActive(true);
return dictionary[_name][i];
}
}
//list数组中当第一个被存储的物体被取出的时候,此时需要第二个相同的物体,就直接实例化一个出来。存储在list数组的第二个空间中
//这样就可以最大限度的减少需要实例化的物体
GameObject object1 = Instantiate(Resources.Load("Prefabs/"+_name)) as GameObject;
dictionary[_name].Add(object1);
return object1;
}
//假如不存在这个键值对,就添加这个键值对
GameObject object2 = Instantiate(Resources.Load("Prefabs/"+_name)) as GameObject;
AddObject(_name, object2);
return object2;
}
public void TestFun()
{
Debug.Log("testfun");
}
public void AddObject(string name,GameObject obj)
{
List<GameObject> list = new List<GameObject>();
list.Add(obj);
dictionary.Add(name, list);
obj.SetActive(false);
}
/// <summary>
/// 直接回收
/// </summary>
/// <param name="go">Go.</param>
public void CollectObject(GameObject go)
{
go.SetActive(false);
}
/// <summary>
/// 延迟回收
/// </summary>
/// <param name="go">Go.</param>
/// <param name="delay">Delay.</param>
public void CollectObject(GameObject go, float delay)
{
StartCoroutine(Collect(go, delay));
}
private IEnumerator Collect(GameObject go, float delay)
{
yield return new WaitForSeconds(delay);
CollectObject(go);
}
/// <summary>
/// 释放资源
/// </summary>
/// <returns>The clear.</returns>
/// <param name="key">Key.</param>
public void Clear(string key)
{
if (dictionary.ContainsKey(key))
{
//Destroy当中所有的对象
for (int i = 0; i < dictionary[key].Count; i++)
{
Destroy(dictionary[key][i]);
}
//清除键当中的所有值
//dictionary[key].Clear();
//清除这个键(键值一起清除)
dictionary.Remove(key);
}
}
/// <summary>
/// 释放所有对象池
/// </summary>
public void ClearAll()
{
List<string> list = new List<string>(dictionary.Keys);
for (int i = 0; i < list.Count; i++)
{
Clear(list[i]);
}
}
}
然后绑在生产者上的生产脚本
{
public Transform tra;
private GameObject fireball;
private GameObject gameObj;
private string _name;
public Vector3 managerposition;
public Vector3 maposition;
public Vector3 nor;
public float stay_time;
public FireBallController ballController;
// Start is called before the first frame update
void Awake()
{
_name = "FireBalls";
gameObj =Instantiate(Resources.Load("Prefabs/"+_name)) as GameObject;
gameObj.SetActive(false);
}
// Update is called once per frame
void Update()
{
managerposition = GameObject.Find("Manager").transform.position;
maposition = GameObject.Find("Ma").transform.position;
nor = (managerposition - maposition).normalized;
if (Input.GetKeyDown(KeyCode.J))
{
fireball = ObjectsPool.Intance.GetInstanse(_name);
fireball.transform.position = tra.position;
}
然后再在火球上绑定火球的运行脚本就OK了