- Home /
New Object Pooling Problem
As of right now i have a spawning script that is randomly choosing a prefab from an array of prefabs and randomly picking a transform to spawn into from a set array. This works perfectly, however ive been reading into object pooling because for what im doing (mobile) i need all the optimization i can get. So i have an object pooling script thats meant for multiple prefabs, however im still having trouble in the generation script as far as implementing the object pooling for the objects in my arrays. Here are the scripts.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Generation : MonoBehaviour {
// These are the spawn point arrays that are being used as placement for what's being spawned
public Transform[] terrainPoints;
public Transform[] obstaclePoints;
public Transform[] middlePoints;
// These are the actual list of prefabs that will be spawning
public GameObject[] terrainSpawn;
public GameObject[] obstacleSpawn;
public GameObject[] middleSpawn;
// These are the time intervals in which the spawnings will occur
public float terrainTimer = 2;
public float obstacleTimer = 2;
public float middleTimer = 2;
public float newTime = 2;
// This is the bool that determines whether the game will be pause (this is accessed by another script, not set by this one)
public bool isPause;
// This is accessing the level ring of the level, this isn't set in the inspector this is set dynamically
public GameObject theGround;
public static Generation S;
void Awake()
{
S = this;
theGround = GameObject.Find("level ring");
}
// Update is called once per frame
void Update () {
// If isPause is set to false then we proceed to spawn the clones using the custom functions
if(isPause == false)
{
TerrainSpawn();
ObstacleSpawn();
MiddleSpawn();
}
}
// This is the function thats being called thats doing the spawning for terrain prefabs
void TerrainSpawn()
{
// This is taking the duration of time and decreasing it by a set frame
terrainTimer -= Time.deltaTime;
// If this is below or equal to zero, it spawns a new clone
if(terrainTimer <= 0)
{
// This randomly chooses an indexed prefab and transform and spawns something at those points
GameObject terrainClone = Instantiate(terrainSpawn[Random.Range(0, terrainSpawn.Length)], terrainPoints[Random.Range(0, terrainPoints.Length)].transform.position, transform.rotation);
terrainClone.transform.parent = theGround.transform;
// This resets the duration of time for the next spawn
terrainTimer = newTime;
ObjectPoolerScript.instance.GetObjectForType("ice_env1", true);
ObjectPoolerScript.instance.GetObjectForType("ice_env2", true);
}
}
// This is the function called to spawn the obstacle clones
void ObstacleSpawn()
{
obstacleTimer -= Time.deltaTime;
if(obstacleTimer <= 0)
{
GameObject obstacleClone = Instantiate(obstacleSpawn[Random.Range(0, obstacleSpawn.Length)], obstaclePoints[Random.Range(0, obstaclePoints.Length)].transform.position, transform.rotation);
obstacleClone.transform.parent = theGround.transform;
obstacleTimer = newTime;
}
}
// This is the function being called to spawn clones for the middle section prefabs
void MiddleSpawn()
{
middleTimer -= Time.deltaTime;
if(middleTimer <= 0)
{
GameObject middleClone = Instantiate(middleSpawn[Random.Range(0, middleSpawn.Length)], middlePoints[Random.Range(0, middlePoints.Length)].transform.position, transform.rotation);
middleClone.transform.parent = theGround.transform;
middleTimer = newTime;
}
}
}
Thats the generation script that does the spawning.
here is the object pooler
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Repository of commonly used prefabs.
/// </summary>
[AddComponentMenu("Gameplay/ObjectPool")]
public class ObjectPoolerScript : MonoBehaviour
{
public static ObjectPoolerScript instance { get; private set; }
#region member
/// <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;
}
#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.SetActiveRecursively(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.SetActiveRecursively(false);
obj.transform.parent = ContainerObject.transform;
Pool[i].Add(obj);
return;
}
}
}
Could someone please point me into the right direction? I'm confused.
That's an awful lot of code to ask strangers to debug. Can you point out where you think the problem is? Or at least explain exactly what's going wrong and when. Include error messages if you're getting any.
okay so i have created a new code that is much better for what im trying to do, i now have a custom script on the objects being spawned and the object pooling happening in the generation script. but im getting and error in these sets of lines:
class PoolObject
{
public GameObject go;
public int id = -1;
public bool isBeingUsed = false;
}
private List<PoolObject> obstaclePool;
GameObject GetPoolObstacle()
{
if (obstaclePool == null)
obstaclePool = new List<PoolObject>();
else
{
for (int i = 0; i < obstaclePool.Count; i++)
{
if (!obstaclePool[i].isBeingUsed) // not in use
return obstaclePool[i].go;
}
}
// if we made it this far, instantiate one
GameObject newGO = Instantiate(obstacleSpawn[Random.Range(0, obstacleSpawn.Length)], obstaclePoints[Random.Range(0, obstaclePoints.Length)].transform.position, transform.rotation);
PoolObject pO = new PoolObject();
pO.go = newGO;
pO.isBeingUsed = true;
pO.id = obstaclePool.Count;
obstaclePool.Add(pO);
GetComponent<DestroyClones>().ID = pO.id;
return newGO;
}
// This is the function called to spawn the obstacle clones
void ObstacleSpawn()
{
obstacleTimer -= Time.deltaTime;
if(obstacleTimer <= 0)
{
GameObject obstacleClone = GetPoolObstacle();
obstacleClone.transform.parent = theGround.transform;
obstacleTimer = newTime;
}
}
currently im trying to access the destroyClones script and making the public int ID into the id of the object being instantiated aka pO. But im getting this error with strange behavior
NullReferenceException: Object reference not set to an instance of an object Generation.GetPoolObstacle () (at Assets/Revamp/Scripts/Generation.cs:92) Generation.ObstacleSpawn () (at Assets/Revamp/Scripts/Generation.cs:106) Generation.Update () (at Assets/Revamp/Scripts/Generation.cs:39)
and when i click on the error it takes me to this line
GetComponent().ID = pO.id;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyClones : $$anonymous$$onoBehaviour {
public int ID = 0;
void OnEnable()
{
Invoke("Destroy", 7);
}
void Destroy()
{
gameObject.SetActive(false);
}
void OnDisable()
{
CancelInvoke();
}
}
thats the custom code on the objects being spawned
Answer by AlwaysSunny · Mar 25, 2017 at 11:45 AM
Null Reference Exception questions are forbidden at UA. Check out the FAQ.
The reason is they always have the same answer: You're trying to access a variable that is not populated with data.
Most likely you were trying to indicate this line, but if you don't format it as code, UA cuts out stuff inside less-than and greater-than symbols:
GetComponent<DestroyClones>().ID = pO.id;
The likely culprit is that the object with this script attached does not also have a DestroyClones script attached. Ergo when you attempt to access its "ID" variable, you encounter a null ref exception.
Are you expecting the DestroyClones script to lie on the instantiated prefabs? In that case, you need to call GetComponent on the instance. Simply call
myObjectWhichHasADestroyClonesScriptAttached.GetComponent<DestroyClones>().ID
Your answer
Follow this Question
Related Questions
Code optimisation - how to use arrays? 4 Answers
Merging together all GameObjects of an Array? 1 Answer
script optimization (choosing an object in game by click) 1 Answer
Why is Unity loading disabled GameObjects at scene start? 1 Answer
Cells for a mobile app stacked vertically, not moving correctly 0 Answers