尽可能使用对象池:
在unity中,内存的分配和内存消耗都会程序带来影响,以及每次C#对实例对象的垃圾回收都很吃力,会占用大量的CPU算力,因此我们才需要尽可能的使用对象池将暂时不适用的对“存放起来”,待到再次使用的时候直接拿出来,而省去实例化的操作”
ps:
减少内存的分配除了使用对象池外,还可以在对象池上使用预加载来优化,在程序运行前让对象池中的对象分配的多一些,这样在需要实例对象的时候就不在需要临时分配内存了。同时!此方法也可以拓展到资源内存,类实例对象有对象池,资源也可以有对象池,在核心程序运行前,如果能够提前知道后面的加载的内容,那么就提前将资源内容加载到内存就可以减少让内存分配的次数,甚至完全避免临时的加载和分配。
例如:统计每个角色图样的资源和实例对象在下一个场景中数量并提前加载,或者接近某个出口或者出口的时候开始预测即将进入的场景内容等
实现思路:
- 使用queue先入先出的特点(使用stack也可以)存放同一种实例对象
- 以相同对象的name为key,queue<实例对象>为value 生命字典作为对象池
- 实现基本的三个方法:入池,取出和清除
公共函数
函数名 | 解释 |
---|---|
GetObj(GameObject) | 从对象池中得到一个游戏对象,如果池中不存在就会创建一个 |
PushAllChildren(GameObject) | 将传入物体的子物体全部放入对象池 |
PushObj(GameObject) | 将游戏对象放入对象池 |
实现:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//对象池
public class ObjectPool {
//存放所有池中物体的父对象
private GameObject poolPos = GameObject.Find("ObjectPool");
private static ObjectPool instance;
public static ObjectPool Instance {
get {
if (instance == null) instance = new ObjectPool();
return instance;
}
}
//字典实现
private Dictionary<string, Queue<GameObject>> pool = new Dictionary<string, Queue<GameObject>>();
//入池
public void PushObj(GameObject perfab) {
//由于生成的预制体的name后面都会加上(Clone),所以我们需要将其去除
string name=perfab.name.Replace("(Clone)",string.Empty);
//如果不存在该类的游戏物体就在对象池中加入,存在则直接加入
if (!pool.ContainsKey(name)) {
pool.Add(name, new Queue<GameObject>());
}
pool[name].Enqueue(perfab);
perfab.SetActive(false);//记得将放入的游戏物体的属性设置为false
}
//将父对象的所有子对象全部加入对象池
public void PushAllChildren(GameObject parent) {
while (parent.transform.childCount>0) {
PushObj(parent.transform.GetChild(0).gameObject);
}
}
//从池中拿物体
public GameObject GetObj(GameObject perfab) {
GameObject result;
//如果池中没有该游戏物体或者游戏物体的队列中已经没有剩余的游戏对象时。
//在这种情况下需要实例化一个新的物体,然后在判断是否需要在池中新加一个健值对还是直接放入队列中去
//然后把新生成的放入池中。
if (!pool.ContainsKey(perfab.name) || pool[perfab.name].Count == 0) {
result = GameObject.Instantiate(perfab);
if (poolPos == null) {
poolPos = new GameObject("ObjectPool");
}
GameObject child = GameObject.Find(perfab.name + "Pool");
if (child == null) {
child = new GameObject(perfab.name + "Pool");
child.transform.SetParent(poolPos.transform);
}
result.transform.SetParent(child.transform);
PushObj(result);
}
//然后直接拿去就可以了,因为无论第一次存取还是没有游戏对象,还是存在游戏对象,到了这一步都可以确保池中有物体。
result = pool[perfab.name].Dequeue();
result.SetActive(true);
return result;
}
//清空池中对象
public void Clear(){
pool.Clear();
}
}
使用案例:
注意:对象池类不需要自己手动实例化。
//存入游戏物体时。
ObjectPool.Instance.PushObj(gameObject);
//从对象池中取出游戏物体
GameObject localBullet = ObjectPool.Instance.GetObj(bullet);