对象池,简单的来说,就是当你想GameObject消失时,而这个GameObject又需要经常用到时,你就不必去Destroy掉,而是隐藏掉,为了便于管理,就把这些GameObject统一放在一个“池”中。许多时候,实例化一个物体需要消耗一定的性能,不直接Destroy掉就会提高性能。
下面实现的对象池,是针对Resources.Load来设计的。
1.根据Resources下的预制物生成配置文件
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.IO;
//生成配置信息,供对象池使用
public class PoolConfigure {
private static List allFiles;
//右键菜单
[MenuItem("Assets/Build Pool Configure")]
static void Build()
{
string path = AssetDatabase.GetAssetPath(Selection.objects[0]);
if (!path.Contains("Resources"))
{
Debug.Log("请选中Resources文件夹");
return;
}
path = Application.dataPath + path;
path = path.Replace("AssetsAssets", "Assets");
Debug.Log(path);
allFiles = new List();
GetAllFiles(path);
allFiles.RemoveAll((s) => { return s.Contains("meta");});
//for (int i = 0; i < allFiles.Count; i++)
//{
// Debug.Log(allFiles[i]);
//}
//对路径进行处理,使其可以直接通过Resources.Load进行加载
for (int i = 0; i < allFiles.Count; i++)
{
allFiles[i] = allFiles[i].Replace("\\", "/");
allFiles[i] = allFiles[i].Substring(allFiles[i].IndexOf("Resources/") + 10);
allFiles[i] = allFiles[i].Replace(".prefab", "");
Debug.Log(allFiles[i]);
}
//生成配置文件
string outputPath = Application.dataPath + "/Script/ObjectPool/PoolInfo.cs";
ClassTemplate.Start(outputPath);
ClassTemplate.WriteClass("");
for (int i = 0; i < allFiles.Count; i++)
{
string name = allFiles[i].Substring(allFiles[i].LastIndexOf('/') + 1);
ClassTemplate.WriteField("string", name, "public const", "\"" + allFiles[i] + "\"");
}
ClassTemplate.End();
AssetDatabase.Refresh();
}
//获取一个根目录下的所有文件
public static void GetAllFiles(string dir)
{
string[] files = Directory.GetFiles(dir);
foreach (var item in files)
{
allFiles.Add(item);
}
string[] dirs = Directory.GetDirectories(dir);
foreach (var item in dirs)
{
GetAllFiles(item);
}
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
//创建类模板(文件)
//统一在语句末加上"\n"
public class ClassTemplate {
private static StringBuilder builder;
private static string path;
private static string className;
private static bool useNameSpace = false;
private static string fourSpace = " ";
public static void Start(string savaPath)
{
builder = new StringBuilder();
path = savaPath;
className = savaPath.Substring(savaPath.LastIndexOf("/") + 1);
className = className.Substring(0, className.IndexOf('.'));
useNameSpace = false;
//Debug.Log("className:" + className);
}
public static void End()
{
if (useNameSpace) Write(fourSpace + "}\n}");
else Write("\n}");
File.WriteAllText(path, builder.ToString());
}
public static void WriteUsing(string s)
{
Write("using " + s + ";" + "\n");
}
public static void WriteNameSpace(string s)
{
useNameSpace = true;
Write("\nnamespace " + s + "\n{\n");
}
public static void WriteClass(string parentName)
{
if (!useNameSpace) Write("\n");
WriteZeroOrOneInterval();
Write("public class " + className);
if (!string.IsNullOrEmpty(parentName)) Write(" : " + parentName + " ");
else Write(" ");
WriteZeroOrOneInterval();
Write("{\n\n");
}
//prefix即变量修饰符,如public static
public static void WriteField(string type, string name, string prefix = "public", string value = "")
{
WriteOneOrTwoInterval();
if (string.IsNullOrEmpty(value)) Write(prefix + " " + type + " " + name + ";\n");
else Write(prefix + " " + type + " " + name + " = " + value + ";\n");
}
///
/// methodInfo:如public static void Init()
///
///
public static void StartMethod(string methodInfo)
{
Write("\n");
WriteOneOrTwoInterval();
Write(methodInfo);
Write("\n");
WriteOneOrTwoInterval();
Write("{\n");
}
public static void MethodProcess(string s)
{
WriteTwoOrThreeInterval();
Write(s + "\n");
}
public static void EndMethod()
{
WriteOneOrTwoInterval();
Write("}\n");
}
public static void WriteConstructionRead(List name, List readMethod)
{
Write("\n");
WriteOneOrTwoInterval();
Write("public " + className + "()\n");
WriteOneOrTwoInterval();
Write("{\n");
for (int i = 0; i < name.Count; i++)
{
WriteTwoOrThreeInterval();
Write(name[i] + " = " + readMethod[i] + "();\n");
}
WriteOneOrTwoInterval();
Write("}\n\n");
}
public static void WriteConstructionCopy(List name, List readMethod)
{
Write("\n");
WriteOneOrTwoInterval();
Write("public " + className + "(" + className + " a" + ")\n");
WriteOneOrTwoInterval();
Write("{\n");
for (int i = 0; i < name.Count; i++)
{
if (readMethod[i].Contains("List"))
{
string type = readMethod[i].Substring(4, readMethod[i].Length - 8).ToLower();
//Debug.Log(type);
WriteTwoOrThreeInterval();
Write(name[i] + " = new " + type + "[a." + name[i] + ".Length];\n");
WriteTwoOrThreeInterval();
Write("Array.Copy(a." + name[i] + ", " + name[i] + ", " + "a." + name[i] + ".Length);\n");
}
else if (readMethod[i].Contains("Dict"))
{
string type = readMethod[i].Substring(4, readMethod[i].Length - 8).ToLower();
//Debug.Log(type);
WriteTwoOrThreeInterval();
Write(name[i] + " = " + "new Dictionary(a." + name[i] + ");\n");
}
else
{
WriteTwoOrThreeInterval();
Write(name[i] + " = " + "a." + name[i] + ";\n");
}
}
WriteOneOrTwoInterval();
Write("}\n\n");
}
public static string GetClassName()
{
return className;
}
public static void Write(string s)
{
builder.Append(s);
}
private static void WriteZeroOrOneInterval()
{
if (useNameSpace) Write(fourSpace);
}
private static void WriteOneOrTwoInterval()
{
if (useNameSpace) Write(fourSpace + fourSpace);
else Write(fourSpace);
}
private static void WriteTwoOrThreeInterval()
{
if (useNameSpace) Write(fourSpace + fourSpace + fourSpace);
else Write(fourSpace + fourSpace);
}
}
生成的配置文件如下:
public class PoolInfo {
public const string Cube = "Character/Enemy/Cube";
public const string Sphere = "Character/Hero/Sphere";
public const string Capsule = "Effect/Capsule";
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PoolManager : CSharpSingletion {
//path -- go
public Dictionary> dic = new Dictionary>();
//把对象放到对象池中
public void SetPoolObject(string path, GameObject go)
{
if (!dic.ContainsKey(path))
{
List list = new List();
list.Add(go);
dic.Add(path, list);
}
else
{
dic[path].Add(go);
}
go.SetActive(false);
}
//从对象池中取出对象
//isNewGo为true,表示返回新的go,否则表示返回缓存池中的go
public GameObject GetPoolObject(string path, bool isNewGo = false)
{
//Resources.Load: 重复Load同样的资源只会指向同一段内存,不会增加开销
if (isNewGo)
{
GameObject go = MonoBehaviour.Instantiate(Resources.Load(path));
return go;
}
if (!dic.ContainsKey(path))
{
GameObject go = MonoBehaviour.Instantiate(Resources.Load(path));
return go;
}
else
{
if (dic[path].Count > 0)//还未取完
{
GameObject go = dic[path][0];
go.SetActive(true);
dic[path].Remove(go);
return go;
}
else//已经取完
{
GameObject go = MonoBehaviour.Instantiate(Resources.Load(path));
return go;
}
}
}
public void Cache(string path, int amount = 1, bool isNewGo = true)
{
for (int i = 0; i < amount; i++)
{
GameObject go = GetPoolObject(path, isNewGo);
SetPoolObject(path, go);
}
}
}
using UnityEngine;
using System.Collections;
public class PoolTest : MonoBehaviour {
void Start ()
{
//1
//PoolManager.Instance.Cache(PoolInfo.Cube, 5000, true);
//Profiler.BeginSample("PoolTest");
//for (int i = 0; i < 5000; i++)
//{
// PoolManager.Instance.GetPoolObject(PoolInfo.Cube);
//}
//Profiler.EndSample();
//2
Profiler.BeginSample("PoolTest");
for (int i = 0; i < 5000; i++)
{
Instantiate(Resources.Load(PoolInfo.Cube));
}
Profiler.EndSample();
}
}