Unity3D仿星露谷物语开发41之创建池管理器
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 startfor(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 nameGameObject 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 queueGameObject 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 activeif(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棵树(这些树会被重复利用)。