找回密码
 立即注册
查看: 248|回复: 0

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

[复制链接]
发表于 2023-1-25 14:53 | 显示全部楼层 |阅读模式
大家好,本期讲解的是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("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[prefab].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[prefab].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[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("找不到游戏"+prefab.gameObject.name);
        return null;
      }
    #endif
      return dictionary[prefab].PreparedObject(position,rotation,scale);
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 06:49 , Processed in 0.090074 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表