Why is this one line causing such bad GC ? ______________________________________________________
NewObjectPool.instance.PoolObject(SpawnedObject);
Rest of the function the line of code in lives in: _____________________________________________________
public virtual void ResetEffect ()
{
if(poolAfterComplete)
{
NewObjectPool.instance.PoolObject(SpawnedObject);
//Debug.Log("Fish is returning back to pool");
}
else
{
return;
Debug.Log("Object was pooled and is returning");
}
}
The line is referencing an public static in our object pool. So I decided to post the pool just in case:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class NewObjectPool : MonoBehaviour {
/// <summary>
/// Repository of commonly used prefabs.
/// </summary>
[AddComponentMenu("Gameplay/ObjectPool")]
public static NewObjectPool instance { get; private set; }
#region member
/// <summary>
/// Member class for a prefab entered into the object pool
/// </summary>
[System.Serializable]
public class ObjectPoolEntry {
/// <summary>
/// the object to pre instantiate
/// </summary>
[SerializeField]
public GameObject Prefab;
/// <summary>
/// quantity of object to pre-instantiate
/// </summary>
[SerializeField]
public int Count;
}
#endregion
/// <summary>
/// The object prefabs which the pool can handle
/// by The amount of objects of each type to buffer.
/// </summary>
public ObjectPoolEntry[] Entries;
/// <summary>
/// The pooled objects currently available.
/// Indexed by the index of the objectPrefabs
/// </summary>
[HideInInspector]
public List<GameObject>[] Pool;
/// <summary>
/// The container object that we will keep unused pooled objects so we dont clog up the editor with objects.
/// </summary>
protected GameObject ContainerObject;
void OnEnable()
{
instance = this;
}
// Use this for initialization
void Start()
{
ContainerObject = new GameObject("ObjectPool");
//Loop through the object prefabs and make a new list for each one.
//We do this because the pool can only support prefabs set to it in the editor,
//so we can assume the lists of pooled objects are in the same order as object prefabs in the array
Pool = new List<GameObject>[Entries.Length];
for (int i = 0; i < Entries.Length; i++)
{
var objectPrefab = Entries[i];
//create the repository
Pool[i] = new List<GameObject>();
//fill it
for (int n = 0; n < objectPrefab.Count; n++)
{
var newObj = Instantiate(objectPrefab.Prefab) as GameObject;
newObj.name = objectPrefab.Prefab.name;
PoolObject(newObj);
}
}
}
/// <summary>
/// Gets a new object for the name type provided. If no object type exists or if onlypooled is true and there is no objects of that type in the pool
/// then null will be returned.
/// </summary>
/// <returns>
/// The object for type.
/// </returns>
/// <param name='objectType'>
/// Object type.
/// </param>
/// <param name='onlyPooled'>
/// If true, it will only return an object if there is one currently pooled.
/// </param>
public GameObject GetObjectForType(string objectType, bool onlyPooled)
{
for (int i = 0; i < Entries.Length; i++)
{
var prefab = Entries[i].Prefab;
if (prefab.name != objectType)
continue;
if (Pool[i].Count > 0)
{
GameObject pooledObject = Pool[i][0];
Pool[i].RemoveAt(0);
pooledObject.transform.parent = null;
pooledObject.SetActive(true);
return pooledObject;
}
if (!onlyPooled)
{
GameObject newObj = Instantiate( Entries[ i ].Prefab ) as GameObject;
newObj.name = Entries[ i ].Prefab.name;
return newObj;
}
}
//If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true
return null;
}
/// <summary>
/// Pools the object specified. Will not be pooled if there is no prefab of that type.
/// </summary>
/// <param name='obj'>
/// Object to be pooled.
/// </param>
public void PoolObject(GameObject obj)
{
for (int i = 0; i < Entries.Length; i++)
{
if (Entries[i].Prefab.name != obj.name)
continue;
obj.SetActive(false);
obj.transform.parent = ContainerObject.transform;
Pool[i].Add(obj);
return;
}
}
}
Heres the profiler data showing the bad Garbage Collection: ____________________________________________________________________
Ok, I did a test to see if it could be the loop generating new strings by creating a new test script and object that doesn’t use an coroutine: In it’s place I used an invoke.
Test script with suspect line of code: __________________________________
using UnityEngine;
using System.Collections;
public class TestDestory : MonoBehaviour {
public GameObject Self;
// Use this for initialization
void Start ()
{
Invoke ("DesotryTheTest", 5f);
}
// Update is called once per frame
void DesotryTheTest ()
{
NewObjectPool.instance.PoolObject(Self);
}
}
However despite the new code, the garbage was still very awful(suggesting it’s not the loop). While testing this with the deep profile on (just as Hidden Monk had suggested), the profiler pointed to “Object.get_name()”. ____________________________________________________________________________________________
Should I replace Get name with tag? would this be more performant? how would I implement get tag instead of name within the current object pool system?
/// <summary>
/// Member class for a prefab entered into the object pool
/// </summary>
[Serializable]
public class ObjectPoolEntry
{
/// <summary>
/// the object to pre instantiate
/// </summary>
[SerializeField]
public GameObject Prefab;
/// <summary>
/// quantity of object to pre-instantiate
/// </summary>
[SerializeField]
public int Count;
private string name;
public string Name // <======================================
{
get
{
if (name == null && Prefab != null)
{
name = Prefab.name;
}
return name;
}
}
}
/// <summary>
/// Pools the object specified. Will not be pooled if there is no prefab of that type.
/// </summary>
/// <param name='obj'>
/// Object to be pooled.
/// </param>
public void PoolObject(GameObject obj)
{
var objName = obj.name; // <======================================
for (int i = 0; i < Entries.Length; i++)
{
if (Entries[i].Name != objName)
{
continue;
}
obj.SetActive(false);
obj.transform.parent = ContainerObject.transform;
Pool[i].Add(obj);
return;
}
}
Hmm… 4 calls to PoolObject() and 68 calls to Object.get_name(). There should have been only 4 calls to Object.get_name(). Have you made all the changes?
I thought I did, but I could have easily missed something:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class NewObjectPool : MonoBehaviour
{
/// <summary>
/// Repository of commonly used prefabs.
/// </summary>
[AddComponentMenu("Gameplay/ObjectPool")]
public static NewObjectPool instance { get; private set; }
#region member
/// <summary>
/// Member class for a prefab entered into the object pool
/// </summary>
[System.Serializable]
public class ObjectPoolEntry
{
/// <summary>
/// the object to pre instantiate
/// </summary>
[SerializeField]
public GameObject Prefab;
/// <summary>
/// quantity of object to pre-instantiate
/// </summary>
[SerializeField]
public int Count;
private string name;
public string Name
{
get
{
if (name == null && Prefab != null)
{
name = Prefab.name;
}
return name;
}
}
}
#endregion
/// <summary>
/// The object prefabs which the pool can handle
/// by The amount of objects of each type to buffer.
/// </summary>
public ObjectPoolEntry[] Entries;
/// <summary>
/// The pooled objects currently available.
/// Indexed by the index of the objectPrefabs
/// </summary>
[HideInInspector]
public List<GameObject>[] Pool;
/// <summary>
/// The container object that we will keep unused pooled objects so we dont clog up the editor with objects.
/// </summary>
protected GameObject ContainerObject;
void OnEnable()
{
instance = this;
}
// Use this for initialization
void Start()
{
ContainerObject = new GameObject("ObjectPool");
//Loop through the object prefabs and make a new list for each one.
//We do this because the pool can only support prefabs set to it in the editor,
//so we can assume the lists of pooled objects are in the same order as object prefabs in the array
Pool = new List<GameObject>[Entries.Length];
for (int i = 0; i < Entries.Length; i++)
{
var objectPrefab = Entries[i];
//create the repository
Pool[i] = new List<GameObject>();
//fill it
for (int n = 0; n < objectPrefab.Count; n++)
{
var newObj = Instantiate(objectPrefab.Prefab) as GameObject;
newObj.name = objectPrefab.Prefab.name;
PoolObject(newObj);
}
}
}
/// <summary>
/// Gets a new object for the name type provided. If no object type exists or if onlypooled is true and there is no objects of that type in the pool
/// then null will be returned.
/// </summary>
/// <returns>
/// The object for type.
/// </returns>
/// <param name='objectType'>
/// Object type.
/// </param>
/// <param name='onlyPooled'>
/// If true, it will only return an object if there is one currently pooled.
/// </param>
public GameObject GetObjectForType(string objectType, bool onlyPooled)
{
for (int i = 0; i < Entries.Length; i++)
{
var prefab = Entries[i].Prefab;
if (prefab.name != objectType)
continue;
if (Pool[i].Count > 0)
{
GameObject pooledObject = Pool[i][0];
Pool[i].RemoveAt(0);
pooledObject.transform.parent = null;
pooledObject.SetActive(true);
return pooledObject;
}
if (!onlyPooled)
{
GameObject newObj = Instantiate( Entries[ i ].Prefab ) as GameObject;
newObj.name = Entries[ i ].Prefab.name;
return newObj;
}
}
//If we have gotten here either there was no object of the specified type or non were left in the pool with onlyPooled set to true
return null;
}
/// <summary>
/// Pools the object specified. Will not be pooled if there is no prefab of that type.
/// </summary>
/// <param name='obj'>
/// Object to be pooled.
/// </param>
public void PoolObject(GameObject obj)
{
var objName = obj.name;
for (int i = 0; i < Entries.Length; i++)
{
if (Entries[i].Prefab.name != objName)
{
continue;
}
obj.SetActive(false);
obj.transform.parent = ContainerObject.transform;
Pool[i].Add(obj);
return;
}
}
}
Ok fixed line 161, the Garbage went down even more this time and the draw calls for Object.get_name(). One call per object it seems. Thanks allot for your help :).