The question is answered, right answer was accepted
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);
}
}
}
}
Answer by yummy81 · Feb 03, 2018 at 05:09 PM
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.
$$anonymous$$any thanks!
Your if/try method solved the problem. According to your answer, I tried initiate the array Enemy[] with 0 element, array.resize before assign Prefab to it, the problem is also gone.
But I still cannot assign the instances to the array or the list directly, like Enemy[Num]=(GameObject)instantiate(foe); or this.Enemy.Add( (GameObject) Instantiate (foe) ); . With an intermediate to store the instance, both of array and list worked fine.
(For the prefab issue. "Prefab" is a GameObeject variable I used as intermediate to store the instance generated in current cycle, so that I can assign the instance to the array or list. Foe[] is assigned with the prefab in inspector. This is the way of the tutorial. It seems to provide possibility for different prefabs with GameObject[].)
Could you tell me what's the advantage of list compared to array in this issue? That I don't need to make the count, and no resize() needed?
Finally, thank you again!
Yes, the advantage of the list is that it resizes itself behind the scenes. I'm glad I could help
I have got the idea. It's my fault, I added a Debug.Log() for test and forgot to change it accordingly.
Thank you for your help again!
Follow this Question
Related Questions
Error CS0177 - "GameManager does not contain a definition for 'GetPlayer' PLEASE HELP! 0 Answers
Can someone please help me find out what wrong with my code. 0 Answers
Unity {[(2D)]} RotateAround while jumping 0 Answers
My gameobject keeps leaving the screen 0 Answers
stealth Frezz when "hit&" play bttn 1 Answer