Search Unity

MissingReferenceException while loading a scene

Discussion in 'Scripting' started by deLord, Mar 19, 2019.

  1. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    Can someone explain me this behavior and how to deal with it?

    I have an object with a while(true) loop in a coroutine that is started either
    a) in Start() or
    b) in OnLevelWasLoaded, called by the GameManager (FindObjectsOfType<XY>())

    in scenario a) I do not get exceptions when I restart the level
    in scenario b) I do [MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.]

    It occurs on different scripts so I don't think it has to do with what is inside the while(true) loop.

    Nothing is wrong ingame, everything looks fine but I get these exceptions. And since I want to synchronize all my coroutine starts from the GameManager, I use the behavior of b) which causes exceptions.

    Can someone shed light onto this?
     
    Last edited: Mar 19, 2019
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    Would help to see your code for B. And usually helps if you copy the error as it includes important info.
     
  3. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    added the error message.

    Code (CSharp):
    1.     public IEnumerator startSpawningCR() {
    2.         yield return new WaitForSeconds(phaseShift);
    3.         while (true) {
    4.  
    5.             while (canSpawn && spermCount != 0) {
    6.                 for(i=0; i < spawnees.Length; i++) {
    7.                     if(spawnDelays.Length < spawnees.Length) {
    8.                         yield return new WaitForSeconds(spawnDelays[0]);
    9.                     } else {
    10.                         yield return new WaitForSeconds(spawnDelays[i]);
    11.                     }
    12.                     if (canSpawn) {
    13.                         GameObject myBaby = Instantiate(spawnees[i], transform.position, Quaternion.identity, transform.parent);
    14.                         myBaby.SetActive(true);
    15.                         if (spermCount != -1) spermCount--;
    16.                     } else {
    17.                         break;
    18.                     }
    19.                 }
    20.             }
    21.             yield return new WaitForSeconds(0.1f);
    22.         }
    23.     }
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    It looks like from the error code you have a reference in the scene that breaks since you're either reloading the scene or changing scenes. So even though there is the reference there, the gameobject has been destroyed. You're most likely going to have to change your logic on that part.
     
  5. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    I think this is Unity-internal
    I am changing a scene, yes. OnLevelWasLoaded (yes yes, deprecated) is being called on my GameManager and this starts the CR. The old, running Cr should end because of the scene change itself. I NEVER had to hardcode that all loops of all scripts have to be stopped before changing a scene. So what's the deal here?
     
    Last edited: Mar 19, 2019
  6. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    Is it set to dontdestroyonload?
     
  7. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
  8. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    I guess you are instantiating myBaby and while in that loop changing the scene will not stop that loop and try to access mybaby, which is destroyed meanwhile giving you that error?
     
  9. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    I can add null-checks everywhere in the loop, it doesnt matter.
    From my understanding, even when within a CR, it will run within the same "tick-phase" as every other function being called. I mean with that that before the next frame is rendered, every LoC that goes up until a new wait-line is being executed (lines 5-20). And even if not, then break at least after any line, but it cannot be that the framework changes the scene and THEN executes the next line. No way it was designed like that.
    That is why I am so confused. It cannot be that one has to write any special code to treat running CRs when changing the scene as in my example. Also, this behavior is working for other CRs as well.

    I think we have to take a look at my first post, differentiating between a) and b). The answer must lie there , somewhere.... :rolleyes:
     
  10. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    Are we sure that on level was loaded is called when all objects are ready? I guess they act in front of awake and start maybe? Just another guess, sorry as I am not at home and only on mobile
     
  11. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    Even if that was the case, then the GameManager wouldn't be able to find them and start their CRs, or am I wrong? Or what do you mean with "objects are ready" and "they act"? No object can do anything before it's Awake() function is being called
     
  12. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    Even if I code this before and after changing the scene, I get this strange behavior

    Code (CSharp):
    1. foreach (FireTrapPerma firetrap in GameObject.FindObjectsOfType<FireTrapPerma>()) firetrap.StopAllCoroutines();
    this doesnt make any sense to me.
     
  13. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    What I meant with my last post is, that I am not sure, if OnLevelWasLoaded happens when the Scene is done changing but the gameobjects aren't ready yet, what would explain, why Start() is working, because it might get fired after onlevelwasloaded. On Level was loaded could get fired for each object at a different frame which is not going to give us the guarantee, that all gameobjects are ready. It would be great if we could see your full script to work on that special case, but don't know if you can strip it down to the necessary parts easily.
     
  14. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    I condense it here again:
    GameManager
    Code (CSharp):
    1.     public void OnLevelWasLoaded(int level) {
    2.         setupLevel();  
    3.     }
    4.  
    5.     private void setupLevel() {
    6.         foreach (FireTrapPerma trap in GameObject.FindObjectsOfType<FireTrapPerma>()) {
    7.             trap.StopAllCoroutines();
    8.             StartCoroutine(trap.activationCoroutine());
    9.         }
    10.     }
    FireTrapPerma
    Code (CSharp):
    1.     public IEnumerator activationCoroutine() {
    2.         isRunning = true;
    3.         yield return new WaitForSeconds(phaseShift);
    4.         float einFrame = 1f / 13f;
    5.         float sechsFrames = 6f / 13f;
    6.         while (isRunning) {
    7.             triggeringSprite.SetActive(true);
    8.             triggeringSprite.GetComponent<Animator>().Play("FireTrapLoop", -1, 0);
    9.             offSprite.SetActive(false);
    10.             yield return new WaitForSeconds(sechsFrames);
    11.             if (!isRunning) yield break;
    12.             triggeringSprite.GetComponent<Animator>().Play("FireTrapLoop", -1, sechsFrames);
    13.             foreach (Collider2D taker in guysOnMe) dealDamageIfNotAlready(taker);
    14.             yield return new WaitForSeconds(einFrame);
    15.             triggeringSprite.GetComponent<Animator>().Play("FireTrapLoop", -1, 0.5384615f);
    16.             foreach (Collider2D taker in guysOnMe) dealDamageIfNotAlready(taker);
    17.             yield return new WaitForSeconds(einFrame);
    18.             triggeringSprite.GetComponent<Animator>().Play("FireTrapLoop", -1, 0.6153846f);
    19.             foreach (Collider2D taker in guysOnMe) dealDamageIfNotAlready(taker);
    20.             yield return new WaitForSeconds(einFrame);
    21.             triggeringSprite.GetComponent<Animator>().Play("FireTrapLoop", -1, 0.6923077f);
    22.             yield return new WaitForSeconds(4f/13f);
    23.             triggeringSprite.SetActive(false);
    24.             offSprite.SetActive(true);
    25.             yield return new WaitForSeconds(additionalIdleTime);
    26.         }
    The code above (in my second post) is from the class Spawnfield which, at the moment, starts the CR itself in it's Start() method. So Spawnfield is variant a) but the spawn timings suck (because of different Start()-times) and FireTrapPerma is variant b.
     
  15. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    the problem even persists if I call setupLevel() after 1s delay
     
  16. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    I don't know. Most of the time, the FireTraps are not insync with my phaseShift waiting time.
    This doesnt make any sense really!
    I have an formation of let's say 4 traps in a row and the player needs to run through them at the right time. But this levelsetup gets destroyed if 2 or 3 of them do not follow my phaseShift time exactly. I can only call this a Unity bug as I see nothing else I can do. Using 2018.3.3f1.
     
  17. WheresMommy

    WheresMommy

    Joined:
    Oct 4, 2012
    Posts:
    890
    Did you try to get the traps after one second delay and then call the foreach on the array instead of using the find inside the foreach loop?
     
  18. deLord

    deLord

    Joined:
    Oct 11, 2014
    Posts:
    306
    I am not sure what you mean with "Find inside the foreach loop", but what I did was waiting a whole second, then calling setupLeveL(). I did not use a Find INSIDE a foreach loop.