对象池是游戏开发中最重要的概念之一,可能会对游戏的性能产生巨大影响。
在本教程中,您将学习,
- 为什么“ Rockstar Games推出乒乓球”是有史以来最好的游戏之一
- El Dilema del Publisher
- 开发日志-24.02.17
- 将游戏世界融入现实生活:印度人创造意识和替代收入的构想
- CS:GO玩家的重要信息
- 对象池的概念。
- 如何使用Unity创建对象池。
- 使用对象后,如何从对象池中检索对象并返回它们。
为了遵循本教程,您需要在计算机上安装Unity。 你可以在这里下载。
对象池是一种编程模式,其中我们重用最初创建的对象池中的对象,而不是在玩游戏时实例化和销毁游戏对象。
你玩过太空射击游戏吗? 在典型的太空射击游戏中,您可以在很短的时间内发射数百发子弹。
Unity API提供了两种可用于实例化和销毁游戏对象的方法(Instantiate()和Destroy())。 您可以轻松地将这些方法用于上述太空射击游戏。 您可以使用Instantiate()方法创建新项目符号,并稍后使用Destroy()销毁它们。
但是,由于多次发生内存分配和释放,频繁创建和销毁对象可能会导致游戏延迟。 我们可以通过使用对象池来解决这个问题。
我们在玩游戏之前创建一个对象池。 我们在需要时从池中检索游戏对象,而不是在游戏过程中创建它们。 当我们完成游戏对象的使用后,我们会将它们返回到池中。
- 打开Unity并创建一个名为SimpleObjectPool的新项目。

2.创建一个空的游戏对象,并将其命名为PoolManager 。
3.创建一个名为PoolManager的C#脚本,并将其添加到PoolManager游戏对象中。
4.双击在MonoDevelop中打开脚本。 你应该有这样的东西。
使用System.Collections;
使用System.Collections.Generic;
使用UnityEngine;
公共类PoolManager:MonoBehaviour {
//使用它进行初始化
无效开始(){
}
//每帧调用一次更新
无效更新(){
}
}
5.我们的PoolManager类需要支持几种类型的游戏对象。 基本上,每种类型都需要有一个池。 我们将为每种类型的游戏对象创建一个列表,并将这些列表存储在字典中。 让我们从创建字典开始。
使用System.Collections;
使用System.Collections.Generic;
使用UnityEngine;
公共类PoolManager:MonoBehaviour
{
Dictionary <int,List > poolDictionary =新Dictionary <int,List >();
}
6.我们将从外部调用此类中的方法。 因此,让我们创建一个属性,该属性为此返回PoolManager类的实例。
使用System.Collections;
使用System.Collections.Generic;
使用UnityEngine;
公共类PoolManager:MonoBehaviour
{
Dictionary <int,List > poolDictionary =新Dictionary <int,List >();
静态PoolManager _instance;
公共静态PoolManager实例
{
得到
{
如果(_instance == null)
{
_instance = FindObjectOfType ();
}
返回_instance;
}
}
}
7.将以下CreatePool()方法添加到PoolManager类。
公共无效CreatePool(GameObject预制,int poolSize)
{
int poolKey = prefab.GetInstanceID();
如果(!poolDictionary.ContainsKey(poolKey))
{
poolDictionary.Add(poolKey,新的List ());
为(int i = 0; i <poolSize; i ++)
{
GameObject newObject =实例化(预制);
poolDictionary [poolKey] .Add(newObject);
}
}
}
CreatePool方法接受两个参数,一个游戏对象和池的大小。 此方法从传入的游戏对象创建对象列表,并将该列表存储在我们之前创建的字典(poolDictionary)中。 poolKey是传入的游戏对象的ID。我们使用该ID作为字典中每个值(列表)的键。
8.将以下GetObject()方法添加到PoolManager类中。
公共GameObject GetObject(GameObject预制)
{
int poolKey = prefab.GetInstanceID();
如果(poolDictionary.ContainsKey(poolKey))
{
List pool = poolDictionary [poolKey];
为(int i = 0; i <pool.Count; i ++)
{
如果(!pool [i] .gameObject.activeInHierarchy)
{
返回池[i];
}
}
GameObject newObject = Instantiate(预制);
poolDictionary [poolKey] .Add(newObject);
返回newObject;
}
返回null;
}
此方法接受游戏对象(预制)。 然后,通过将poolKey与字典中的键进行比较,使用其实例 ID检查特定游戏对象是否存在一个池。
如果该游戏对象有一个池(列表),我们将遍历该池并检查是否有未在游戏过程中使用的非活动游戏对象。 如果存在非活动的游戏对象,我们将其返回以用于游戏。 如果列表中没有不活动的项目,则意味着池中的所有游戏对象都在使用中。 在这种情况下,我们创建一个新的游戏对象并返回它。
如果没有传入的游戏对象的对象池,我们只需返回null 。
9.您可以在下面看到完整的脚本。
使用System.Collections;
使用System.Collections.Generic;
使用UnityEngine;
公共类PoolManager:MonoBehaviour
{
Dictionary <int,List > poolDictionary =新Dictionary <int,List >();
静态PoolManager _instance;
公共静态PoolManager实例
{
得到
{
如果(_instance == null)
{
_instance = FindObjectOfType ();
}
返回_instance;
}
}
公共无效CreatePool(GameObject预制,int poolSize)
{
int poolKey = prefab.GetInstanceID();
如果(!poolDictionary.ContainsKey(poolKey))
{
poolDictionary.Add(poolKey,新的List ());
为(int i = 0; i <poolSize; i ++)
{
GameObject newObject =实例化(预制);
poolDictionary [poolKey] .Add(newObject);
}
}
}
公共GameObject GetObject(GameObject预制)
{
int poolKey = prefab.GetInstanceID();
如果(poolDictionary.ContainsKey(poolKey))
{
List pool = poolDictionary [poolKey];
为(int i = 0; i <pool.Count; i ++)
{
如果(!pool [i] .gameObject.activeInHierarchy)
{
返回池[i];
}
}
GameObject newObject =实例化(预制);
poolDictionary [poolKey] .Add(newObject);
返回newObject;
}
返回null;
}
}
10.现在,我们完成了PoolManager类。 我们需要一个新的脚本来调用PoolManager中的方法并实例化场景中的游戏对象。 创建一个名为Spawner的C#脚本,并将其添加到场景中的PoolManager游戏对象中。
11.将以下代码添加到Spawner脚本。
使用UnityEngine;
公共类Spawner:MonoBehaviour {
[SerializeField] GameObject cubePrefab;
[SerializeField] int poolSize = 20;
无效Start()
{
PoolManager.instance.CreatePool(cubePrefab,poolSize);
InvokeRepeating(“ SpawnCubes”,1f,2f);
}
无效SpawnCubes()
{
GameObject cubeObject = PoolManager.instance.GetObject(cubePrefab);
cubeObject.transform.position = Vector3.zero;
cubeObject.SetActive(true);
}
}
在Start方法中,我们在PoolManager脚本中调用CreatePool方法,然后将带有 poolSize的cubePrefab传入。 然后,我们调用Unity的内置InvokeRepeating() ,每隔2秒从1秒的延迟开始运行SpawnCubes方法。
在SpawnCubes方法内部,我们在PoolManager脚本中调用GetObject方法,并检索传入的游戏对象的实例。然后,将游戏对象的位置设置为Vectore.zero(类似于new Vector(0,0,0 ))。 然后我们激活它。 请注意,默认情况下,所有游戏对象一开始都是不活动的。 从池中检索它们后,我们需要激活它们。
12.在Unity内部,创建一个多维数据集并将其拖放到项目窗格中,使其成为预制件。 然后停用预制件。

13.将Cube预制件拖放到Spawner脚本中的Cube Prefab字段中。

14.现在,如果您运行游戏,将会看到在场景内部创建了一个多维数据集池。 它们每两秒钟激活一次。
但是,当我们不需要任何游戏对象时,我们仍然不会停用它们。 他们只是不断堆积在现场。 让我们创建一个新脚本来移动多维数据集并在一段时间后将其停用。
15.创建一个名为CubeMovement的C#脚本,并将其添加到Cube预制中。 然后将以下代码添加到CubeMovement脚本中。
使用System.Collections;
使用System.Collections.Generic;
使用UnityEngine;
公共课程CubeMovement:MonoBehaviour {
[SerializeField]浮点速度= 5f;
[SerializeField]浮点数deactivationDelay = 5f;
无效Start()
{
Invoke(“ Deactivate”,deactivationDelay);
}
无效Update()
{
transform.Translate(Vector3.up * Time.deltaTime *速度);
}
无效Deactivate()
{
gameObject.SetActive(false);
}
}
在Update方法内部,我们将多维数据集向上移动。 自启动以来,使用Invoke方法启动5秒钟后,我们在Start方法内部调用Deactivate方法。
现在,您可以尝试再次玩游戏。 您会注意到,一旦在场景中生成立方体,它们就会开始向上移动。 他们在5秒钟后被停用。
我希望您现在对对象池的概念有所了解。
您可以在此处找到本教程的源代码。
不要忘记关注我,以获取更多有关游戏开发的文章。 🙂