Null Reference Exception ONLY (sometimes) when loading the scene...
Trying to set up a simple menu, which works. I load my "main scene" with the following script attached to a button:
public class ChangeScene : MonoBehaviour {
public void ChangeToScene (string sceneToChangeTo)
{
SceneManager.LoadScene(sceneToChangeTo);
}
}
This all works. The issue is, when I use that to load my "Main Scene", I get a Null Reference Exception error.
NullReferenceException: Object reference not set to an instance of an object
ObjectPooler.GetPooledObject () (at Assets/Scripts/ObjectPooler.cs:33)
WorldGenerator.Update () (at Assets/Scripts/WorldGenerator.cs:51)
This only occurs sometimes. If I play the scene directly from within that scene, it never occurs, but if I load it by clicking on a button it will occur around 70% of the time. As far as I can tell, it doesn't actually affect anything in the game, and only triggers once at the start, but I'd still like to fix it.
Here is the world generator script:
using System.Collections.Generic;
using UnityEngine;
public class WorldGenerator : MonoBehaviour
{
public int worldType;
public Transform generationPoint;
public float distanceBetween;
public CreateLevelSO[] levelsSO;
public GameObject pooler;
public GameObject poolFolder;
public GameObject obstacleFolder;
List<GameObject> objectPools;
void Start ()
{
// List of pools.
objectPools = new List<GameObject>();
// Iterate through all prefabs on SO.
for (int i = 0; i < levelsSO[worldType].Prefabs.Length; i++)
{
// Get the game object held on the SO.
GameObject prefabType = levelsSO[worldType].Prefabs[i];
// Instantiate a pool under the correct folder.
GameObject newPool = Instantiate(pooler, poolFolder.transform);
// Set the properties of the new object and add it to the list.
newPool.GetComponent<ObjectPooler>().prefab = prefabType;
newPool.GetComponent<ObjectPooler>().obstacleFolder = obstacleFolder;
objectPools.Add(newPool);
}
}
void Update ()
{
if (transform.position.z < generationPoint.position.z)
{
transform.position = new Vector3(transform.position.x, transform.position.y, transform.position.z + distanceBetween);
// Randomly select obstacle.
int type = Random.Range(0, levelsSO[worldType].Prefabs.Length);
// Get pooled object from the correct object pool.
GameObject newObstacle = objectPools[type].GetComponent<ObjectPooler>().GetPooledObject();
// Activate and move recieved object.
newObstacle.transform.position = transform.position;
newObstacle.SetActive(true);
}
}
}
Here is the object pooler script:
using System.Collections.Generic;
using UnityEngine;
public class ObjectPooler : MonoBehaviour
{
public GameObject prefab;
public GameObject obstacleFolder;
public int initialPooledAmount;
List<GameObject> pooledObjects;
void Start()
{
// pooledObjects is a list of Game Objects to be pooled.
pooledObjects = new List<GameObject>();
// Instantiate the initial pool.
for (int i = 0; i < initialPooledAmount; i++)
{
GameObject newObject = Instantiate(prefab, obstacleFolder.transform);
newObject.SetActive(false);
pooledObjects.Add(newObject);
}
}
// Function called to return an available pooled object.
public GameObject GetPooledObject()
{
// Iterates through current pooled objects, returning the first deactivated object.
for(int i = 0; i < pooledObjects.Count; i++)
{
if(!pooledObjects[i].activeInHierarchy)
{
return pooledObjects[i];
}
}
// If there are no deactivated objects, expand the pool and return it.
GameObject newObject = Instantiate(prefab, obstacleFolder.transform);
newObject.SetActive(false);
pooledObjects.Add(newObject);
return newObject;
}
}
Any help is appreciated, this is incredibly confusing for me...
Answer by FortisVenaliter · Oct 05, 2017 at 07:17 PM
My best guess, looking at the code, is that your WorldGenerator.Update()'s first call happens before all of the ObjectPooler.Start() functions have been called.
Honestly, I would remove the Start() function from ObjectPooler entirely and add this to the beginning of the GetPooledObject() function:
if(pooledObjects == null)
pooledObjects = new List<GameObject>();
Why would that only sometimes happen though? And never when playing from within the scene? Do scripts for some reason execute in an arbitrary order on scene load?
If you don't define the order in the Script Execution Order settings screen, then the order is indeter$$anonymous$$ate.
I'm guessing it has something to do with the fact that you're instantiating them in Start, and then using them the very next frame in Update. If they haven't initialized in that one frame, then you'll get the error above. I'm not sure if loading the scene causes orders to change, but if you load it asynchronously, I wouldn't be surprised.
That actually makes sense. I'll look into fixing that then. Thanks.
Yup, this works, thanks! I guess I wrongly assumed that Unity would "intelligently" execute scripts to avoid issues like this, but I suppose that'd be rather complicated.
Your answer
Follow this Question
Related Questions
NullReferenceException when attempting to call a GameObject from within PointerEventData 1 Answer
Object reference not set to an instance of an object - Jumping Scripts (C#) 1 Answer
Soccer game, more realistic net 1 Answer
getting a NullReferenceException error and can't see why. 1 Answer
Problem with destroying a gameobject 2 Answers