When call the instances of prefab, got the NullReferenceException warnings while the code functioned properly

Hello there.

I learned the Space Shooter tutorial, and made some changes to produce something different. I want a constant respawning, and the instances are called by a script not bound to the prefab. And the enemies move toward the players.

For most parts I succeeded, but when I assigned the instances to array with Enemy[Num]=(GameObject)instantiate(prefab, position, rotation), I got “UnassignedReferenceException: The variable Prefab of Fight has not been assigned.”. After the instantiation of the first clone, nothing happened. (well, the timer did work properly and I can control the player…)

Then, I use an intermediate to get the instance generated in each cycle, and assign it to Enemy[Num], this work well. But when I call the instances stored in Enemy in update(), well, in fact, the function (movement toward player) worked properly, however I got hundreds warnings (NullReferenceException: Object reference not set to an instance of an object) .

I am curious why I can’t assign (GameObject)isntantiate(bla,bla,bla) to Enemy[Num] directly,

and why though the function worked, there are so many warnigs or errors for the movement code?

And how to solve it?

Thank you in advance.

Here’s the code:

    //Only the lines related to this problem copied. Code for other parts like the  Player-related code are not included.
using System;
using System.Collections;
using UnityEditor;
using UnityEngine;
using Random = UnityEngine.Random;
    
    public class Fight : MonoBehaviour
    {
    	public GameObject[] Foes;
    	public float spawnWait;
    	private int Num=0;
    	private Vector3 _spawnPosition=new Vector3();
    	private float foeStep;
    	public float FoeSpeed;
    	private GameObject[] Enemy;
    	public GameObject Prefab;
    	
    
    void Start () {
    		Enemy=new GameObject[5];
    		StartCoroutine (SpawnWaves ());
    }
    
    void Update () {
    		foeStep = FoeSpeed * Time.deltaTime;
    		foreach (var foe in Enemy) //No warning for this line.
    		{
    			foe.transform.position = Vector3.MoveTowards(foe.transform.position, Player.transform.position, foeStep);  //The warning "NullReferenceException: Object reference not set to an instance of an object" pointed to this line. However, this code's function worked properly.
    		}
    }
    
    	IEnumerator SpawnWaves ()
    	{
    		while (true)
    		{
    			Quaternion spawnRotation = Quaternion.identity;
    			for (;;)
    			{
    				GameObject foe = Foes [Random.Range (0, Foes.Length)];
    				foe.name = Num.ToString();
    				do
    				{
    					_spawnPosition = new Vector3 (Random.Range (-13f,13f), Random.Range (-4.8f,4.8f), 0);
    				}while(Vector3.Distance (_spawnPosition,Player.transform.position)<0.7f);
    				Prefab=(GameObject)Instantiate (foe, _spawnPosition, spawnRotation);
    
    //Enemy[Num]=(GameObject)Instantiate (foe, _spawnPosition, spawnRotation);  //If use this code (without the later "Enemy[Num] = Prefab;" will only spawn one enemy and a warning described above. Why?
    
    				spawnWait=Mathf.Min(1,10/Time.time);
    				Enemy[Num] = Prefab;
    				Array.Resize<GameObject>(ref Enemy, Enemy.Length +1);
    				Num++;
    				Debug.Log(Prefab.name); // This line work well.
    				yield return new WaitForSeconds (spawnWait);
    			}
    		}
    	}
    
    }

In Update, you loop through the Enemy array, even though not all the elements where assigned. It means that at the very begining Enemy array has only one element (foe) assigned. At that moment the NullReferenceExceptions start appearing. The Update method checks the array every frame while the coroutine fills the array at a spawnWait simultaneously resizing it. So each frame the array remains partly filled, partly empty triggering the exceptions.

To make those exceptions disappear in the console, you can add the condition in the foreach loop inside Update to skip those iterations for which there is no foe assigned:

foreach(var foe in Enemy)
{
	if (!foe) continue;
	foe.transform.position = Vector3.MoveTowards(foe.transform.position, Vector3.up*10, Time.deltaTime);
}

You can also try to catch the exceptions like that:

foreach(var foe in Enemy)
{
	try
	{
		foe.transform.position = Vector3.MoveTowards(foe.transform.position, Vector3.up*10, Time.deltaTime);
	}
	catch {}
}

As to “UnassignedReferenceException: The variable Prefab of Fight has not been assigned.” - it seems that you didn’t assign the Prefab in the Inspector. But in the code you delivered I can’t see the use of the Prefab as the parameter in the Instantiate method. So I suppose that this exception appeared in the previous versions of your code.

Finally you can rewrite your code and get rid of all those arrays and use list. This is the simple example:

public GameObject[] Foes;
public float spawnWait;

//public GameObject[] Enemy;
public List<GameObject> Enemy;
private int Num = 0;

private void Start()
{
	//this.Enemy = new GameObject[5];
	this.Enemy = new List<GameObject>();
	StartCoroutine(this.SpawnWaves());
}

private void Update()
{
	foreach(var foe in this.Enemy)
	{
		foe.transform.position = Vector3.MoveTowards(foe.transform.position, Vector3.up*10, Time.deltaTime);
	}
}

private IEnumerator SpawnWaves()
{
	while(true)
	{
		for(;;)
		{
			GameObject foe = Foes[Random.Range(0, Foes.Length)];
			this.Enemy.Add( (GameObject) Instantiate (foe) );
			/*
			// use list instead of the array
			
			this.Enemy[this.Num] = (GameObject) Instantiate (foe);
			System.Array.Resize<GameObject>(ref Enemy, Enemy.Length +1);
			this.Num++;
			*/
			spawnWait=Mathf.Min(1,10/Time.time);				
			yield return new WaitForSeconds(1f);
		}
	}
}

I do not understand why you nested one infinite the for loop inside the while loop without yielding anything and assigning only Quaternion.identity. But I suppose it’s just the excerpt so I ignore that.