为了优化项目资源,我们在场景中最好使用对象池来储存子弹这种需要不断实例出来的对象;因为像子弹这种对象,如果实例出来,用Destroy来销毁后它其实还是占用内存资源的,这些被占用的内存在下下个场景加载完成后才会清空,所以我们要把这些能够重复利用的对象放到对象池中,当使用时把它的SetActive设为true,不使用时设为false,这样就避免不断地重复实例和销毁对象。
对象池的特点:
1.节省对象重复创建和初始化所耗费的时间
2.简化对象获取和使用的过程
3.优化内存资源,提高系统性能
下面就来用一个模拟子弹弹夹来简单介绍对象池的使用。
先创建一个新场景,里面创建一个地面Plane和一个方块Cube,用Cube来模拟枪(虽然很low,就将就着用,这不是重点= =),如图:
然后再创建一个子弹(其实就是一个Sphere)预设体,如图:
Paste_Image.png(记得改一下Sphere的Scale值为0.2)
这个时候我们就可以先写一个对象池的脚本:
public class GameObjectPool : MonoBehaviour
{
const int number = 10;//子弹弹夹的容量
bool isin = true;//用来判断是否实例子弹对象
int n;//用来记录实例出子弹对象的个数
//集合里面的元素,相当于对象池里面的对象,这里的集合可看作为对象池。
static List<GameObject> pools = new List<GameObject>();
//再声明一个集合,用来回收发射出去的子弹对象
List<GameObject> pb = new List<GameObject>();
//首先创建一个单例
private GameObjectPool() { }
private static GameObjectPool instance;
public static GameObjectPool GetInstance(GameObject name)//此处的Gameobject name就是需要放入对象池的对象--子弹
{
if (instance == null)
{
print("实例池子");
//动态的生成一个名为“GameObjectPool”的对象并将单例脚本附加上去
instance = new GameObject("GameObjectPool").AddComponent<GameObjectPool>();
for (int i = 0; i < number; i++)//先往对象池中放入十发子弹
{
name.SetActive(false);
pools.Add(name);
}
}
return instance;
}
//从对象池中取对象
public GameObject MyInstantiate()
{
if (pools.Count == 0)//如果十发子弹打完
{
print("没子弹了:" + pools.Count);
return null;
}
else
{
// print(pools[0].name);
//取出对象池里面的第一个元素
GameObject obj= pools[0];
if (isin)//判断是否已经实例了十发子弹
{
//obj = pools[0];
Instantiate(obj);//实例出子弹
n++;//计数加加
if (n == number)
{
isin = false;
}
}
//将对象设置为激活状态
obj.SetActive(true);
//将被取出的元素,从对象池中移除
pools.Remove(obj);
return obj;
}
}
//回收子弹的方法
public void MyDisable(GameObject name)
{
//将传进来的对象隐藏(处于非激活状态)
name.SetActive(false);
//回收子弹添加到pb集合中
pb.Add(name);
}
public void ResetBullet()//换弹夹的方法
{
if (pb.Count != 0)//判断回收子弹的集合是否是空
{
print("pb不是空");
for (int i = 0; i < pb.Count; i++)
{
pools.Add(pb[i]); //把回收的子弹重新加到弹夹中去
}
pb.Clear();
}
}
}
这个脚本不需要挂到任何物体上;
然后我们需要给预设体子弹写一个脚本:
public class Bullet : MonoBehaviour {
void OnEnable()
{
//开启协程
StartCoroutine(DelayDisable(1f));
}
void Update()
{
//子弹自动向前运动
transform.Translate(Vector3.forward * Time.deltaTime * 50);
}
void OnDisable()
{
Debug.Log("I'm over");
}
//子弹消失的方法
IEnumerator DelayDisable(float time)
{
//等待一秒
yield return new WaitForSeconds(time);
//调用单例中向集合pb里面回收子弹对象的方法
GameObjectPool.GetInstance(gameObject).MyDisable(gameObject);
}
}
如图
Paste_Image.png最后我们给Cube加一个脚本,用来控制它发射子弹
public class GameManerge : MonoBehaviour {
//创建子弹的预设体
public GameObject mBulletPrefab;
void Update()
{
//如果按下鼠标左键,发射子弹
if (Input.GetMouseButtonDown(0))
{
//调用单例脚本里面的从对象池中取对象的方法
GameObject go = GameObjectPool.GetInstance(mBulletPrefab).MyInstantiate();
if(go!=null)
{
go.transform.position = transform.position; //设置子弹生成的位置是Cube的位置
}
}
//按下鼠标右键,换弹夹
if (Input.GetMouseButtonDown(1))
{
print("换子弹:");
GameObjectPool.GetInstance(mBulletPrefab).ResetBullet();
}
}
}
如图,
Paste_Image.png
这样就运用对象池做好了一个弹夹效果,场景有点丑但达到的效果还是不错的^_
网友评论