HuldaGnodim 发表于 2023-1-25 14:53

UNITY学习笔记02——对象池的实现

大家好,本期讲解的是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;



public class Pool
{
    public GameObject Prefab => bullet;
    public int Size => size;
    public int Runtimesize => queue.Count;
    GameObject bullet;
    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():调用字典并调用其Pool类的启用物体PreparedObject()函数
同理Release()有3个重载:分别作用是特定位置生成物体、特定位置生成特定旋转的物体、特定位置生成特定旋转和特定缩放的物体
以下是对象池管理类的代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PoolManager : MonoBehaviour
{
    ///<summary>
    ///PoolManager是管理对象池对象的类,将Hierarchy生成的Clone加以管理(实际运用也是用PoolManager类)
    Pool[] playerbulletPools;
    Pool[] enemybulletPools;
    Pool[] HitVFXPools;
    Pool[] EnemyPools;
    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("Same prefab in multiple pools! Prefab: " + pool.Prefab.name);
             continue;
         }
   #endif
      dictionary.Add(pool.Prefab,pool);
      // 新建父物体存放生成的物体
      Transform poolParent = new GameObject("Pool"+ 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("对象池:{0}.返回了一个尺寸为{1}的池大于定义尺寸{2}",
          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("找不到游戏"+prefab.gameObject.name);
      return null;
      }
    #endif
      return dictionary.PreparedObject();
    }
    /// 重载-2
    /// <summary>
    /// 根据传入的prefab参数返回position位置的游戏对象
    /// </summary>
    public static GameObject Release(GameObject prefab,Vector3 position){
    #if UNITY_EDITOR
      if(!dictionary.ContainsKey(prefab)){
      Debug.LogError("找不到游戏"+prefab.gameObject.name);
      return null;
      }
    #endif
      return dictionary.PreparedObject(position);
    }
    /// 重载-3
    public static GameObject Release(GameObject prefab,Vector3 position,Quaternion rotation){
    #if UNITY_EDITOR
      if(!dictionary.ContainsKey(prefab)){
      Debug.LogError("找不到游戏对象"+prefab.gameObject.name);
      return null;
      }
    #endif
      return dictionary.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("找不到游戏"+prefab.gameObject.name);
      return null;
      }
    #endif
      return dictionary.PreparedObject(position,rotation,scale);
    }
}
页: [1]
查看完整版本: UNITY学习笔记02——对象池的实现