此次的实现效果是,将对象池可以批量管理,只需要提前将对象池的名字、预制体、最大数等配置好,存成资源配置文件,然后存于本地;运行时读取配置文件,将对象池信息存于字典中,当需要对应的实例对象时,只需给出对象的名字就可从对象池中取得。
[SerializeField]序列化字段,可将私有属性仍显示于Inspector面板。
虽然将批量对象放于字典,但访问时foreach不能一边访问一边修改,所以循环遍历字典时用for循环;
做了一个自定义菜单实现点击菜单时,创建资源配置文件,以便手动的批量配置对象池。
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"); } } }