什么是对象池?
通常我们需要用到某个游戏对象时,我们会在内存中分配一部分空间new一个对象出来,之后在该对象完成任务之后,再将其摧毁,释放掉内存。在Unity中就是我们在需要的时候调用Instantiate()函数来生成一个游戏对象。
GameObject gameObject = GameObject.Instantiate(prefab);
在用完该对象之后,调用Destory()函数将其摧毁。
Destoty(gameobject);
Unity引擎的自动内存管理系统会帮我们完成垃圾回收(GC)的工作,但垃圾回收本身也是要消耗性能的,如果对象过多就会十分消耗性能,如果我们要频繁地生成与摧毁对象,GC就会非常忙,导致游戏卡顿甚至崩溃,因此我们应该尽可能少的触发垃圾回收机制,这就要用到对象池了。
对象池的原理:
在游戏初始化的阶段,先取出一块完整的内存空间,用来生成运行过程中需要用到的对象,让它们预备好随时被启用。在我们需要用到某个对象的时候,我们不再new新的对象,而是从预备好的这块内存空间中,即对象池中取出来该对象使用,用完之后再将该对象放回对象池,随取随用。
使用对象池与不使用对象池对比:
很明显,使用对象池相比于不使用对象池会给游戏带来显著的性能提升,尤其是对于像是射击类游戏这种需要创建大量的子弹对象的游戏,使用对象池是必不可少的,同时使用对象池还能够减少目标游戏平台的性能消耗,增加游戏运行流畅度。
创建对象池:
这里我们用队列(Queue)的数据结构来存储我们要实例化的游戏对象,即我们做好的预制体(Prefabs)。
Queue<GameObject> queue;
创建对象池的步骤:
(1)生成备用对象,禁用它们,准备随时被启用,将它们挂载到同一个父对象上,将对象初始化。
//复制预制体对象 加入对象池
GameObject Copy()
{
var copy = GameObject.Instantiate(prefab, parent);
copy.SetActive(false);
return copy;
}
//初始化对象池 将所有对象入队
public void Init(Transform parent)
{
queue = new Queue<GameObject>();
this.parent = parent;
for (int i = 0; i < size; i++)
{
queue.Enqueue(Copy());
}
}
(2)从池中取出可用对象,将其出队,若池中没有可用对象,则调用生成备用对象函数。
//从池中取出可用的对象
GameObject AvailableObject()
{
GameObject avaliableObject = null;
if (queue.Count > 0) //&& !queue.Peek().activeSelf
{
avaliableObject = queue.Dequeue();
}
else
{
avaliableObject = Copy();
}
//queue.Enqueue(avaliableObject);
return avaliableObject;
}
(3)启用可用对象,可以根据自己的实际要求写几个重载,让启用对象做出某些选择。
//启用可用的对象
public GameObject PreparedObject()
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
return proparedObject;
}
//启用可用的对象 并让其出现在场景中的某个位置
public GameObject PreparedObject(Vector3 position)
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
proparedObject.transform.position = position;
return proparedObject;
}
//启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度
public GameObject PreparedObject(Vector3 position, Quaternion rotation)
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
proparedObject.transform.position = position;
proparedObject.transform.rotation = rotation;
return proparedObject;
}
//启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度 并缩放其大小
public GameObject PreparedObject(Vector3 position, Quaternion rotation, Vector3 localScale)
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
proparedObject.transform.position = position;
proparedObject.transform.rotation = rotation;
proparedObject.transform.position = localScale;
return proparedObject;
}
(4)让完成任务的对象返回对象池。文章来源:https://www.toymoban.com/news/detail-810483.html
//让已用过的对象返回对象池(可与启用写在同一函数中)
public void Return(GameObject gameObject)
{
queue.Enqueue(gameObject);
}
简单对象池完整代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Pool
{
public GameObject Prefab => prefab;
[SerializeField] GameObject prefab;
//存储所有复制体的数量
[SerializeField] int size = 1;
//池的父对象
Transform parent;
Queue<GameObject> queue;
//初始化对象池 将所有对象入队
public void Init(Transform parent)
{
queue = new Queue<GameObject>();
this.parent = parent;
for (int i = 0; i < size; i++)
{
queue.Enqueue(Copy());
}
}
//复制预制体对象 加入对象池
GameObject Copy()
{
var copy = GameObject.Instantiate(prefab, parent);
copy.SetActive(false);
return copy;
}
//从池中取出可用的对象
GameObject AvailableObject()
{
GameObject avaliableObject = null;
if (queue.Count > 0) //&& !queue.Peek().activeSelf
{
avaliableObject = queue.Dequeue();
}
else
{
avaliableObject = Copy();
}
//queue.Enqueue(avaliableObject);
return avaliableObject;
}
//启用可用的对象
public GameObject PreparedObject()
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
return proparedObject;
}
//启用可用的对象 并让其出现在场景中的某个位置
public GameObject PreparedObject(Vector3 position)
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
proparedObject.transform.position = position;
return proparedObject;
}
//启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度
public GameObject PreparedObject(Vector3 position, Quaternion rotation)
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
proparedObject.transform.position = position;
proparedObject.transform.rotation = rotation;
return proparedObject;
}
//启用可用的对象 并让其出现在场景中的某个位置 并使其旋转某个角度 并缩放其大小
public GameObject PreparedObject(Vector3 position, Quaternion rotation, Vector3 localScale)
{
GameObject proparedObject = AvailableObject();
proparedObject.SetActive(true);
proparedObject.transform.position = position;
proparedObject.transform.rotation = rotation;
proparedObject.transform.position = localScale;
return proparedObject;
}
//让已用过的对象返回对象池(可与启用写在同一函数中)
public void Return(GameObject gameObject)
{
queue.Enqueue(gameObject);
}
}
以上是我对实现对象池的理解,写为博客,作为自己的学习记录与参考,欢迎指正。文章来源地址https://www.toymoban.com/news/detail-810483.html
到了这里,关于Unity对象池的简单理解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!