此次的實作效果是,将對象池可以批量管理,隻需要提前将對象池的名字、預制體、最大數等配置好,存成資源配置檔案,然後存于本地;運作時讀取配置檔案,将對象池資訊存于字典中,當需要對應的執行個體對象時,隻需給出對象的名字就可從對象池中取得。
做了一個自定義菜單實作點選菜單時,建立資源配置檔案,以便手動的批量配置對象池。
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)
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");
}
}
}