Unity中基于自定义资源配置实现对象池管理

        此次的实现效果是,将对象池可以批量管理,只需要提前将对象池的名字、预制体、最大数等配置好,存成资源配置文件,然后存于本地;运行时读取配置文件,将对象池信息存于字典中,当需要对应的实例对象时,只需给出对象的名字就可从对象池中取得。

        做了一个自定义菜单实现点击菜单时,创建资源配置文件,以便手动的批量配置对象池。

1. 对象信息及实例化取得对象

1.1 对象信息及方法(GameObjectPool.cs)

        [Serializable],序列化;可将对象的属性显示于Inspector面板,以便手动配置。
        [SerializeField]序列化字段,可将私有属性仍显示于Inspector面板。
        虽然将批量对象放于字典,但访问时foreach不能一边访问一边修改,所以循环遍历字典时用for循环;

        用两个字典存放对象,一个为正在使用的对象字典,一个为空闲字典,当需要实例化时,先从空闲字典中取得;如果所有放于一个字典,当需要对象时则遍历整个字典,两个字典可以节约性能。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

/// <summary>
/// 资源池
/// 此资源池将实例化出的对象放于根目录,优化时可将同一类对象放于一个父对象下
/// </summary>
[Serializable]
public class GameObjectPool{
    [SerializeField]//虽为私有成员,但加上[SerializeField]Inspector面板显示
    public string name;
    [SerializeField]
    private GameObject prefab;
    [SerializeField]
    private int maxAmount;

    [NonSerialized]
    public List<GameObject> goActiveIsFalseList = new List<GameObject>();//空闲的实例(未被使用)
    [NonSerialized]
    public List<GameObject> goActiveIsTrueList = new List<GameObject>();//正在被使用的实例

    public GameObject GetIns()
    {

        //当资源被使用完,active为false时就将对象放入未使用的字典中
        //因为GetIns()非每帧执行,二是触发一次执行一次,所以在触发最开始先将未使用的实例
        //放入goActiveIsFalseList,以便在需要实例是保证是最先从goActiveIsFalseList中取得的
        //foreach (GameObject go in goActiveIsTrueList)//foreach不能一边修改一边遍历
        for (int i = 0; i < goActiveIsTrueList.Count; i++)
        {
            if (goActiveIsTrueList[i].activeInHierarchy == false)
            {
                GameObject tempgo = goActiveIsTrueList[i];
                goActiveIsTrueList.Remove(goActiveIsTrueList[i]);
                goActiveIsFalseList.Add(tempgo);
            }
        }

        for (int i=0;i< goActiveIsFalseList.Count;i++)
        {//先遍历未使用的资源池,如果池子中有未使用的实例
            if(goActiveIsFalseList[i].activeInHierarchy==false)
            {
                goActiveIsFalseList[i].SetActive(true);

                GameObject tempgo = goActiveIsFalseList[i];

                goActiveIsFalseList.Remove(goActiveIsFalseList[i]);
                goActiveIsTrueList.Add(tempgo);//加入到正在使用的字典中

                return tempgo;
            }
        }

        if (goActiveIsTrueList.Count>=maxAmount)//正在使用的池子满了,就删除第一个
        {
            GameObject.Destroy(goActiveIsTrueList[0]);
            goActiveIsTrueList.RemoveAt(0);
        }
        
        GameObject temp = GameObject.Instantiate(prefab);
        goActiveIsTrueList.Add(temp);
        return temp;
    }
}

1.2 对象池字典(GameObjectPoolList.cs)

        继承自ScriptableObject表示把类GameObjectPoolList变成可以自定义资源配置的文件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;


public class GameObjectPoolList:ScriptableObject
{//继承自ScriptableObject表示把类GameObjectPoolList变成可以自定义资源配置的文件

    public List<GameObjectPool> poolList;
}

2. 定义菜单

        创建一个菜单,当点击时,生成对象池配置文件。

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class PoolManagerEditor : EditorWindow
{
    [MenuItem("PoolTool/Create GameObjectPoolConfig")]
    static void CreateGameObjectPoolList()
    {
        GameObjectPoolList poolList = ScriptableObject.CreateInstance<GameObjectPoolList>();
        //string path = "Assets/Framwork/Resources/gameobjectpool.asset";
        string path = PoolManager.PoolConfigPath;
        AssetDatabase.CreateAsset(poolList, path);
        AssetDatabase.SaveAssets();
    }
}
 
 

3. 读取配置(管理)及实例化对象(PoolManager.cs)

        GetInstance()方法只需给出对象名字就可得到对象。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 运用单利模式
/// </summary>
public class PoolManager{
    private static PoolManager _instance;

    public static PoolManager Instance
    {
        get
        {
            if(_instance==null)
            {
                _instance = new PoolManager();
            }
            return _instance;
        }
    }

    ///将资源路径分为三部分,方便后续的更改
    private static string poolConfigPathPrefix = "Assets/Framwork/Resources/";
    private static string poolConfigPathMiddle = "gameobjectpool";
    private static string poolConfigPathPosfix = ".asset";

    public static string PoolConfigPath
    {
        get
        {
            return poolConfigPathPrefix + poolConfigPathMiddle + poolConfigPathPosfix;
        }
    }

    Dictionary<string, GameObjectPool> poolDict;//将配置的资源文件信息存入字典,方便使用时根据资源池名字取得实例

    private PoolManager()
    {
        GameObjectPoolList poolList = Resources.Load<GameObjectPoolList>(poolConfigPathMiddle);//得到配置的资源文件

        poolDict = new Dictionary<string, GameObjectPool>();

        foreach(GameObjectPool pool in poolList.poolList)
        {
            poolDict.Add(pool.name, pool);
        }
    }

    /// <summary>
    /// 此方法不做任何事,只是为了有个调用的方法,调用此方法时就会创建PoolManager的单例对象
    /// </summary>
    public void Init()
    {
        //DoNothing
    }

    /// <summary>
    /// 实例化时,根据需要实例化的名字从资源池获取
    /// </summary>
    /// <param name="poolName"></param>
    /// <returns></returns>
    public GameObject GetInstance(string poolName)
    {
        GameObjectPool pool;
        if(poolDict.TryGetValue(poolName,out pool))
        {
            return pool.GetIns();
        }
        //else if
        Debug.LogWarning("Pool: " + poolName + "is not exits!!");
        return null;
    }
}

4. 对象的“销毁”

        为做测试,在预制体上挂一脚本用于将此预制体实例化后能隐藏。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 相当于做销毁,挂于Bullet上做测试
/// </summary>
public class DeactiveForTime : MonoBehaviour {

     void OnEnable()
    {
        Invoke("Deactive", 3);//3秒后执行Deactive函数
    }

    void Deactive()
    {
        this.gameObject.SetActive(false);
    }
}

5. 运行

        脚本GameScriptsOnScene.cs挂于场景中一物体上,用于触发。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 继承MonoBehavior,挂于场景中,触发执行
/// </summary>
public class GameScriptsOnScene : MonoBehaviour {

    private void Awake()
    {
        PoolManager.Instance.Init();//实例化出单利对象,Init()为空函数,调用Init只为实例化出PoolManager的单利对象
    }

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            PoolManager.Instance.GetInstance("Bullet");
        }
        if (Input.GetMouseButtonDown(1))
        {
            PoolManager.Instance.GetInstance("HitEfflect");
        }
    }
}


猜你喜欢

转载自blog.csdn.net/a962035/article/details/80649899