|
大家好,本期讲解的是UNITY中对象池的作用和实现方式。
对象池的作用:
对象池在游戏制作中的作用有两个:
- 用于连续的生成和禁用物体,比如生成游戏中的子弹
- 减少频繁生成和销毁物体对内存的消耗,对象池实际上采用的是物体的连续启用和禁用
- 对象池的实现方式
对象池主要包括两个类:对象池类和对象池管理类
对象池类的实现方式:
总体思路:使用队列存储生成的游戏物体
具体变量:游戏预制体、空队列queue、父物体parent、以及对象池的大小size
具体函数实现思路:
初始化函数void Initialize():new一个新队列、将对象池的父物体设为parent、使用for循环将生成的物体加入到队列中
生成物体函数GameObject Copy():定义临时变量存放使用Initiate生成出来的游戏物体、将游戏物体禁用、返回该游戏物体
取用游戏物体的函数GameObject ActivateGO():如果队列长度大于0且队列开头的物体处于被禁用的状态定义一个gameobject并存放queue.Dequeue()以取出物体否则就调用Copy()函数并用gameobject存放这个Copy()函数生成的物体、然后使用Enqueue(gameobject)复列
启用物体函数PreparedObject():调用ActivateGO()并用一个gameobject存起来然后将这个gameobject启用【几个重载函数则是用来改变启用的物体的位置、旋转、缩放】
以下是对象池类的代码
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class Pool
{
public GameObject Prefab => bullet;
public int Size => size;
public int Runtimesize => queue.Count;
[SerializeField] GameObject bullet;
[SerializeField] int size;
Queue<GameObject> queue;
Transform parent;
// 初始化队列
public void Initialize(Transform parent)
{
queue = new Queue<GameObject>();
this.parent = parent;
for(var i=0;i< size;i++)
{
queue.Enqueue(Copy());
}
}
// 生成对象
public GameObject Copy()
{
var copy = GameObject.Instantiate(bullet,parent);
copy.SetActive(false);
return copy;
}
// 取用
public GameObject ActiveGO()
{
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 preparedObject = ActiveGO();
preparedObject.SetActive(true);
return preparedObject;
}
// 重载1-特定位置生成
public GameObject PreparedObject(Vector3 position)
{
GameObject preparedObject = ActiveGO();
preparedObject.SetActive(true);
preparedObject.transform.position = position;
return preparedObject;
}
// 重载2-特定位置特定角度
public GameObject PreparedObject(Vector3 position,Quaternion rotation)
{
GameObject preparedObject = ActiveGO();
preparedObject.SetActive(true);
preparedObject.transform.position = position;
preparedObject.transform.rotation = rotation;
return preparedObject;
}
// 重载3-特定位置特定角度特定大小
public GameObject PreparedObject(Vector3 position,Quaternion rotation,Vector3 scale)
{
GameObject preparedObject = ActiveGO();
preparedObject.SetActive(true);
preparedObject.transform.position = position;
preparedObject.transform.rotation = rotation;
preparedObject.transform.localScale = scale;
return preparedObject;
}
}
对象池管理类的实现方式:
总体思路:利用字典来管理不同的对象池
具体变量:多个对象池数组Pool1、Pool2、Pool3.。。。。。键为GameObject值为Pool类的字典
具体函数实现思路:
生命周期函数:
Awake():new一个新字典、调用Initialize()对对象池数组进行初始化
OnDestroy():调用CheckPoolSize()函数检查对象池容量
自定义函数:
Initialize():foreach循环将数组中的物体添加到对应字典中、新建父物体存放数组中的物体、调用pool的Initialize()函数
CheckPoolSize():foreach循环如果对象池运行时大小大于预设的大小则输出Debug
Release():调用字典[prefab]并调用其Pool类的启用物体PreparedObject()函数
同理Release()有3个重载:分别作用是特定位置生成物体、特定位置生成特定旋转的物体、特定位置生成特定旋转和特定缩放的物体
以下是对象池管理类的代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : MonoBehaviour
{
///<summary>
///PoolManager是管理对象池对象的类,将Hierarchy生成的Clone加以管理(实际运用也是用PoolManager类)
[SerializeField] Pool[] playerbulletPools;
[SerializeField] Pool[] enemybulletPools;
[SerializeField] Pool[] HitVFXPools;
[SerializeField] Pool[] EnemyPools;
[SerializeField] Pool[] LootItemPools;
static Dictionary<GameObject,Pool> dictionary;
void Awake()
{
dictionary = new Dictionary<GameObject, Pool>();
Initialize(playerbulletPools);
Initialize(enemybulletPools);
Initialize(HitVFXPools);
Initialize(EnemyPools);
Initialize(LootItemPools);
}
private void OnDestroy()
{
CheckPoolSize(playerbulletPools);
CheckPoolSize(enemybulletPools);
CheckPoolSize(HitVFXPools);
CheckPoolSize(EnemyPools);
CheckPoolSize(LootItemPools);
}
void Initialize(Pool[] pools){
foreach(var pool in pools)
{
#if UNITY_EDITOR
if (dictionary.ContainsKey(pool.Prefab))
{
Debug.LogError(&#34;Same prefab in multiple pools! Prefab: &#34; + pool.Prefab.name);
continue;
}
#endif
dictionary.Add(pool.Prefab,pool);
// 新建父物体存放生成的物体
Transform poolParent = new GameObject(&#34;Pool&#34;+ pool.Prefab.name).transform;
poolParent.parent = transform;
pool.Initialize(poolParent);
}
}
void CheckPoolSize(Pool[]pools){
foreach(var pool in pools){
if(pool.Runtimesize>pool.Size){
Debug.LogWarning(string.Format(&#34;对象池:{0}.返回了一个尺寸为{1}的池大于定义尺寸{2}&#34;,
pool.Prefab.name,
pool.Runtimesize,
pool.Size));
}
}
}
/// <summary>
///根据传入的<Prafab>参数返回对象池中准备好的游戏对象
/// </summary>
/// 重载-1
public static GameObject Release(GameObject prefab){
#if UNITY_EDITOR
if(!dictionary.ContainsKey(prefab)){
Debug.LogError(&#34;找不到游戏&#34;+prefab.gameObject.name);
return null;
}
#endif
return dictionary[prefab].PreparedObject();
}
/// 重载-2
/// <summary>
/// 根据传入的prefab参数返回position位置的游戏对象
/// </summary>
public static GameObject Release(GameObject prefab,Vector3 position){
#if UNITY_EDITOR
if(!dictionary.ContainsKey(prefab)){
Debug.LogError(&#34;找不到游戏&#34;+prefab.gameObject.name);
return null;
}
#endif
return dictionary[prefab].PreparedObject(position);
}
/// 重载-3
public static GameObject Release(GameObject prefab,Vector3 position,Quaternion rotation){
#if UNITY_EDITOR
if(!dictionary.ContainsKey(prefab)){
Debug.LogError(&#34;找不到游戏对象&#34;+prefab.gameObject.name);
return null;
}
#endif
return dictionary[prefab].PreparedObject(position,rotation);
}
/// 重载-4
/// <summary>
/// 根据传入的prefab参数返回position位置,rotation旋转,scale尺寸的游戏对象
/// </summary>
public static GameObject Release(GameObject prefab,Vector3 position,Quaternion rotation,Vector3 scale){
#if UNITY_EDITOR
if(!dictionary.ContainsKey(prefab)){
Debug.LogError(&#34;找不到游戏&#34;+prefab.gameObject.name);
return null;
}
#endif
return dictionary[prefab].PreparedObject(position,rotation,scale);
}
} |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|