您现在的位置是:首页 >技术教程 >UnityVR--Managers--对象池2网站首页技术教程

UnityVR--Managers--对象池2

唐小墨同学 2024-06-17 11:19:19
简介UnityVR--Managers--对象池2

目录

前言

对象池及管理器的基本结构

对象池代码  Chunk.cs

对象池管理器代码 ChunkAllocator.cs

使用

总结


前言

  经过上一篇对象池1的了解,已经做到了使用Unity自带的ObjectPool进行内存优化。本篇自己构建一个对象池管理器(Manager),实现对象池的创建、删除、加载资源等功能。这不是多此一举,因为在定义这个对象池管理器过程中,可以了解一个管理器Manager类的脚本,大致应该是如何的结构,之后还会写到很多的管理器Manager。

对象池及管理器的基本结构

    建立一个对象池的管理器(分配器),因为在场景中有可能同时存在多个对象池,需要一个管理器来管理从池中取出对象、回收对象等方法。也就是说,对象池管理器是一个工具集,包含了对象池操作的一般方法。

    对象池管理设计的总体结构如下图,有一个总的管理器(ChunkAllocator),它是一个管理器(Manager),因此是单例模式(即场上只有唯一一个实例);在ChunkAllocator建立一个字典(Dictionary<kew,value>)用来存储所有的对象池(Chunk),每个对象池(Chunk)都有一个列表List<T>,存储所有的对象。

 

     在管理器(ChunkAllocator)和对象池(Chunk)中,建立了以下场景中使用的工具方法:

  

对象池代码  Chunk.cs

   针对单个对象池的管理(想象上一篇中的Trophy对象池),需要实现的功能:1. 建立对象列表;2. 取一个对象;3. 消除一个对象;  

public class Chunk
{
    List<Object> objectList; //建立一个列表,作为对象池的容器
	public bool IsHave => objectList.Count > 0; //判断列表中有没有对象
    //当IsHave为True时才能使用GetObj()调取对象
  
	public Chunk()
	{//构造中先初始化容器(列表)
        objectList = new List<Object>();
	}

	public Object GetObj()
	{//从容器(列表)中取出对象
		Object obj = objectList[objectList.Count - 1]; //取出第一个对象
		objectList.RemoveAt(objectList.Count - 1);  //容器(列表)中移除这个被取出的对象
		return obj;
	}

	public void RevertObj(Object obj)
	{//回收对象到容器(列表)
		(obj as GameObject).transform.parent= null;  //将对象父节点设为空
        (obj as GameObject).SetActive(false);  //将对象失活
		objectList.Add(obj);  //最后添加回容器(列表)中
    }
}

对象池管理器代码 ChunkAllocator.cs

  管理器Manager代码继承单例(所有的管理类都是single)。对象池管理器实现的功能:1. 建立对象池字典;2. 判断对象池是否已经存在;3. 从对象池中获取一个对象并加载到场景内; 4. 通过资源管理器向对象池加入一个对象,并将它加载到场景内;5. 清空对象池

  *注:1. 这里使用的Resload(从Asset文件夹中加载资源)、Single(单例)等类,后面单开篇幅说明,这些是一个项目中的基本类;

  2. 方法还不够全面,仅测试参考。

//对象池分配器,单例
public class ChunkAllocator : Single<ChunkAllocator> 
{
    Dictionary<string, Chunk> chunkList; //定义一个字典存储所有的对象池

    public ChunkAllocator()
    {//构造中先实例化字典
        chunkList = new Dictionary<string, Chunk>();
    }

    private bool IsHavePool(string poolName)
    {//用对象池的名字查询是否已存在
        return chunkList.ContainsKey(poolName);
    }

    //获取对象
    public Object GetObject(string poolName)
    {//判断池子是否存在
        if (!IsHavePool(poolName))
        {
            return new Object();//不存在就直接实例化一个Object
        }
        return chunkList[poolName].GetObj(); //存在则通过名字找到它,并拿取对象
    }

    //回收对象到池中
    public void Revert(string poolName,Object obj)
    {
        if(IsHavePool(poolName))  //先判断对象池是否存在
        {
            chunkList[poolName].RevertObj(obj); //如果存在就直接回收进池
        }
        else
        {
            Chunk chunk= new Chunk(); //没有对应的对象池则先新建一个
            chunk.RevertObj(obj);     //再用新对象池回收该对象
            chunkList.Add(poolName, chunk);  //并把这个新对象池加入到字典中
        }
    }

    //直接从对象池中获取对象
    public GameObject GetGameObject(string poolName,GameObject gameObj,out bool isFirst,Transform parent=null)
    {//(对象池名称,对象,是否第一次创建,父节点)
        GameObject go = null;
        if(!IsHavePool(poolName))  //先判断对象池是否存在
        {//对象池不存在
            go = GameObject.Instantiate(gameObj); //实例化一个物体
            isFirst=true;  //将“第一次创建”的标记设为True
        }
        else
        {//对象池已存在
            isFirst = !chunkList[poolName].IsHave;  //池内有物体,就把“第一次创建”设false
            if (chunkList[poolName].IsHave)
            {
                go = chunkList[poolName].GetObj() as GameObject;//池内有物体就获取它
            }
            else
            {
                go = GameObject.Instantiate(gameObj); //池内没有物体就实例化一个
            }
        }
        if(parent != null)
        {//如果有父节点的要求,就将物体设置到父节点下
            go.transform.SetParent(parent);
        }
        go.transform.localScale = Vector3.one;  //设置物体大小
        go.transform.position = Vector3.zero;   //位置
        go.name= poolName;                      //名字
        go.SetActive(true);   //激活物体

        return go;  //返回物体
    }

    //从资源管理器中获取资源对象
    public T GetObj<T>(string poolName,string resName) where T : Object
    {
        if (!IsHavePool(poolName))
        {//如果对象池不存在,就用Resload加载Asset文件夹中的对象
            return Resload.Instance.LoadAsset<T>(resName);
        }
        return chunkList[poolName].GetObj() as T; //如果对象池存在,则直接调取对象
    }

    //从资源管理器中获取预制体
    public GameObject GetPrefab(string poolName,string resName,Transform parent=null)
    {
        GameObject go = null;
        if (!IsHavePool(poolName))
        {//如果对象池不存在,就用Resload加载
            go = Resload.Instance.LoadPrefab(resName);
        }
        else
        {//如果有对象池,先判断对象池中是否有东西
            if (chunkList[poolName].IsHave)
                go = chunkList[poolName].GetObj() as GameObject;//如果有,就从池中获取
            else
                go = Resload.Instance.LoadPrefab(resName);//否则就通过路径加载
        }
        if(parent!=null)
        {//设置父节点
            go.transform.SetParent(parent);
        }
        go.transform.localScale = Vector3.one;
        go.transform.position = Vector3.zero;
        go.SetActive(true);
        go.name = poolName;
        return go;
    }

    //清空对象池
    public void ClearPool(string poolName="")
    {
        if(poolName=="")
        {
            chunkList.Clear();
            return;
        }
        if(IsHavePool(poolName))
        {
            chunkList.Remove(poolName);  //如果对象池已存在,就清除
        }
    }
}

使用

    我们还是使用在上一篇的奖品(Trophy)中,场景结构不变,仅修改TrophyManager.cs

using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Pool;

public class TrophyManager : MonoBehaviour
{
    public GameObject[] trophies;
    public int number = 50;
    public bool useObjectPool;
    public bool useChunkAllocator;
    private ObjectPool<GameObject> trophyPool;

    void Start()
    {

    }

    void Update()
    {
        for (int i = 0; i < number; i++)
        {
            bool isfirst = false;
            GameObject trophy = ChunkAllocator.Instance.GetGameObject("trophyPool", trophies[Random.Range(0, trophies.Length)], out isfirst, transform);
            trophy.transform.localPosition = Random.insideUnitSphere;
            trophy.AddComponent<Trophy>();
            if (isfirst)
            {
                trophy.GetComponent<Trophy>().destroyEvent.AddListener(() =>
                {
                    ChunkAllocator.Instance.Revert("trophyPool", trophy);
                });
            }
        }

总结

  可以看出,管理器Manager是一个方法的集合,也就是把常用的一些方法放到一个类中。而且这个管理器的实现方式也有很多种,并根据不同的项目需要加入不同的工具方法,但是这个思路和结构是之后建立的项目通用的。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。