1、目标
在PersistentScene中创建池管理器(Pool Manager)。这将允许一个预制对象池被创建和重用。
在游戏中当鼠标点击地面时,便会启用某一个对象。比如点击地面,就创建了一棵树,而这棵树是从预制体对象池中获取的(并且最大数量有限)。
2、池管理器
(1)概念
对象池通常用于频繁或快速生成的游戏对象,如游戏中的子弹。
什么是池管理器,以及它们为什么有用?
- 对象池是指在场景中有一组“池化”对象,在需要时可以启用它们,不需要时可以禁用它们。
- 这避免了创建新对象和销毁不再需要的对象
- 反复创建新对象和销毁旧对象会消耗大量资源,并引发问题,例如增加“垃圾”回收量。当系统试图清理反复分配的未使用内存时,这可能会在游戏运行中导致掉帧。
- 通过使用“池”中的对象,并在不再需要时将其返回池中,可以减少这些性能问题。
(2)创建流程
(3)使用池对象
3、创建PoolManager.cs脚本
在Assets -> Scripts 下新建目录命名为VFX,在此目录下创建新脚本命名为PoolManager。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : SingletonMonobehaviour<PoolManager>
{
private Dictionary<int, Queue<GameObject>> poolDictionary = new Dictionary<int, Queue<GameObject>>();
[SerializeField] private Pool[] pool = null; // 池数组,每个值为预制体和个数。
[SerializeField] private Transform objectPoolTransform = null; // 用于存储对象池的父 Transform
[System.Serializable]
public struct Pool
{
public int poolSize;
public GameObject prefab;
}
private void Start()
{
// Create object pools on start
for(int i = 0; i < pool.Length; i++)
{
CreatePool(pool[i].prefab, pool[i].poolSize);
}
}
private void CreatePool(GameObject prefab, int poolSize)
{
int poolKey = prefab.GetInstanceID();
string prefabName = prefab.name; // get prefab name
GameObject parentGameObject = new GameObject(prefabName + "Anchor"); // 创建一个新的游戏对象作为该对象池所有游戏对象的父对象,并将其设置为objectPoolTransform的子对象。
parentGameObject.transform.SetParent(objectPoolTransform);
if (!poolDictionary.ContainsKey(poolKey))
{
poolDictionary.Add(poolKey, new Queue<GameObject>());
for(int i = 0; i < poolSize; i++)
{
GameObject newObject = Instantiate(prefab, parentGameObject.transform) as GameObject;
newObject.SetActive(false);
poolDictionary[poolKey].Enqueue( newObject );
}
}
}
public GameObject ReuseObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
int poolKey = prefab.GetInstanceID() ;
if (poolDictionary.ContainsKey(poolKey))
{
// Get object from pool queue
GameObject objectToReuse = GetObjectFromPool(poolKey);
ResetObject(position, rotation, objectToReuse, prefab);
return objectToReuse;
}
else
{
Debug.Log("No object pool for " + prefab);
return null;
}
}
private GameObject GetObjectFromPool(int poolKey)
{
GameObject objectToReuse = poolDictionary[poolKey].Dequeue();
poolDictionary[poolKey].Enqueue(objectToReuse);
// log to console if object is currently active
if(objectToReuse.activeSelf == true) // objectToReuse.activeSelf 是 GameObject 类的一个属性,用于检查该对象当前是否处于激活状态。
{
objectToReuse.SetActive(false); // 确保返回的对象处于非激活状态,以便后续可以根据需要重新激活使用。
}
return objectToReuse;
}
private static void ResetObject(Vector3 position, Quaternion rotation, GameObject objectToReuse, GameObject prefab)
{
objectToReuse.transform.position = position;
objectToReuse.transform.rotation = rotation;
objectToReuse.transform.localScale = prefab.transform.localScale;
}
}
4、创建池对象
在Hierarchy -> PersistentScene下创建空物体命名为PoolManager。
给PoolManager对象添加PoolManager组件,设置Pool为1,同时将Transform组件拖到Object Pool Transform中。
然后,在Assets -> Prefabs -> Scenary中,复制CanyonOakScenary预制体并且命名为TestObjectPoolCanyonOak。在Element0中,设置Pool Size为20,再把TestObjectPoolCanyonOak拖入到Prefab选项中。
点击TestObjectPoolCanyonOak预制体,删除它和Trunk的Obscuringxxx组件。
5、修改Player.cs脚本
实现测试功能:当鼠标右击时,启用一个Pool中的预制体。
在最开始的变量定义中添加代码:
public GameObject canyonOakTreePrefab;
在PlayerTestInput函数中添加如下功能:
// Test object pool
if (Input.GetMouseButtonDown(1))
{
GameObject tree = PoolManager.Instance.ReuseObject(canyonOakTreePrefab, mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,
Input.mousePosition.y, -mainCamera.transform.position.z)), Quaternion.identity);
tree.SetActive(true);
}
在Unity中,配置Player脚本的canyonOakTreePrefab信息。
注解:此时配置canyonOakTreePrefab信息,主要是通过int poolKey = prefab.GetInstanceID() ;获取poolKey信息,然后从Pool池中获取对象。
6、运行游戏
每次右击鼠标时,都会产生一棵树,同时最多只有20棵树(这些树会被重复利用)。