Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug One of my functions which returns IEnumerator is consistently returning null.

Discussion in 'Scripting' started by unity_45DDF9D84D05CB31595E, Aug 30, 2023.

  1. unity_45DDF9D84D05CB31595E

    unity_45DDF9D84D05CB31595E

    Joined:
    Jul 30, 2023
    Posts:
    3
    I'm brand new to coding in Unity and so I'm running into a whole horde of errors, which I suppose could be counted as "growing pains".

    The error below is that RoundOverCountdown() is consistently returning null. Sadly I have no clue as to why this is, hence why I am here.

    Don't worry about the SpawnEnemyWave function - that has been excluded as it bears no relevance to the topic at hand.

    Any help would be deeply appreciated.

    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3.  
    4.  
    5. public class SpawnManager : MonoBehaviour
    6. {
    7.  
    8.     [SerializeField] private GameObject _enemy;
    9.     [SerializeField] private GameObject _player;
    10.  
    11.     private WaveCounter _waveCounter;
    12.     private GameObject _roundOverText;
    13.  
    14.     private bool waveAllowed = true;
    15.     private bool WaveCountdownCoroutineRunning = false;
    16.  
    17.     private float spawnDelay = 2f;
    18.     private float spawnTimer = 1f;
    19.  
    20.     private int _enemyCount = 2;    //Should be 1 less than what is the desired at the very beginning of
    21.                                     //the game launch as it is incremented on the first wave spawn.
    22.  
    23.     // Start is called before the first frame update
    24.     void Start()
    25.     {
    26.         _waveCounter = GameObject.Find("WaveCounterText").GetComponent<WaveCounter>();
    27.         _roundOverText = GameObject.Find("RoundOverText");
    28.     }
    29.  
    30.     // Update is called once per frame
    31.     void Update()
    32.     {
    33.         if(GameObject.FindGameObjectsWithTag("Enemy").Length <= 0)
    34.         {
    35.             if(waveAllowed)
    36.             {
    37.                 waveAllowed = false;
    38.                 _enemyCount++;                          //Adds to _enemyCount which is used to measure how many enemies will be spawned.
    39.                 _waveCounter.AddWave();                 //Changes wave counter text in UI.
    40.                 SpawnEnemyWave(_enemyCount);            //Spawns as many enemies as the wave count is.
    41.             }
    42.             else if(!WaveCountdownCoroutineRunning)     // If the WaveCountdown Coroutine is NOT running.
    43.             {
    44.                 WaveCountdownCoroutineRunning = true;  
    45.                 StartCoroutine(WaveCountdown());        //Start WaveCountdown() coroutine.
    46.                 if (RoundOverCountdown() == null)
    47.                 {
    48.                     StartCoroutine(RoundOverCountdown()); //Display the "Round Over" text block on canvas.
    49.                 }
    50.                 else
    51.                 {
    52.                     Debug.Log("RoundOverCountDown() is null.");
    53.                 }
    54.                  
    55.             }
    56.         }
    57.     }
    58.  
    59.     IEnumerator RoundOverCountdown()
    60.     {
    61.         _roundOverText.SetActive(true);
    62.         yield return new WaitForSeconds(4);
    63.         _roundOverText.SetActive(false);
    64.     }
    65.  
    66.     IEnumerator WaveCountdown()
    67.     {
    68.         WaveCountdownCoroutineRunning = false;
    69.         yield return new WaitForSeconds(5);
    70.         waveAllowed = true;
    71.     }
    72. }
    73.  
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,563
    Remember the first rule of GameObject.Find():

    Do not use GameObject.Find();

    More information: https://starmanta.gitbooks.io/unitytipsredux/content/first-question.html

    More information: https://forum.unity.com/threads/why-cant-i-find-the-other-objects.1360192/#post-8581066

    In general, DO NOT use Find-like or GetComponent/AddComponent-like methods unless there truly is no other way, eg, dynamic runtime discovery of arbitrary objects. These mechanisms are for extremely-advanced use ONLY.

    If something is built into your scene or prefab, make a script and drag the reference(s) in. That will let you experience the highest rate of The Unity Way(tm) success of accessing things in your game.




    And regardless of HOW you are structuring the program...

    The answer is always the same... ALWAYS!

    How to fix a NullReferenceException error

    https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

    Three steps to success:
    - Identify what is null <-- any other action taken before this step is WASTED TIME
    - Identify why it is null
    - Fix that
     
  3. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,718
    I think you're confusing "returning null" with "throwing a NullReferenceException". These are two very different things.

    In the future if you are getting an error please include the full text of the error message in your post to get the best help.

    Anyway clearly
    _roundOverText
    is null if you're getting a NullReferenceException in that function. Which means that
    GameObject.Find("RoundOverText")
    in Start is returning null, hence assigning
    null
    to your
    _roundOverText
    variable. That will happen if no such active object with that name exists at the time Find runs.
     
  4. unity_45DDF9D84D05CB31595E

    unity_45DDF9D84D05CB31595E

    Joined:
    Jul 30, 2023
    Posts:
    3

    Wow, it really was this simple. That's absolutely insane. The problem was GameObject.Find() - it was setting the GameObject to "None" in the editor. Thank you very much! I won't be using that anymore.
     
  5. CodeRonnie

    CodeRonnie

    Joined:
    Oct 2, 2015
    Posts:
    280
    Code (CSharp):
    1. if (RoundOverCountdown() == null)
    2.     StartCoroutine(RoundOverCountdown());
    You're checking if RoundOverCountdown() == null before trying to start it. Because it will never return null, you then inaccurately write a log message that it is null.

    Methods that return an IEnumator which contain a yield statement will not return null. They are iterator blocks which will always just return a new instance of the secret class that the compiler created for you. Also, you're calling the method twice. The first time it returns a new instance of the IEnumerator, which will never be null. Then (if your null check was right) you call the method again, creating a completely different instance of that class again, and the old one will just be thrown away for garbage collection! Here is more information on how they work: https://csharpindepth.com/Articles/IteratorBlockImplementation
     
    halley and Bunny83 like this.
  6. unity_45DDF9D84D05CB31595E

    unity_45DDF9D84D05CB31595E

    Joined:
    Jul 30, 2023
    Posts:
    3

    Thank you, I'll give this a read.
     
  7. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,495
    This part makes no sense. First of all you check if "RoundOverCountdown" returns null and you only print
    "RoundOverCountDown() is null."
    when the return value is NOT null. So your own log message is misleading.

    Apart from that your whole logic makes no sense since we talk about coroutines here and not methods. You can not check the return value of the generator method as it will ALWAYS return a non null value as it returns a statemachine object. The return value of the "RoundOverCountdown()" method is not related in any way to the yielded values inside your coroutine. Each time you call RoundOverCountdown() you will create a new object which represents your coroutine. Though you don't do anything with it besides checking if it's null or not which is pointless as it is never null.

    You may want to have a look at my coroutine crash course to better understand what coroutines are.
     
    Kurt-Dekker and CodeRonnie like this.
  8. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,834
    Huzzah, someone actually read the code.
     
    Bunny83 likes this.